From 9ab65578226b69583ca257edbe96f260ac365e57 Mon Sep 17 00:00:00 2001 From: Shiny Metagross <30511800+ShinyMetagross@users.noreply.github.com> Date: Fri, 8 Jul 2022 22:47:12 -0700 Subject: [PATCH] A_ChangeModel now has generatorIndex - To really take advantage of this function, I thought it would be useful to be able to add additional models if the user wants to. Let's say you got a player model at index 0. Your gun model has the same frames, but you don't want to duplicate the modeldef data. With generator index, you don't need to duplicate the data, just tell generator index to clone frame data from index 0. - Implemented a little something to check if a negative skin or model index were passed, and prevent modders from pulling that off. --- src/playsim/actor.h | 1 + src/playsim/p_actionfunctions.cpp | 38 ++++++++++++++++++++------- src/playsim/p_mobj.cpp | 1 + src/r_data/models.cpp | 22 +++++++++++++++- wadsrc/static/zscript/actors/actor.zs | 2 +- 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 2780f856f..bcd08b9c1 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -682,6 +682,7 @@ public: TArray modelIDs; TArray skinIDs; TArray surfaceSkinIDs; + TArray modelFrameGenerators; DActorModelData() = default; virtual void Serialize(FSerializer& arc) override; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 2f7743ea2..2a898c6dd 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -5038,17 +5038,28 @@ enum ChangeModelFlags DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) { PARAM_ACTION_PROLOGUE(AActor); - PARAM_NAME(modeldef) - PARAM_INT(modelindex) - PARAM_STRING_VAL(modelpath) - PARAM_NAME(model) - PARAM_INT(skinindex) - PARAM_STRING_VAL(skinpath) - PARAM_NAME(skin) - PARAM_INT(flags) + PARAM_NAME(modeldef); + PARAM_INT(modelindex); + PARAM_STRING_VAL(modelpath); + PARAM_NAME(model); + PARAM_INT(skinindex); + PARAM_STRING_VAL(skinpath); + PARAM_NAME(skin); + PARAM_INT(flags); + PARAM_INT(generatorindex); if (self == nullptr) ACTION_RETURN_BOOL(false); + else if (modelindex < 0) + { + Printf("Attempt to pass invalid model index %d in %s, index must be non-negative.", modelindex, self->GetCharacterName()); + ACTION_RETURN_BOOL(false); + } + else if (skinindex < 0) + { + Printf("Attempt to pass invalid skin index %d in %s, index must be non-negative.", skinindex, self->GetCharacterName()); + ACTION_RETURN_BOOL(false); + } AActor* mobj = (ACTION_CALL_FROM_PSPRITE() && (flags & CMDL_WEAPONTOPLAYER)) || ACTION_CALL_FROM_INVENTORY() ? self : stateowner; @@ -5062,6 +5073,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) ptr->modelIDs = *new TArray(); ptr->skinIDs = *new TArray(); ptr->surfaceSkinIDs = *new TArray(); + ptr->modelFrameGenerators = *new TArray(); ptr->modelDef = NAME_None; mobj->modelData = ptr; mobj->hasmodel = 1; @@ -5071,6 +5083,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) int maxModels = mobj->modelData->modelIDs.Size(); int maxSkins = mobj->modelData->skinIDs.Size(); int maxSurfaceSkins = mobj->modelData->surfaceSkinIDs.Size(); + int maxGenerators = mobj->modelData->modelFrameGenerators.Size(); int skinPosition = skinindex + modelindex * MD3_MAX_SURFACES; @@ -5079,6 +5092,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) //[SM] - Let's clear out any potential entries at the specified indices mobj->modelData->modelDef = modeldef; if(maxModels > modelindex) mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs[modelindex]); + if(maxGenerators > modelindex) mobj->modelData->modelFrameGenerators.Pop(mobj->modelData->modelFrameGenerators[modelindex]); + if (flags & CMDL_USESURFACESKIN) { if (maxSurfaceSkins > skinPosition) @@ -5092,12 +5107,14 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) //[SM] - We need to fill up any holes this new index will make so that it doesn't leave behind any undefined behavior while ((int)mobj->modelData->modelIDs.Size() < modelindex) mobj->modelData->modelIDs.Push(-1); + while ((int)mobj->modelData->modelFrameGenerators.Size() < modelindex) mobj->modelData->modelFrameGenerators.Push(-1); if (flags & CMDL_USESURFACESKIN) while ((int)mobj->modelData->surfaceSkinIDs.Size() < skinPosition) mobj->modelData->surfaceSkinIDs.Push(FNullTextureID()); else while ((int)mobj->modelData->skinIDs.Size() < skinindex) mobj->modelData->skinIDs.Push(FNullTextureID()); mobj->modelData->modelIDs.Insert(modelindex, queryModel); + mobj->modelData->modelFrameGenerators.Insert(modelindex, generatorindex); if (flags & CMDL_USESURFACESKIN) mobj->modelData->surfaceSkinIDs.Insert(skinPosition, skin != NAME_None ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : FNullTextureID()); else @@ -5118,15 +5135,18 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) //[SM] - if an indice of modelIDs or skinIDs comes up blank and it's the last one, just delete it. For using very large amounts of indices, common sense says to just not run this repeatedly. while (mobj->modelData->modelIDs.Size() > 0 && mobj->modelData->modelIDs.Last() == -1) mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs.Last()); + while (mobj->modelData->modelFrameGenerators.Size() > 0 && mobj->modelData->modelFrameGenerators.Last() == -1) + mobj->modelData->modelFrameGenerators.Pop(mobj->modelData->modelFrameGenerators.Last()); while (mobj->modelData->skinIDs.Size() > 0 && mobj->modelData->skinIDs.Last() == FNullTextureID()) mobj->modelData->skinIDs.Pop(mobj->modelData->skinIDs.Last()); while (mobj->modelData->surfaceSkinIDs.Size() > 0 && mobj->modelData->surfaceSkinIDs.Last() == FNullTextureID()) mobj->modelData->surfaceSkinIDs.Pop(mobj->modelData->surfaceSkinIDs.Last()); - if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && mobj->modelData->surfaceSkinIDs.Size() == 0 && modeldef == NAME_None) + if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->modelFrameGenerators.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && mobj->modelData->surfaceSkinIDs.Size() == 0 && modeldef == NAME_None) { mobj->hasmodel = mobj->modelData->hasModel; mobj->modelData->modelIDs.Reset(); + mobj->modelData->modelFrameGenerators.Reset(); mobj->modelData->skinIDs.Reset(); mobj->modelData->surfaceSkinIDs.Reset(); mobj->modelData->Destroy(); diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 400f00748..75aeae6cd 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -1369,6 +1369,7 @@ void DActorModelData::Serialize(FSerializer& arc) ("modelIDs", modelIDs) ("skinIDs", skinIDs) ("surfaceSkinIDs", surfaceSkinIDs) + ("modelFrameGenerators", modelFrameGenerators) ("hasModel", hasModel); } diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index b87970689..0c0a1a29c 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -278,15 +278,35 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr } } + //[SM] - if we added any models for the frame to also render, then we also need to update modelsAmount for this smf + if (actor->modelData != nullptr) + { + if (tempsmf->modelsAmount < actor->modelData->modelIDs.Size()) + { + tempsmf->modelsAmount = actor->modelData->modelIDs.Size(); + while (tempsmf->modelframes.Size() < actor->modelData->modelIDs.Size()) tempsmf->modelframes.Push(-1); + while (smfNext->modelframes.Size() < actor->modelData->modelIDs.Size()) smfNext->modelframes.Push(-1); + } + } + for (int i = 0; i < tempsmf->modelsAmount; i++) { if (actor->modelData != nullptr) { if (i < (int)actor->modelData->modelIDs.Size()) { - if(actor->modelData->modelIDs[i] != -1) + if (actor->modelData->modelIDs[i] != -1) tempsmf->modelIDs[i] = actor->modelData->modelIDs[i]; } + if (i < (int)actor->modelData->modelFrameGenerators.Size()) + { + //[SM] - We will use this little snippet to allow a modder to specify a model index to clone. It's also pointless to clone something that clones something else in this case. And causes me headaches. + if (actor->modelData->modelFrameGenerators[i] >= 0 && smf->modelframes[i] != -1) + { + tempsmf->modelframes[i] = tempsmf->modelframes[actor->modelData->modelFrameGenerators[i]]; + if (smfNext) smfNext->modelframes[i] = smfNext->modelframes[actor->modelData->modelFrameGenerators[i]]; + } + } if (i < (int)actor->modelData->skinIDs.Size()) { if (actor->modelData->skinIDs[i].isValid()) diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 5da286695..f1be78e48 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -1132,7 +1132,7 @@ class Actor : Thinker native native void A_SetBlend(color color1, double alpha, int tics, color color2 = 0, double alpha2 = 0.); deprecated("2.3", "Use 'b = [true/false]' instead") native void A_ChangeFlag(string flagname, bool value); native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE); - action native void A_ChangeModel(name modeldef, int modelindex = 0, string modelpath = "", name model = "", int skinindex = 0, string skinpath = "", name skin = "", int flags = 0); + action native void A_ChangeModel(name modeldef, int modelindex = 0, string modelpath = "", name model = "", int skinindex = 0, string skinpath = "", name skin = "", int flags = 0, int generatorindex = -1); void A_SetFriendly (bool set) {