diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 2780f856f7..bcd08b9c11 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 2f7743ea27..2a898c6dd5 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 400f007484..75aeae6cd6 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 b87970689a..0c0a1a29c1 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 5da2866957..f1be78e48d 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) {