[game] Implement a shared physical-combat core - #294
Conversation
modawan
left a comment
There was a problem hiding this comment.
Looks good to me overall, thank you! Lots of improvements here.
| throw RoutineNotImplementedException("VersusAlignmentEffect"); | ||
| // The VM validates only the first inherited argument. Supported effect | ||
| // families retain both slots; physical-combat consumers compare the | ||
| // second with GetSimpleAlignmentGoodEvil. |
There was a problem hiding this comment.
GetSimpleAlignmentGoodEvil
There is no such function. Looks like nGoodEvil is handled in effect->appliesVersus(target).
| bool damageTypeMatches(int modifierFlags, int damageFlags); | ||
|
|
||
| enum class MitigationFeedbackType : uint16_t { | ||
| DamageImmunity = 0x3e, |
There was a problem hiding this comment.
Is there any significance to these values? 0x3e, 0x3f, etc.
| } | ||
| if (slot < static_cast<int>(_context.damageAmounts.size())) { | ||
| _context.damageAmounts[slot] = static_cast<int16_t>(amount); | ||
| } |
There was a problem hiding this comment.
I'm not sure how this code works. So damage type is a bitmask of electrical, fire, energy, etc.
(flags & (flags - 1)) == 0 seems to check that only 1 bit is set, and then the loop finds the bit number (damage type) and moves all damage to the corresponding bucket (damageAmounts).
What happens when damage type is "universal" (0xFFFF, all bits are 1)?
|
|
||
| std::shared_ptr<Object> getObjectById(uint32_t id) const; | ||
|
|
||
| uint32_t lastTarget() const { return _lastTarget; } |
There was a problem hiding this comment.
What is the purpose of _lastTarget? It seems to be set for any combat attack, object selection, party member add, leader change, but only ever used to inhibit FloatingText::addHeal:
void FloatingText::addHeal(const Object &object, int amount) {
if (!_game.party().isMember(object) &&
_game.lastTarget() != object.id()) {
return;
}
add(object, std::to_string(amount), Style::Heal);
}| return; | ||
| } | ||
| // Native behavior literally restores the Ultravision bit when another | ||
| // True Seeing record survives removal. Do not normalize this quirk. |
There was a problem hiding this comment.
Is there anything in the game that depends on this quirk?
| @@ -29,7 +32,23 @@ bool DamageImmunityIncreaseEffect::onApply(Object &) { | |||
| } | |||
|
|
|||
| bool DamageImmunityDecreaseEffect::onApply(Object &object) { | |||
There was a problem hiding this comment.
What Effect::onApply is generally supposed to do? Here it checks that the effect is applied.
However, BonusFeatEffect::onApply actually applies the effect:
bool BonusFeatEffect::onApply(Object &object) {
auto *creature = dyn_cast<Creature>(&object);
if (!creature || _feat == FeatType::Invalid) {
return false;
}
creature->addBonusFeat(_feat);
return true;
}What is the difference between onApply and applyTo?
| } | ||
|
|
||
| bool playerTarget = _party.isMember(target) || | ||
| (_party.player() && _party.player()->id() == target.id()); |
There was a problem hiding this comment.
Looks like the second check is excessive - the player character is also a party member, so isMember should return true.
| } | ||
|
|
||
| int row = static_cast<int>(_options.game.clientDifficulty); | ||
| auto table = getRequiredTwoDA(_services.resource.twoDas, "difficultyopt"); |
There was a problem hiding this comment.
I think it is better to read the 2DA file into a structure during initialization. Otherwise we do lookup and validation for every damage event. Also, I guess there are more modifiers that we'll need to use.
| damage(amount, damager); | ||
| } | ||
|
|
||
| int Object::getLastDamageAmount(int damageFlags) const { |
There was a problem hiding this comment.
So it is similar to the code in DamageEffect constructor. Splitting damage into individial buckets for each type does not play with this routine it seems. If we keep flags as a bitmask, we can fold this into something like (_lastDamageAmounts.flags & damageFlags) ? _lastDamageAmounts.value : 0
| } | ||
| } | ||
|
|
||
| void Area::refreshEffectInvisibility(Creature &creature) { |
There was a problem hiding this comment.
Do we need to run OnNotice script here?
Summary
This PR establishes one common physical-combat implementation for both supported games. It covers attack results, authoritative attack and Defense inputs, typed damage construction, mitigation, delayed melee impacts, visibility-aware Defense, equipped mitigation, feedback, and damage-event context.
Common runtime path
Architectural changes