From 630e39dd62c5896e7876553da96614a7e5e4401e Mon Sep 17 00:00:00 2001 From: Dan Doel Date: Fri, 10 Sep 2021 01:28:19 -0400 Subject: [PATCH 1/4] Start replacement logic for Stunned countdown - There's an extra signal sent on wrapping around in initiative that breaks the logic for when to tick because both new and old initiative numbers are 0 - The logic is still faulty if there are duplicate initiative numbers, but this at least replicates what seems to be the intended original behavior. --- TemplePlus/condition.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/TemplePlus/condition.cpp b/TemplePlus/condition.cpp index 23fa612d3..4d5fa48bf 100644 --- a/TemplePlus/condition.cpp +++ b/TemplePlus/condition.cpp @@ -324,6 +324,7 @@ class ConditionFunctionReplacement : public TempleFix { static int ManyShotDamage(DispatcherCallbackArgs args); static bool StunningFistHook(objHndl objHnd, objHndl caster, int DC, int saveType, int flags); + static int StunnedInitiativeUpdate(DispatcherCallbackArgs args); static int AnimalCompanionLevelHook(objHndl, Stat shouldBeClassDruid); @@ -573,6 +574,9 @@ class ConditionFunctionReplacement : public TempleFix { // Stunning Fist extension redirectCall(0x100E84B0, StunningFistHook); + // Fix Stunned countdown + replaceFunction(0x100E9500, StunnedInitiativeUpdate); + // Animal Companion Bonus Levels Extension redirectCall(0x100FC18C, AnimalCompanionLevelHook); redirectCall(0x100FC2D6, AnimalCompanionLevelHook); @@ -4117,6 +4121,42 @@ bool ConditionFunctionReplacement::StunningFistHook(objHndl objHnd, objHndl cast return result; } +int ConditionFunctionReplacement::StunnedInitiativeUpdate(DispatcherCallbackArgs args){ + GET_DISPIO(dispIoTypeSendSignal, DispIoD20Signal); + + auto newInit = dispIo->data1; + auto oldInit = dispIo->data2; + + // Avoid degenerate initiative wrap around signal + if (newInit == 0 && oldInit == 0) return 0; + + auto durIx = args.GetData1(); + auto initIx = args.GetData2(); + + auto duration = args.GetCondArg(durIx); + auto tickOn = args.GetCondArg(initIx); + + if (oldInit <= newInit) { + if (newInit > tickOn && tickOn >= oldInit) { + return 0; + } + } + else { + if (oldInit <= tickOn || tickOn < newInit) { + return 0; + } + } + + if (duration <= 1) { + args.RemoveCondition(); + } + else { + args.SetCondArg(durIx, duration-1); + } + + return 0; +} + int ConditionFunctionReplacement::TurnUndeadHook(objHndl handle, Stat shouldBeClassCleric, DispIoD20ActionTurnBased * evtObj){ From 1d9e7dc85558e76ff29754eb48db5551fa81e4c0 Mon Sep 17 00:00:00 2001 From: Dan Doel Date: Sat, 11 Sep 2021 12:22:57 -0400 Subject: [PATCH 2/4] Added some explanations in stun ticker --- TemplePlus/condition.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TemplePlus/condition.cpp b/TemplePlus/condition.cpp index 4d5fa48bf..def3c1fc2 100644 --- a/TemplePlus/condition.cpp +++ b/TemplePlus/condition.cpp @@ -4137,11 +4137,15 @@ int ConditionFunctionReplacement::StunnedInitiativeUpdate(DispatcherCallbackArgs auto tickOn = args.GetCondArg(initIx); if (oldInit <= newInit) { + // Initiative wrapping around from N to N+K, so don't tick + // if the target is in the interval (N+K,N] if (newInit > tickOn && tickOn >= oldInit) { return 0; } } else { + // Initiative counting down from N+K to N, so don't tick unless + // the target is in the interval (N+K,N] if (oldInit <= tickOn || tickOn < newInit) { return 0; } From b85b483a9ed72b549f18a7e6b8268cdd6ba0c51e Mon Sep 17 00:00:00 2001 From: Dan Doel Date: Sat, 11 Sep 2021 12:23:40 -0400 Subject: [PATCH 3/4] Tweak initiative counting logic - There seems to have been a mistake translating it from the original, which always caused a 0->0 update message, rather than an N->0 update message on wrapping around (and thus no N->0 message was sent before the 0->MAX message, causing some intervals to be skipped). - There probably shouldn't be an N->0 message when N is already <=0 due to actual rolled initiatives being that low. --- TemplePlus/turn_based.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/TemplePlus/turn_based.cpp b/TemplePlus/turn_based.cpp index 17122bb18..56dbe65da 100644 --- a/TemplePlus/turn_based.cpp +++ b/TemplePlus/turn_based.cpp @@ -120,8 +120,15 @@ void TurnBasedSys::InitiativeListNextActor() dispatch.DispatcherProcessor(dispatcher, dispTypeInitiative, 0, 0); } } - actorInitiative = nextInitiativeIdx = 0; - InitiativeRefresh(actorInitiative, 0); + nextInitiativeIdx = 0; + + // If initiative hasn't already counted down to 0 (or less), make + // that happen. Original always counted to 0 here, but that is + // problematic. + if (actorInitiative > 0) { + InitiativeRefresh(actorInitiative, 0); + actorInitiative = 0; + } } objHndl actorNext = objHndl::null; From 3fdd76de0a348c1ec1260314cfd811788406bb65 Mon Sep 17 00:00:00 2001 From: Dan Doel Date: Sun, 12 Sep 2021 13:24:11 -0400 Subject: [PATCH 4/4] Tweak/document stun tick logic - Clarify relation to initiative signals, and generalize the avoidance of 'degenerate' messages, although they shouldn't occur anymore. - Made the intervals mentioned in the comments no longer backwards. --- TemplePlus/condition.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/TemplePlus/condition.cpp b/TemplePlus/condition.cpp index def3c1fc2..4f5d4785c 100644 --- a/TemplePlus/condition.cpp +++ b/TemplePlus/condition.cpp @@ -4127,8 +4127,10 @@ int ConditionFunctionReplacement::StunnedInitiativeUpdate(DispatcherCallbackArgs auto newInit = dispIo->data1; auto oldInit = dispIo->data2; - // Avoid degenerate initiative wrap around signal - if (newInit == 0 && oldInit == 0) return 0; + // We shouldn't get initiative updates for turns with the same + // initiative. In case we do, exit, because the logic below acts as + // if an entire round has ticked in that case. + if (newInit == oldInit) return 0; auto durIx = args.GetData1(); auto initIx = args.GetData2(); @@ -4136,16 +4138,16 @@ int ConditionFunctionReplacement::StunnedInitiativeUpdate(DispatcherCallbackArgs auto duration = args.GetCondArg(durIx); auto tickOn = args.GetCondArg(initIx); - if (oldInit <= newInit) { + if (oldInit < newInit) { // Initiative wrapping around from N to N+K, so don't tick - // if the target is in the interval (N+K,N] + // if the target is in the interval [N,N+K) if (newInit > tickOn && tickOn >= oldInit) { return 0; } } else { // Initiative counting down from N+K to N, so don't tick unless - // the target is in the interval (N+K,N] + // the target is in the interval [N,N+K) if (oldInit <= tickOn || tickOn < newInit) { return 0; }