Skip to content

Cast Spell On Condition [232] trigger check frequency

UnpersonUnperson Member Posts: 29
Hey,

Is it possible to change how often the game checks for opcode 232 trigger conditions?

The current behavior, which is used by player Contingency spells (but also by trolls to determine when they have to fall down etc) is fairly unresponsive. Too often, by the time the game realizes a Contingency should fire, you will no longer need it either because you have handled the problem otherwise, or because you are already dead. It's another of those things that subtly handicap the player compared to the AI -- its contingencies fire immediately because they don't actually use opcode 232, working off their assigned scripts instead, which also allows them to use conditions not available to the player. But I digress. The split-second responsiveness makes AI mages much more unassailable than a PC mage could ever be. In short, AI contingencies work as advertised, player contingencies very much do not.

There was a TobEx config parameter that allowed the check interval to be user-defined, with the usual warnings that the game may lag, etc. With the EE incorporating many of the functionalities of TobEx, has this been added? If not, is it possible to emulate that behavior somehow?

Comments

  • kjeronkjeron Member Posts: 2,367
    You could emulate it through the players scripts and by converting the Contingency Spells into Sequencers.
    [spoiler]
    Changing the Contingency spells into Sequencer Spells:
    COPY_EXISTING ~SPWI617.spl~ override
    LPF ALTER_SPELL_EFFECT INT_VAR match_opcode = 234 opcode = 257 parameter2 = 1 END
    Duplicating the Sequencer sub-spell for the Contingency spell:
    COPY_EXISTING ~SPWI420D.spl~ ~override\SPWI617D.spl~
    REPLACE_TEXTUALLY ~SPWI420~ ~SPWI617~
    Using different Hotkey's for different conditions or the same Hotkey to cycle through conditions in the character's script:

    IF
    Hotkey(_)
    Global("SPWI617","LOCALS",0)
    THEN
    RESPONSE #100
    SetGlobal("SPWI617","LOCALS",1)
    END

    IF
    Hotkey(_)
    Global("SPWI617","LOCALS",1)
    THEN
    RESPONSE #100
    SetGlobal("SPWI617","LOCALS",2)
    END

    IF
    Hotkey(_)
    Global("SPWI617","LOCALS",2)
    THEN
    RESPONSE #100
    SetGlobal("SPWI617","LOCALS",0)
    END

    IF
    Global("SPWI617","LOCALS",1)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI617D",LastSeenBy(Myself))
    SetGlobal("SPWI617","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI617","LOCALS",2)
    HPPercentLT(Myself,50)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI617D",Myself)
    SetGlobal("SPWI617","LOCALS",0)
    SetInterrupt(TRUE)
    END
    [/spoiler]
    If you want better Troll deaths, I made a mod for that some time ago:
    https://forums.beamdog.com/discussion/38287/trolls-unconscious-on-demand#latest
  • UnpersonUnperson Member Posts: 29
    Hey, thanks. That worked like a charm.

    It's a bit tedious to script all the trigger/target combinations for a Chain Contingency, but it's way more responsive that way. I think I have it working, I just have to test it a bit to see if the state checks are correct, and it'll be good to go. This method is also more flexible in that it allows the player to deactivate the contingency checks on the fly, thus not "wasting" contingencies on the first goblin and forcing you to rest to set it up again.

    Unfortunately I can't WeiDU my way out of a wet paper bag, so that may be a while...
  • UnpersonUnperson Member Posts: 29
    edited June 2016
    Ok, I basically have both contingencies working. Problem I'm encountering is that being confused or charmed ceases script execution, which is really weird. Other disabling conditions (Hold, Sleep, Stun...) do not cause this. Any ideas?

    edit: also happens with Feeblemind.
    Post edited by Unperson on
  • UnpersonUnperson Member Posts: 29
    edited June 2016
    Well, I'm posting the script I wrote based on kjeron's, for Chain Contingency. From what I've tested, it works as intended, except for Feeblemind/Charm/Confusion interrupting the execution (which admittedly is a pretty big problem). I'll ask around and see if there's a way to circumvent this outside of dumping a piece of this into baldur.bcs, which for obvious reasons I'd rather not do.

    [spoiler]

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",0)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",1)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30973) // See Enemy
    END

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",1)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",2)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,31112) // Hit
    END

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",2)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",3)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30975) // Hit Points at 50%
    END

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",3)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",4)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30986) // Helpless
    END

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",4)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",5)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30982) // Hit Points at 25%
    END

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",5)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",6)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30984) // Hit Points at 10%
    END

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",6)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",7)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30988) // Poisoned
    END

    IF
    Hotkey(F)
    Global("SPWI908C","LOCALS",7)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908C","LOCALS",0)
    DisplayStringHead(Myself,34559) // Contingency Removed
    END

    IF
    Hotkey(G)
    Global("SPWI908T","LOCALS",0)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908T","LOCALS",1)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30994) // Nearest Enemy
    END

    IF
    Hotkey(G)
    Global("SPWI908T","LOCALS",1)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908T","LOCALS",2)
    DisplayStringHead(Myself,26328) // Chain Contingency
    DisplayStringHead(Myself,30992) // Myself
    END

    IF
    Hotkey(G)
    Global("SPWI908T","LOCALS",2)
    THEN
    RESPONSE #100
    SetGlobal("SPWI908T","LOCALS",0)
    DisplayStringHead(Myself,34559) // Contingency Removed
    END

    IF
    Global("SPWI908C","LOCALS",1)
    Global("SPWI908T","LOCALS",1)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",LastSeenBy(Myself))
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",1)
    Global("SPWI908T","LOCALS",2)
    See([EVILCUTOFF])
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",Myself)
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",2)
    Global("SPWI908T","LOCALS",1)
    HitBy([ANYTHING],0)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",LastSeenBy(Myself))
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",2)
    Global("SPWI908T","LOCALS",2)
    HitBy([ANYTHING],0)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",Myself)
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",3)
    Global("SPWI908T","LOCALS",1)
    HPPercentLT(Myself,50)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",LastSeenBy(Myself))
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",3)
    Global("SPWI908T","LOCALS",2)
    HPPercentLT(Myself,50)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",Myself)
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",4)
    Global("SPWI908T","LOCALS",1)
    StateCheck(Myself,CD_STATE_NOTVALID) // Sleeping, Berserk, Panic, Stunned, Helpless, Charmed, Feebleminded, Confused
    !StateCheck(Myself,STATE_SILENCED)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",LastSeenBy(Myself))
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",4)
    Global("SPWI908T","LOCALS",2)
    StateCheck(Myself,CD_STATE_NOTVALID) // Sleeping, Berserk, Panic, Stunned, Helpless, Charmed, Feebleminded, Confused
    !StateCheck(Myself,STATE_SILENCED)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",Myself)
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",5)
    Global("SPWI908T","LOCALS",1)
    HPPercentLT(Myself,25)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",LastSeenBy(Myself))
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",5)
    Global("SPWI908T","LOCALS",2)
    HPPercentLT(Myself,25)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",Myself)
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",6)
    Global("SPWI908T","LOCALS",1)
    HPPercentLT(Myself,10)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",LastSeenBy(Myself))
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",6)
    Global("SPWI908T","LOCALS",2)
    HPPercentLT(Myself,10)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",Myself)
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",7)
    Global("SPWI908T","LOCALS",1)
    StateCheck(Myself,STATE_POISONED)
    See([EVILCUTOFF])
    !StateCheck(LastSeenBy(Myself),STATE_NOT_TARGETABLE)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",LastSeenBy(Myself))
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END

    IF
    Global("SPWI908C","LOCALS",7)
    Global("SPWI908T","LOCALS",2)
    StateCheck(Myself,STATE_POISONED)
    THEN
    RESPONSE #100
    SetInterrupt(FALSE)
    ReallyForceSpellRES("SPWI908D",Myself)
    SetGlobal("SPWI908C","LOCALS",0)
    SetInterrupt(TRUE)
    END
    [/spoiler]

    This of course assumes that there is a spwi908d.spl and the original spwi908.spl was modified, as per kjeron's post.
Sign In or Register to comment.