Guest Jukebox Posted June 21, 2023 Share Posted June 21, 2023 Hi! I was testing Immunity to weapons (120) with the help of the IESDP to fasten things up, and noticed that Set image type (237) seems incorrect: Quote If Master ID triggers HitBy([ANYONE],0), this creature is moved to area "NO_AREA.ARE". This will delete non-global creatures from the game, unless such an area has been added to the game. If I read this right, a Project Image should disappear if HitBy() is triggered on the caster regardless of how it happens. Immunity to weapons still allows HitBy() to trigger, as confirmed by the "hit" condition of Cast spell on condition (232) (param2 = 0) working fine through e.g. Fireshields. (According to the IESDP op232 relies on HitBy().) But the image doesn't disappear, at least in the only game I tested: BG2EE 2.6.6.0. I assume there's some basis for that quote above. If it's roughly correct but inaccurate, where's the inaccuracy? (Or is it wrong, outdated in 2.6.6.0, or original edition only? I have no idea.) Quote Link to comment
jmerry Posted June 21, 2023 Share Posted June 21, 2023 The description is technically true, but misleading. The "HitBy([ANYONE],0)" script trigger is true exactly when any type of damage is dealt to the subject. "HitBy([ANYONE])" without the second parameter, as used for contingency effects, is true whenever the subject is hit by any attack or hostile spell/ability - even if no damage is dealt or the subject is immune. One example involving Project Image: what happens if a mage is protected by Stoneskin and casts Project Image, then an illithid comes over and attacks the real mage? What happens is that the illithid auto-hits, drains 5 Int, and deals zero damage. The image sticks around. Repeat about four times - which only takes a round - and the image finally pops when the real mage dies. Quote Link to comment
polytope Posted June 21, 2023 Share Posted June 21, 2023 EE hasn't changed this, I believe. About the HitBy([EA],0)...HitBy([EA])The subsequent 0 means (oddly) crushing OR any listed damage type from DMGTYPE.ids. If you don't specify a damage type from DMGTYPE.ids HitBy([EA]) returns true for any attack regardless of the damage being blocked by Stoneskin or protection from weapon types. The other thing is that Mirror Image is different in this regard, attacks soaked by stoneskin trigger the HitBy() condition, but not if intercepted by a Mirror Image. For example BEAR.BCS has this block, only triggered by fire damage specifically: IF HitBy([ANYONE],FIRE) THEN RESPONSE #100 SetInterrupt(FALSE) RunAwayFrom(LastAttackerOf(Myself),200) SetInterrupt(TRUE) END Quote Link to comment
Guest Jukebox Posted June 21, 2023 Share Posted June 21, 2023 What strange behavior. The Infinity engine is so quirky, and yet I'd argue that the quirks are part of its success as they allow a lot of emergent gameplay. So... The IESDP might need a to make it clear. Perhaps the first "Info" bullet point in the description of Immunity to weapons (120) could be changed to: Quote Protected weapons will trigger HitBy(), but only if the condition's second argument is not specified. They will not interact with Mirror Image or Stone skins. Also, what is the thing that decides Immunity to weapons is evaluated before Mirror Image, which is evaluated before Stoneskin? Is it just hard coded ordering, or is it more of a side (but intended) consequence of some other logic? Quote Link to comment
Bubb Posted June 21, 2023 Share Posted June 21, 2023 (edited) As of v2.6.6.0, HitBy() is triggered in three places: CGameEffect::OnAdd() Prerequisites: Spell / Item ability flagged as HOSTILE; effect has a source, and source isn't the creature being affected. cause = Source AI type specific = 0 CGameEffectDamage::OnAddSpecific() - This runs after CGameEffect::OnAdd() if an effect defines it. Prerequisites: Effect has a source. cause = Source AI type specific = Damage type CGameSprite::Swing() Prerequisites: Weapon ability type != RANGED && <real attack> && CGameSprite::Hit() != 0. op120 is checked after calculating the hit rolls and can still block the attack after HitBy() has been triggered. cause = Attacker's AI type specific = 0 Key points: Mirror image / stoneskin resistances are processed in CGameEffectDamage::CheckSave(), before the effect is added to the target – meaning that when mirror image / stoneskin blocks an effect, it never goes through the OnAdd() / OnAddSpecific() stages. The same principle applies if the effect is blocked by any mechanism. Event-based triggers, (those with IDs not in the 0x4000 range), match two things: the event cause, (the AI type triggered the event), and a "specific" int value. If a trigger checks a specific int value of 0, the engine treats that '0' to mean "any value." So, some takeaways: Triggers: HitBy(<AI type>) and HitBy(<AI type>, 0) match any HitBy() event that matches <AI type>; i.e. <damage type> is ignored. Effects: When added, any effect that inherited the hostile flag and that has a valid source triggers HitBy(<attacker AI type>, 0), (except when the target was source of the effect). When added, any op12 that has a valid source triggers HitBy(<source AI type>, <damage type>). When an effect is blocked by magic resistance, saving throws, mirror image / stoneskin (in the instance of op12), or by any other mechanism, the effect doesn't trigger HitBy(). Swings: Real attack swings that pass their hit roll trigger HitBy(<attacker AI type>, 0). For some reason weapon abilities with type = RANGED don't trigger HitBy() as part of the swing. op120 can still block a hit even after HitBy() is triggered by a swing. Though, all of this is moot, because Project Image doesn't use the HitBy() trigger to determine when to destroy the image. There are two ways the image can get destroyed: The mechanism that determines whether spell casting is disrupted is triggered: either the creature was hit by an op12 with a damage type it didn't have 100%+ resistance to, (I'm glossing over some of the finer details here), or the creature went under op39. Any op12 is applied to the caster. I'm inclined to think this is a bug, since it overrides the nuance of the previous check. Why have two checks if the latter makes the former largely redundant? In this case, the only thing the previous check does is provide image destruction in reaction to op39. Edited June 21, 2023 by Bubb Correct op12 explanation Quote Link to comment
Graion Dilach Posted June 21, 2023 Share Posted June 21, 2023 12 minutes ago, Bubb said: It uses the same mechanism that determines whether spell casting is disrupted Oh, does this mean it's also affected by the facing-changes-for-a-frame-preventing-disruption bug? Quote Link to comment
Bubb Posted June 21, 2023 Share Posted June 21, 2023 46 minutes ago, Graion Dilach said: Oh, does this mean it's also affected by the facing-changes-for-a-frame-preventing-disruption bug? Thankfully not. That bug is in the spell actions, this is just co-opting how the engine determines whether the creature has taken damage. Also, a correction: I missed a part of the Project Image handling. op12 specifically destroys the Project Image clone of the target, regardless of the character's resistances. To me this feels like a bug, since the clone's processing goes out of its way to check if its master "took damage," just for the nuance of that check to be ignored by op12. Quote Link to comment
jmerry Posted June 21, 2023 Share Posted June 21, 2023 1 hour ago, Bubb said: Though, all of this is moot, because Project Image doesn't use the HitBy() trigger to determine when to destroy the image. It uses the same mechanism that determines whether spell casting is disrupted: either the creature was hit by an op12 with a damage type it didn't have 100%+ resistance to, (I'm glosing over some of the finer details here), or the creature went under op39. So ... the IESDP description of op237 should definitely get an update. Replace If Master ID triggers HitBy([ANYONE],0), this creature is moved to area "NO_AREA.ARE". This will delete non-global creatures from the game, unless such an area has been added to the game. with If Master ID is dealt damage or becomes unconscious, this creature is moved to area "NO_AREA.ARE". This will delete non-global creatures from the game, unless such an area has been added to the game. Does that look good to everyone? Quote Link to comment
kjeron Posted June 21, 2023 Share Posted June 21, 2023 2 hours ago, jmerry said: If Master ID is dealt damage or becomes unconscious, this creature is moved to area "NO_AREA.ARE". This will delete non-global creatures from the game, unless such an area has been added to the game. Does that look good to everyone? "Took damage" can be misleading, since, just like spell disruption, zero damage will trigger it. 2 hours ago, Bubb said: Also, a correction: I missed a part of the Project Image handling. op12 specifically destroys the Project Image clone of the target, regardless of the character's resistances. To me this feels like a bug, since the clone's processing goes out of its way to check if its master "took damage," just for the nuance of that check to be ignored by op12. Interestingly this was NOT the case in v2.6.5, 100% damage resistance would prevent the image from being destroyed. Regression maybe? Quote Link to comment
kjeron Posted July 16, 2023 Share Posted July 16, 2023 On 6/21/2023 at 3:13 PM, Bubb said: Swings: Real attack swings that pass their hit roll trigger HitBy(<attacker AI type>, 0). For some reason weapon abilities with type = RANGED don't trigger HitBy() as part of the swing. op120 can still block a hit even after HitBy() is triggered by a swing. The likely reason for ranged weapons is so that the "hit" doesn't trigger until the projectile impacts the target. With melee weapons, it's not an issue, as they ignore their projectile field. With ranged weapons, a projectile impact is not guaranteed, and can even be setup to never occur. I would guess that the extra step for op120 is to stop effects that aren't tied to the projectile (those using anything other than "Preset Target" and "Original Caster"). Quote Link to comment
Recommended Posts
Join the conversation
You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.