From b44741b8aaf1f8804ad3c98a515ed2a32c26ee4a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 12 Sep 2021 21:04:40 +0200 Subject: [PATCH] - dsdehacked: allow dynamic creation of new states. --- src/gamedata/d_dehacked.cpp | 41 ++++++++++++++++++++++++++----- src/gamedata/info.h | 3 +++ src/p_states.cpp | 17 +++++++++---- src/playsim/p_actionfunctions.cpp | 1 - src/playsim/p_pspr.cpp | 30 ++++++++++++++-------- src/serializer_doom.cpp | 16 +++++++++++- 6 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/gamedata/d_dehacked.cpp b/src/gamedata/d_dehacked.cpp index bd9de53dc..db87de3cd 100644 --- a/src/gamedata/d_dehacked.cpp +++ b/src/gamedata/d_dehacked.cpp @@ -150,7 +150,7 @@ struct MBFArgs int argsused; }; static TMap stateargs; -static FState* FindState(int statenum); +static FState* FindState(int statenum, bool mustexist = false); // DeHackEd trickery to support MBF-style parameters // List of states that are hacked to use a codepointer @@ -341,7 +341,7 @@ FString PatchName; static int PatchSize; static char *Line1, *Line2; static int dversion, pversion; -static bool including, includenotext; +static bool including, includenotext, dsdhacked = false; static int LumpFileNum; static const char *unknown_str = "Unknown key %s encountered in %s %d.\n"; @@ -373,6 +373,8 @@ static int PatchPars (int); static int PatchCodePtrs (int); static int PatchMusic (int); static int DoInclude (int); +static int PatchSpriteNames (int); +static int PatchSoundNames(int) {} // todo static bool DoDehPatch(); static const struct { @@ -396,6 +398,8 @@ static const struct { { "[PARS]", PatchPars }, { "[CODEPTR]", PatchCodePtrs }, { "[MUSIC]", PatchMusic }, + { "[SPRITES]", PatchSpriteNames }, + { "[SOUNDS]", PatchSoundNames }, { NULL, NULL }, }; @@ -456,12 +460,12 @@ static int FindSprite (const char *sprname) return f == UnchangedSpriteNames.Size() ? -1 : f; } -static FState *FindState (int statenum) +static FState *FindState (int statenum, bool mustexist) { int stateacc; unsigned i; - if (statenum == 0) + if (statenum <= 0) return NULL; for (i = 0, stateacc = 1; i < StateMap.Size(); i++) @@ -480,6 +484,18 @@ static FState *FindState (int statenum) } stateacc += StateMap[i].StateSpan; } + if (dsdhacked && !mustexist) + { + auto p = dehExtStates.CheckKey(statenum); + if (p) return *p; + auto state = (FState*)ClassDataAllocator.Alloc(sizeof(FState)); + dehExtStates.Insert(statenum, state); + memset(state, 0, sizeof(*state)); + state->Tics = -1; + state->NextState = state; + state->DehIndex = statenum; + + } return NULL; } @@ -745,7 +761,7 @@ static void CreatePlaySoundFunc(FunctionCallEmitter &emitters, int value1, int v // misc1 = state, misc2 = probability static void CreateRandomJumpFunc(FunctionCallEmitter &emitters, int value1, int value2, MBFParamState* state) { // A_Jump - auto symlabel = StateLabels.AddPointer(FindState(value1)); + auto symlabel = StateLabels.AddPointer(FindState(value1, true)); emitters.AddParameterIntConst(value2); // maxchance emitters.AddParameterIntConst(symlabel); // jumpto @@ -2288,7 +2304,7 @@ static int PatchPointer (int ptrNum) { if ((unsigned)ptrNum < CodePConv.Size() && (!stricmp (Line1, "Codep Frame"))) { - FState *state = FindState (CodePConv[ptrNum]); + FState *state = FindState (CodePConv[ptrNum], true); if (state) { int index = atoi(Line2); @@ -3112,6 +3128,11 @@ static bool DoDehPatch() dversion = 1; else if (dversion == 21) dversion = 4; + else if (dversion == 2021) + { + dversion = 4; + dsdhacked = true; + } else { Printf ("Patch created with unknown DOOM version.\nAssuming version 1.9.\n"); @@ -3319,6 +3340,7 @@ static bool LoadDehSupp () else if (sc.Compare("StateMap")) { bool addit = StateMap.Size() == 0; + int dehcount = 0; sc.MustGetStringName("{"); while (!sc.CheckString("}")) @@ -3360,6 +3382,13 @@ static bool LoadDehSupp () if (sc.CheckString("}")) break; sc.MustGetStringName(","); + // This mapping is mainly for P_SetSafeFlash. + for (int i = 0; i < s.StateSpan; i++) + { + dehExtStates.Insert(dehcount, s.State + i); + s.State[i].DehIndex = dehcount; + dehcount++; + } } } else if (sc.Compare("SoundMap")) diff --git a/src/gamedata/info.h b/src/gamedata/info.h index d9e89037b..69ad560e0 100644 --- a/src/gamedata/info.h +++ b/src/gamedata/info.h @@ -112,6 +112,7 @@ struct FState uint8_t DefineFlags; int32_t Misc1; // Was changed to int8_t, reverted to long for MBF compat int32_t Misc2; // Was changed to uint8_t, reverted to long for MBF compat + int32_t DehIndex; // we need this to resolve offsets in P_SetSafeFlash. public: inline int GetFrame() const { @@ -178,6 +179,8 @@ public: }; +extern TMap dehExtStates; + struct FStateLabels; struct FStateLabel { diff --git a/src/p_states.cpp b/src/p_states.cpp index 6c3ff2f33..90241dd54 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -44,6 +44,7 @@ // stores indices for symbolic state labels for some old-style DECORATE functions. FStateLabelStorage StateLabels; +TMap dehExtStates; // Each state is owned by an actor. Actors can own any number of // states, but a single state cannot be owned by more than one @@ -138,6 +139,7 @@ FString FState::StaticGetStateName(const FState *state, PClassActor *info) } if (so == nullptr) { + if (state->DehIndex > 0) return FStringf("DehExtraState.%d", state->DehIndex); return ""; } return FStringf("%s.%d", so->TypeName.GetChars(), int(state - so->GetStates())); @@ -1029,6 +1031,7 @@ int FStateDefinitions::FinishStates(PClassActor *actor) for (i = 0; i < count; i++) { + realstates[i].DehIndex = -1; // resolve labels and jumps switch (realstates[i].DefineFlags) { @@ -1074,16 +1077,20 @@ void DumpStateHelper(FStateLabels *StateList, const FString &prefix) { for (int i = 0; i < StateList->NumLabels; i++) { - if (StateList->Labels[i].State != NULL) + auto state = StateList->Labels[i].State; + if (state != NULL) { - const PClassActor *owner = FState::StaticFindStateOwner(StateList->Labels[i].State); + const PClassActor *owner = FState::StaticFindStateOwner(state); if (owner == NULL) { - Printf(PRINT_LOG, "%s%s: invalid\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars()); + if (state->DehIndex >= 0) + Printf(PRINT_LOG, "%s%s: DehExtra %d\n", prefix.GetChars(), state->DehIndex); + else + Printf(PRINT_LOG, "%s%s: invalid\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars()); } else { - Printf(PRINT_LOG, "%s%s: %s\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(), FState::StaticGetStateName(StateList->Labels[i].State).GetChars()); + Printf(PRINT_LOG, "%s%s: %s\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(), FState::StaticGetStateName(state).GetChars()); } } if (StateList->Labels[i].Children != NULL) @@ -1135,7 +1142,7 @@ DEFINE_ACTION_FUNCTION(FState, DistanceTo) { // Safely calculate the distance between two states. auto o1 = FState::StaticFindStateOwner(self); - if (o1->OwnsState(other)) retv = int(other - self); + if (o1 && o1->OwnsState(other)) retv = int(other - self); } ACTION_RETURN_INT(retv); } diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 5eb72ef17..de0abf053 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -128,7 +128,6 @@ static int CallStateChain (AActor *self, AActor *actor, FState *state) if (state->ActionFunc->Unsafe) { // If an unsafe function (i.e. one that accesses user variables) is being detected, print a warning once and remove the bogus function. We may not call it because that would inevitably crash. - auto owner = FState::StaticFindStateOwner(state); Printf(TEXTCOLOR_RED "Unsafe state call in state %s to %s which accesses user variables. The action function has been removed from this state\n", FState::StaticGetStateName(state).GetChars(), state->ActionFunc->PrintableName); state->ActionFunc = nullptr; diff --git a/src/playsim/p_pspr.cpp b/src/playsim/p_pspr.cpp index 3864967b0..d65b4d003 100644 --- a/src/playsim/p_pspr.cpp +++ b/src/playsim/p_pspr.cpp @@ -1352,25 +1352,35 @@ void P_SetSafeFlash(AActor *weapon, player_t *player, FState *flashstate, int in P_SetPsprite(player, PSP_FLASH, flashstate + index, true); return; } - else + else if (flashstate->DehIndex < 0) { - // oh, no! The state is beyond the end of the state table so use the original flash state. + // oh, no! The state is beyond the end of the state table so use the original flash state if it does not have a Dehacked index. P_SetPsprite(player, PSP_FLASH, flashstate, true); return; } + else break; // no need to continue. } // try again with parent class cls = static_cast(cls->ParentClass); } - // if we get here the state doesn't seem to belong to any class in the inheritance chain - // This can happen with Dehacked if the flash states are remapped. - // The only way to check this would be to go through all Dehacked modifiable actors, convert - // their states into a single flat array and find the correct one. - // Rather than that, just check to make sure it belongs to something. - if (FState::StaticFindStateOwner(flashstate + index) == NULL) - { // Invalid state. With no index offset, it should at least be valid. - index = 0; + + // if we get here the target state doesn't belong to any class in the inheritance chain. + // This can happen with Dehacked if the flash states are remapped. + // In this case we should check the Dehacked state map to get the proper state. + if (flashstate->DehIndex >= 0) + { + auto pTargetstate = dehExtStates.CheckKey(flashstate->DehIndex + index); + if (pTargetstate) + { + P_SetPsprite(player, PSP_FLASH, *pTargetstate, true); + return; + } } + + // If we still haven't found anything here, just use the base flash state. + // Normally this code should not be reachable. + index = 0; + } P_SetPsprite(player, PSP_FLASH, flashstate + index, true); } diff --git a/src/serializer_doom.cpp b/src/serializer_doom.cpp index 953bb9afc..63dc08b07 100644 --- a/src/serializer_doom.cpp +++ b/src/serializer_doom.cpp @@ -346,6 +346,13 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState arc.w->Uint((uint32_t)(state - info->GetStates())); arc.w->EndArray(); } + else if (state->DehIndex >= 0) + { + arc.w->StartArray(); + arc.w->String("@DehExtraState@"); + arc.w->Uint(state->DehIndex); + arc.w->EndArray(); + } else { arc.w->Null(); @@ -373,11 +380,18 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState assert(cls.IsString() && ndx.IsUint()); if (cls.IsString() && ndx.IsUint()) { - PClassActor *clas = PClass::FindActor(UnicodeToString(cls.GetString())); + auto str = UnicodeToString(cls.GetString()); + PClassActor *clas = PClass::FindActor(str); if (clas && ndx.GetUint() < (unsigned)clas->GetStateCount()) { state = clas->GetStates() + ndx.GetUint(); } + else if (!strcmp(str, "@DehExtraState@")) + { + state = nullptr; + auto pState = dehExtStates.CheckKey(ndx.GetInt()); + if (pState) state = *pState; + } else { // this can actually happen by changing the DECORATE so treat it as a warning, not an error.