Jump to content

[code] Code fixes, aka stuff we can't fix


CamDawg

Recommended Posts

Thought it might be good to have a thread of stuff that's broken but requires fixes in the engine itself. This is stuff that, generally, EEFP won't be able to fix.

I'll start with an oldie but goodie: using the auto-pause for 'spell cast' causes all sorts of issues. To replicate, start an IWDEE game with a druid and at least one other character. Memorize some Sunscorch spells on your druid, rest your party, and enable 'Spell Cast' on the auto-pause menu. Cast Sunscorch on the other party member.

Pausing on the moment the spell is cast causes each individual effect in the spell to be rolled separately for saves and probability. In the attached screenshot, the target has made their save against the actual blind effect, but failed the save for the blind icon, 'blinded' message, and damage. These effects should all roll one save collectively and either all be blocked or succeed.

Icewn011.png

Link to comment

Here's one I had fixed in old-EEex:

Event triggers (IDS < 0x4000) "lock on" to a specific creature when using standard objects. For example:

IF
    Global("B3TEST","GLOBAL",1)
    AttackedBy(PartySlot1,DEFAULT)
THEN
    RESPONSE #100
        DisplayStringHead(Myself,1)  // No, I'm sorry, none of them sound familiar.
END

The AttackedBy() will only return true for whoever the party leader was during its first evaluation, even if you change the party order.

Link to comment

There is a bug that causes any arcane caster with a kit to receive a -15% penalty to learning spells by scribing scrolls.

Roughly speaking, the engine (as of 2.6 or earlier) instead of checking if the caster is a specialist of a different school than the spell attempted to, it also applies the specialist penalty to classes with ANY kit. That means any kitted Bard gets the -15% penalty, but also a Berserker>Mage or Kensai>Mage.

The sources for this are Bubb and CamDawg, and according to Cam, this will be fixed in 2.7. Links to the conversation on Discord (which I hope is not a problem to share, given that it was a public conversation):
 

Also, related, but on the side of fixable things with WeiDU:

 

Edited by suy
Added link.
Link to comment

I'll report here what I've already said in the PM

Spoiler

  1. Opcode #135 (Polymorph) is partially bugged

    When used with `Polymorph type: Change into // parameter2=0` and upon shapechanging between two different non-natural forms, all your equipment will be considered as "unequipped" => this is particularly relevant for NPCs that wear undroppable items (f.i. Nalia's ring or Edwin's Amulet).
    In fact, if you accidentally shapechange between two different non-natural forms, you'll be forced to save & reload your game in order to re-equip these undroppable items.
  2. Invisible animations

    What about adding a new VVC flag that can take care of this (this was originally proposed by @Galactygon on the BD forum...)
  3. Non-decrementing reflection opcodes (opcode #197, opcode #198, opcode #199, opcode #202, opcode #203 and opcode #207) vs. op146*p2=2 / op326 / op333

    As of now, when op146*p2=2 and op326 get reflected, the SPL specified by the resource key will also affect the original target (as if it wasn't reflected). In case of op333, only its first "hit" gets reflected (subsequent hits occur as if reflection wasn't involved).
    Patch v2.6 fixed this issue with the decrementing reflection opcodes, but apparently forgot to fix the non-decrementing ones...
  4. Opcode #333 vs. Saving Throw

    As of now, its Saving Throw is checked EVERY time it triggers, terminating the opcode #333 effect if successful, so unless that is desired (or it only triggers once), it should not have a Saving Throw.
    This bug requires special workarounds (see how IWDEE Shroud of Flame is implemented...)
  5. Opcode #178/#179 vs. dual-wielding

    As of now, they affect all attacks, not just the weapon they are on, which would be incorrect when dual-wielding. Can you do something about it...?
    Ideally, they should have a "By this weapon only" mode (like op301, 341, 361, 362...)
  6. Opcode #248/#249 and weapon category

    As of now, op248 can be set to affect only fists (see for instance MONK_QUIVERING_PALM "spcl820.spl"). On the contrary, op249 simply affect all ranged weapons.

    1) What if it would be possible to specify which weapon categories (ITM @ 0x1C) are eligible (see op344/345, where you can specify a weapon category). Wouldn't that be great...?
    2) As far as op248 is concerned, would it be possible to have a "By this weapon only" mode (like op301, 341, 361, 362...)? As of now, it always applies to both main and offhand weapon...
  7. Opcode #171 and `timing_mode=2`

    It'd be really good if this opcode were compatible with `timing_mode=2`, so as to grant special abilities when equipping specific items – see for instance SALAMANDER_BREATHE_FIREBALL ("spin160.spl").

 

Link to comment
14 hours ago, CamDawg said:

using the auto-pause for 'spell cast' causes all sorts of issues

It is probably worth mentioning that also manually pausing the game can cause all those issues (of course, it's much less noticeable and reproducible, but it can happen...)

Edited by Luke
Link to comment

Casters can't be disrupted by damage if they are facing SSW(1), SWW(3), NWW(5), NNW(7), NNE(9), NEE(11), SEE(13), or SSE(15). This happens because the disruption code is only checked if the caster is in the correct orientation to face the target. SEQ_DAMAGE rounds a creature's orientation down to the nearest even direction, causing the caster to momentarily not face the target, making the check fail. Here's an example:

Spoiler

km4bjb2kar6x.gif

Edited by Bubb
Link to comment
11 hours ago, subtledoctor said:

concentration checks should go on the list. 

The IESDP lists the following behavior for CONCENTR.2DA's CHECK_MODE:

Quote

Known values for CHECK_MODE are:

  • 0 ⟶ Any damage a spellcaster takes will cause them to fail their spellcasting
  • 1 ⟶ Spell disrupted if (1d20 - 1) + Luck > Spell Level + Damage Taken
  • 2 ⟶ Spell disrupted if (1d20 - 1) + (Constitution / 2) - 5 > Spell Level + 15
  • 3 ⟶ Spell disrupted if (1d20 - 1) + Luck > Spell Level + 15
  • 4 ⟶ Spell disrupted if (1d20 - 1) + (Constitution / 2) - 5 > Spell Level + Damage Taken

Bug: CHECK_MODE values greater than or equal to 1 are bugged (since all those inequality symbols should be < instead of >).
As a result, you might want to stick with the default behavior (CHECK_MODE=0) until this bug is fixed.

And here's some reversed pseudocode from the engine backing that up:

Spoiler
// CHECK_MODE bit0 = Use Luck instead of Constitution
// CHECK_MODE bit1 = Use 15 instead of Damage Taken

roll_modifier = nil

if CHECK_MODE == 0 then
    // return 1 (Always disrupted)
elseif bit0 == 1 then
    roll_modifier = luck
else
    roll_modifier = math.floor(constitution / 2) - 5
end

roll = random_inclusive(0, 19) + roll_modifier
bound_modifier = nil

if bit1 == 1 then
    bound_modifier = 0xF
else
    bound_modifier = damage_taken
end

success_higher_bound = spell_level + bound_modifier

// BUG: This condition is flipped
if roll > success_higher_bound then
    // return 1 (Disrupted)
else
    // return 0 (Not disrupted)
end

The final condition is flipped — the behavior should be the following, based on TobEx:

Do NOT disrupt if (1d20 - 1) + (Luck or Constitution/2) >= (Spell Level) + (Damage Taken or 15)

not

Disrupt if (1d20 - 1) + (Luck or Constitution/2) > (Spell Level) + (Damage Taken or 15)

I.E. A greater Roll/Luck/Constitution should decrease the chance of disruption, not increase it.

Edited by Bubb
Add important note from the IESDP / Clarify what the bug is
Link to comment
35 minutes ago, Bubb said:

The final condition is flipped — higher Luck / Constitution should not increase the chance of disruption.

I have a recollection of someone - probably @kjeron - saying that more than a just one condition is flipped. That, in essence, the roll is on the wrong side of the equation.

After all if only the Luck/CON conditions were flipped then check_mode=1 should work fine with a Luck value of 0. The equation would be:

Spell disrupted if (1d20 - 1) > Spell Level + Damage Taken

But, by all accounts, that does not work.

EDIT - just looking at the logic of the IESDP desription, it seems backwards: what it should be doing (and what ToBEx actually does?) is

Spell NOT disrupted if (1d20 - 1) > Spell Level + Damage Taken

 

Link to comment
3 hours ago, subtledoctor said:

EDIT - just looking at the logic of the IESDP desription, it seems backwards: what it should be doing (and what ToBEx actually does?) is

Spell NOT disrupted if (1d20 - 1) > Spell Level + Damage Taken

Right, that's the intended formula, (with CHECK_MODE=1, Luck=0). The IESDP description I quoted is the observed behavior of the engine, not the intended behavior. I left out this note from the IESDP which I probably should of kept, I'll edit it in:

Quote

Bug: CHECK_MODE values greater than or equal to 1 are bugged (since all those inequality symbols should be < instead of >).
As a result, you might want to stick with the default behavior (CHECK_MODE=0) until this bug is fixed.

Inverting the last condition in the psedocode, (changing the > to <), aligns the behavior with ToBEx.

EDIT — Ok, I've finished editing my previous post. Let me know if it still needs work.

Edited by Bubb
Link to comment
19 hours ago, Bubb said:

Casters can't be disrupted by damage if they are facing SSW(1), SWW(3), NWW(5), NNW(7), NNE(9), NEE(11), SEE(13), or SSE(15). This happens because the disruption code is only checked if the caster is in the correct orientation to face the target. SEQ_DAMAGE rounds a creature's orientation down to the nearest even direction, causing the caster to momentarily not face the target, making the check fail. Here's an example:

  Reveal hidden contents

km4bjb2kar6x.gif

Thanks for sharing, I'll add it to the IESDP...

Link to comment

To quote the IESDP, here's another bug with event triggers, (they really are the worst):

Quote

NOTE: Triggers with values below 0x4000 use an alternate event-driven system, which is buggy even for IE standards. A negated event trigger returns true if there are pending events that have yet to be processed, and if any of these pending events fails to satisfy the unnegated trigger.

You can see this effect by putting !OnCreation() on a creature. The block will fire every time the creature receives an event other than the one that is defined, such as taking damage — so not every time the triggers are checked after load as would be natural to expect.

Link to comment

Quoting past me regarding NextTriggerObject() and how it breaks the trigger block it is in if it evaluates to an invalid creature:

Spoiler
 19/2020 at 10:44 AM, Bubb said:

It's easier just to show the flaw in the trigger block evaluation code.

if (hasNextTriggerObjectOverride)
{
    if (nextTriggerObjectOverride != NULL)
    {
        // do eval stuff
        hasNextTriggerObjectOverride = false
    }
}
else
{
    // do eval stuff
}

NextTriggerObject sets both hasNextTriggerObjectOverride=true, and if it can't find a valid object, nextTriggerObjectOverride=NULL. Once this happens, the engine gets stuck in a bad state. Since nextTriggerObjectOverride is NULL it never clears hasNextTriggerObjectOverride, and it forever skips evaluating further triggers, (in the current trigger block).

Note though, while it's not evaluating the triggers, it is still running through all of them and doing janitorial actions. Meaning - every non-OR'd trigger still sets the whole block's result to false, the active OR count still ticks down - another NextTriggerOverride has the potential of fixing the deadlock, etc.

The only way a trigger block can still succeed after NextTriggerOverride breaks is:

  1. The NextTriggerOverride was in an OR block
  2. There are no subsequent triggers after the OR block
  3. A trigger in the OR block, that is positioned above the NextTriggerOverride, already evaluated to true

or, further in the same OR block, another NextTriggerOverride evaluates to a valid creature, and fixes the bad state. Once that happens, the trigger block will start evaluating normally again from that point onward. So, I suppose, one way to prevent NextTriggerOverride from breaking everything would be something like this:

IF
    OR(3)
        TriggerOverride(Player6,Global("B3WEIRD","LOCALS",1))
        TriggerOverride(Myself,False()) // This will fix the deadlock
        // Whatever trigger here
THEN
Link to comment

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...