diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 276b83736..63ce2691d 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -1068,6 +1068,9 @@ public: DVector3 WorldOffset; double Speed; double FloatSpeed; + FName modelDef; + int models[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; //[SM] - I hate this solution, but it get's the job done + FTextureID skins[16]; // interaction info FBlockNode *BlockNode; // links in blocks (if needed) diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index a0407e028..791a33a50 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -69,6 +69,7 @@ #include "sbar.h" #include "actorinlines.h" #include "types.h" +#include "model.h" static FRandom pr_camissile ("CustomActorfire"); static FRandom pr_cabullet ("CustomBullet"); @@ -5020,6 +5021,46 @@ DEFINE_ACTION_FUNCTION(AActor, A_SetMugshotState) return 0; } +//========================================================================== +// +// A_ChangeModel(modeldef, modelpath, model, modelindex, skinpath, skin, skinid, flags) +// +// This function allows the changing of an actor's modeldef, or models and/or skins at a given index +//========================================================================== + +enum ChangeModelFlags +{ + CMDL_WEAPONTOPLAYER = 1 +}; + +DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) +{ + PARAM_ACTION_PROLOGUE(AActor); + PARAM_NAME(modeldef) + PARAM_STRING_VAL(modelpath) + PARAM_NAME(model) + PARAM_INT(modelindex) + PARAM_STRING_VAL(skinpath) + PARAM_NAME(skin) + PARAM_INT(skinindex) + PARAM_INT(flags) + + if (self == nullptr) + ACTION_RETURN_BOOL(false); + + AActor* mobj = ACTION_CALL_FROM_PSPRITE() && (flags & CMDL_WEAPONTOPLAYER) ? self : stateowner; + + if (modelpath[(int)modelpath.Len() - 1] != '/') modelpath += '/'; + if (skinpath[(int)skinpath.Len() - 1] != '/') skinpath += '/'; + + mobj->hasmodel = modeldef == nullptr && !mobj->hasmodel ? 1 : 0; + mobj->modelDef = modeldef; + mobj->models[modelindex] = model != nullptr ? FindModel(modelpath.GetChars(), model.GetChars()) : -1; + mobj->skins[skinindex] = skin != nullptr ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : mobj->skins[skinindex] = FNullTextureID(); + + return 0; +} + // This needs to account for the fact that internally renderstyles are stored as a series of operations, // but the script side only cares about symbolic constants. DEFINE_ACTION_FUNCTION(AActor, GetRenderStyle) diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 12ec34f01..875e1d6fe 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -55,7 +55,7 @@ EXTERN_CVAR (Bool, r_drawvoxels) extern TDeletingArray Voxels; extern TDeletingArray VoxelDefs; -void RenderFrameModels(FModelRenderer* renderer, FLevelLocals* Level, const FSpriteModelFrame* smf, const FState* curState, const int curTics, const PClass* ti, int translation); +void RenderFrameModels(FModelRenderer* renderer, FLevelLocals* Level, const FSpriteModelFrame* smf, const FState* curState, const int curTics, const PClass* ti, int translation, AActor* actor); void RenderModel(FModelRenderer *renderer, float x, float y, float z, FSpriteModelFrame *smf, AActor *actor, double ticFrac) @@ -176,14 +176,14 @@ void RenderModel(FModelRenderer *renderer, float x, float y, float z, FSpriteMod float orientation = scaleFactorX * scaleFactorY * scaleFactorZ; renderer->BeginDrawModel(actor->RenderStyle, smf, objectToWorldMatrix, orientation < 0); - RenderFrameModels(renderer, actor->Level, smf, actor->state, actor->tics, actor->GetClass(), translation); + RenderFrameModels(renderer, actor->Level, smf, actor->state, actor->tics, actor->modelDef != nullptr ? PClass::FindActor(actor->modelDef) : actor->GetClass(), translation, actor); renderer->EndDrawModel(actor->RenderStyle, smf); } void RenderHUDModel(FModelRenderer *renderer, DPSprite *psp, float ofsX, float ofsY) { AActor * playermo = players[consoleplayer].camera; - FSpriteModelFrame *smf = psp->Caller != nullptr ? FindModelFrame(psp->Caller->GetClass(), psp->GetSprite(), psp->GetFrame(), false) : nullptr; + FSpriteModelFrame *smf = psp->Caller != nullptr ? FindModelFrame(psp->Caller->modelDef != nullptr ? PClass::FindActor(psp->Caller->modelDef) : psp->Caller->GetClass(), psp->GetSprite(), psp->GetFrame(), false) : nullptr; // [BB] No model found for this sprite, so we can't render anything. if (smf == nullptr) @@ -224,11 +224,11 @@ void RenderHUDModel(FModelRenderer *renderer, DPSprite *psp, float ofsX, float o renderer->BeginDrawHUDModel(playermo->RenderStyle, objectToWorldMatrix, orientation < 0); uint32_t trans = psp->GetTranslation() != 0 ? psp->GetTranslation() : 0; if ((psp->Flags & PSPF_PLAYERTRANSLATED)) trans = psp->Owner->mo->Translation; - RenderFrameModels(renderer, playermo->Level, smf, psp->GetState(), psp->GetTics(), psp->Caller->GetClass(), trans); + RenderFrameModels(renderer, playermo->Level, smf, psp->GetState(), psp->GetTics(), psp->Caller->modelDef != nullptr ? PClass::FindActor(psp->Caller->modelDef) : psp->Caller->GetClass(), trans, psp->Caller); renderer->EndDrawHUDModel(playermo->RenderStyle); } -void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, const PClass *ti, int translation) +void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, const PClass *ti, int translation, AActor* actor) { // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. @@ -278,10 +278,12 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr for (int i = 0; i < smf->modelsAmount; i++) { + if (actor->models[i] != -1) + smf->modelIDs[i] = actor->models[i]; if (smf->modelIDs[i] != -1) { FModel * mdl = Models[smf->modelIDs[i]]; - auto tex = smf->skinIDs[i].isValid() ? TexMan.GetGameTexture(smf->skinIDs[i], true) : nullptr; + auto tex = actor->skins[i].isValid() ? TexMan.GetGameTexture(actor->skins[i], true) : smf->skinIDs[i].isValid() ? TexMan.GetGameTexture(smf->skinIDs[i], true) : nullptr; mdl->BuildVertexBuffer(renderer); mdl->PushSpriteMDLFrame(smf, i); diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 6afff0610..003bf3604 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -834,7 +834,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t z += fz; } - modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); + modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing->modelDef != nullptr ? PClass::FindActor(thing->modelDef) : thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); // don't bother drawing sprite shadows if this is a model (it will never look right) if (modelframe && isSpriteShadow) diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 63f0a3f12..e95963cca 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -1132,6 +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, string modelpath = "", name model = "", int modelindex = 0, string skinpath = "", name skin = "", int skinindex = 0, int flags = 0); void A_SetFriendly (bool set) { diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 35b202ed3..ebc317b77 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -365,6 +365,12 @@ enum ERadiusGiveFlags RGF_EITHER = 1 << 17, }; +// Change model flags +enum ChangeModelFlags +{ + CMDL_WEAPONTOPLAYER = 1 +}; + // Activation flags enum EActivationFlags {