diff --git a/src/actor.h b/src/actor.h index 4c0a06297..bc61966d0 100644 --- a/src/actor.h +++ b/src/actor.h @@ -56,6 +56,7 @@ class FLightDefaults; struct FSection; struct FLevelLocals; struct FDynamicLight; + // // NOTES: AActor // @@ -1173,6 +1174,7 @@ public: DRotator PrevAngles; int PrevPortalGroup; TArray AttachedLights; + TDeletingArray UserLights; // When was this actor spawned? int SpawnTime; diff --git a/src/g_shared/a_dynlight.cpp b/src/g_shared/a_dynlight.cpp index edc2e4dfd..6ed47d8d0 100644 --- a/src/g_shared/a_dynlight.cpp +++ b/src/g_shared/a_dynlight.cpp @@ -804,6 +804,144 @@ void AActor::DeleteAttachedLights() AttachedLights.Clear(); } +//========================================================================== +// +// +// +//========================================================================== +extern TDeletingArray LightDefaults; + +unsigned FindUserLight(AActor *self, FName id, bool create = false) +{ + for (unsigned i = 0; i < self->UserLights.Size(); i++ ) + { + if (self->UserLights[i]->GetName() == id) return i; + } + if (create) + { + auto li = new FLightDefaults(id); + return self->UserLights.Push(li); + } + return ~0u; +} + +//========================================================================== +// +// +// +//========================================================================== + +int AttachLightDef(AActor *self, int _lightid, int _lightname) +{ + FName lightid = FName(ENamedName(_lightid)); + FName lightname = FName(ENamedName(_lightname)); + + // Todo: Optimize. This may be too slow. + auto lightdef = LightDefaults.FindEx([=](const auto &a) { + return a->GetName() == lightid; + }); + if (lightdef < LightDefaults.Size()) + { + auto userlight = self->UserLights[FindUserLight(self, lightid, true)]; + userlight->CopyFrom(*LightDefaults[lightdef]); + return 1; + } + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_AttachLightDef, AttachLightDef) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_NAME(lightid); + PARAM_NAME(lightname); + ACTION_RETURN_BOOL(AttachLightDef(self, lightid.GetIndex(), lightname.GetIndex())); +} + +//========================================================================== +// +// +// +//========================================================================== + +int AttachLightDirect(AActor *self, int _lightid, int type, int color, int radius1, int radius2, int flags, double ofs_x, double ofs_y, double ofs_z, double param, double spoti, double spoto, double spotp) +{ + FName lightid = FName(ENamedName(_lightid)); + auto userlight = self->UserLights[FindUserLight(self, lightid, true)]; + userlight->SetType(ELightType(type)); + userlight->SetArg(LIGHT_RED, RPART(color)); + userlight->SetArg(LIGHT_GREEN, GPART(color)); + userlight->SetArg(LIGHT_BLUE, BPART(color)); + userlight->SetArg(LIGHT_INTENSITY, radius1); + userlight->SetArg(LIGHT_SECONDARY_INTENSITY, radius2); + userlight->SetFlags(LightFlags::FromInt(flags)); + float of[] = { float(ofs_x), float(ofs_y), float(ofs_z)}; + userlight->SetOffset(of); + userlight->SetParameter(type == PulseLight? param*TICRATE : param*360.); + userlight->SetSpotInnerAngle(spoti); + userlight->SetSpotOuterAngle(spoto); + if (spotp >= -90. && spotp <= 90.) + { + userlight->SetSpotPitch(spotp); + } + else + { + userlight->UnsetSpotPitch(); + } + + return 1; +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_AttachLight, AttachLightDirect) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_NAME(lightid); + PARAM_INT(type); + PARAM_INT(color); + PARAM_INT(radius1); + PARAM_INT(radius2); + PARAM_INT(flags); + PARAM_FLOAT(ofs_x); + PARAM_FLOAT(ofs_y); + PARAM_FLOAT(ofs_z); + PARAM_FLOAT(parami); + PARAM_FLOAT(spoti); + PARAM_FLOAT(spoto); + PARAM_FLOAT(spotp); + ACTION_RETURN_BOOL(AttachLightDirect(self, lightid, type, color, radius1, radius2, flags, ofs_x, ofs_y, ofs_z, parami, spoti, spoto, spotp)); +} + +//========================================================================== +// +// +// +//========================================================================== + +int RemoveLight(AActor *self, int _lightid) +{ + FName lightid = FName(ENamedName(_lightid)); + auto userlight = FindUserLight(self, lightid, false); + if (userlight < self->UserLights.Size()) + { + delete self->UserLights[userlight]; + self->UserLights.Delete(userlight); + } + return 1; +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_RemoveLight, RemoveLight) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_NAME(lightid); + ACTION_RETURN_BOOL(RemoveLight(self, lightid)); +} + + +//========================================================================== +// +// +// +//========================================================================== + //========================================================================== // // This is called before saving the game diff --git a/src/g_shared/a_dynlight.h b/src/g_shared/a_dynlight.h index 2a681a71d..38f9019bd 100644 --- a/src/g_shared/a_dynlight.h +++ b/src/g_shared/a_dynlight.h @@ -54,7 +54,11 @@ DEFINE_TFLAGS_OPERATORS(LightFlags) class FLightDefaults { public: - FLightDefaults(FName name, ELightType type); + FLightDefaults(FName name, ELightType type = PointLight) + { + m_Name = name; + m_type = type; + } void ApplyProperties(FDynamicLight * light) const; FName GetName() const { return m_Name; } @@ -72,6 +76,28 @@ public: void SetSpot(bool spot) { if (spot) m_lightFlags |= LF_SPOT; else m_lightFlags &= ~LF_SPOT; } void SetSpotInnerAngle(double angle) { m_spotInnerAngle = angle; } void SetSpotOuterAngle(double angle) { m_spotOuterAngle = angle; } + void SetSpotPitch(double pitch) + { + m_pitch = pitch; + m_explicitPitch = true; + } + void UnsetSpotPitch() + { + m_pitch = 0.; + m_explicitPitch = false; + } + void SetType(ELightType type) { m_type = type; } + void CopyFrom(const FLightDefaults &other) + { + auto n = m_Name; + *this = other; + m_Name = n; + } + void SetFlags(LightFlags lf) + { + m_lightFlags = lf; + m_attenuate = !!(m_lightFlags & LF_ATTENUATE); + } static void SetAttenuationForLevel(bool); void OrderIntensities() @@ -88,7 +114,7 @@ protected: int m_Args[5] = { 0,0,0,0,0 }; double m_Param = 0; DVector3 m_Pos = { 0,0,0 }; - ELightType m_type; + int m_type; int8_t m_attenuate = -1; LightFlags m_lightFlags = 0; bool m_swapped = false; @@ -97,8 +123,12 @@ protected: DAngle m_spotInnerAngle = 10.0; DAngle m_spotOuterAngle = 25.0; DAngle m_pitch = 0.0; + + friend FSerializer &Serialize(FSerializer &arc, const char *key, FLightDefaults &value, FLightDefaults *def); }; +FSerializer &Serialize(FSerializer &arc, const char *key, TDeletingArray &value, TDeletingArray *def); + //========================================================================== // // Light associations (intermediate parser data) @@ -247,3 +277,5 @@ public: bool explicitpitch; }; + + diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index decb58b3a..17b221b26 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -186,6 +186,8 @@ AActor::~AActor () // //========================================================================== + + #define A(a,b) ((a), (b), def->b) void AActor::Serialize(FSerializer &arc) @@ -364,7 +366,8 @@ void AActor::Serialize(FSerializer &arc) A("friendlyseeblocks", friendlyseeblocks) A("spawntime", SpawnTime) A("spawnorder", SpawnOrder) - A("friction", Friction); + A("friction", Friction) + A("userlights", UserLights); } #undef A diff --git a/src/r_data/a_dynlightdata.cpp b/src/r_data/a_dynlightdata.cpp index 1d8564a45..f3bcf56c0 100644 --- a/src/r_data/a_dynlightdata.cpp +++ b/src/r_data/a_dynlightdata.cpp @@ -36,6 +36,7 @@ #include "r_state.h" #include "g_levellocals.h" #include "a_dynlight.h" +#include "serializer.h" //========================================================================== @@ -63,12 +64,60 @@ int AttenuationIsSet = -1; // //----------------------------------------------------------------------------- -FLightDefaults::FLightDefaults(FName name, ELightType type) +FSerializer &Serialize(FSerializer &arc, const char *key, FLightDefaults &value, FLightDefaults *def) { - m_Name = name; - m_type = type; + if (arc.BeginObject(key)) + { + arc("name", value.m_Name) + .Array("args", value.m_Args, 5, nullptr) + ("param", value.m_Param) + ("pos", value.m_Pos) + ("type", value.m_type) + ("attenuate", value.m_attenuate) + ("flags", value.m_lightFlags) + ("swapped", value.m_swapped) + ("spot", value.m_spot) + ("explicitpitch", value.m_explicitPitch) + ("spotinner", value.m_spotInnerAngle) + ("spotouter", value.m_spotOuterAngle) + ("pitch", value.m_pitch) + .EndObject(); + } + return arc; } +FSerializer &Serialize(FSerializer &arc, const char *key, TDeletingArray &value, TDeletingArray *def) +{ + if (arc.isWriting()) + { + if (value.Size() == 0) return arc; // do not save empty arrays + } + bool res = arc.BeginArray(key); + if (arc.isReading()) + { + if (!res) + { + value.Clear(); + return arc; + } + value.Resize(arc.ArraySize()); + for (auto &entry : value) entry = new FLightDefaults(NAME_None); + } + for (unsigned i = 0; i < value.Size(); i++) + { + Serialize(arc, nullptr, *value[i], nullptr); + } + arc.EndArray(); + return arc; +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + void FLightDefaults::ApplyProperties(FDynamicLight * light) const { auto oldtype = light->lighttype; diff --git a/src/scripting/vm/vm.h b/src/scripting/vm/vm.h index d8da4d434..49bb51704 100644 --- a/src/scripting/vm/vm.h +++ b/src/scripting/vm/vm.h @@ -593,6 +593,7 @@ struct DirectNativeDesc template DirectNativeDesc(Ret(*func)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11)) : Ptr(reinterpret_cast(func)) { ValidateRet(); VP(1); VP(2); VP(3); VP(4); VP(5); VP(6); VP(7); VP(8); VP(9); VP(10); VP(11); } template DirectNativeDesc(Ret(*func)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12)) : Ptr(reinterpret_cast(func)) { ValidateRet(); VP(1); VP(2); VP(3); VP(4); VP(5); VP(6); VP(7); VP(8); VP(9); VP(10); VP(11); VP(12); } template DirectNativeDesc(Ret(*func)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13)) : Ptr(reinterpret_cast(func)) { ValidateRet(); VP(1); VP(2); VP(3); VP(4); VP(5); VP(6); VP(7); VP(8); VP(9); VP(10); VP(11); VP(12); VP(13); } + template DirectNativeDesc(Ret(*func)(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14)) : Ptr(reinterpret_cast(func)) { ValidateRet(); VP(1); VP(2); VP(3); VP(4); VP(5); VP(6); VP(7); VP(8); VP(9); VP(10); VP(11); VP(12); VP(13), VP(14); } #undef TP #undef VP diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 4fd3d46e3..bbad8174f 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -1162,6 +1162,10 @@ class Actor : Thinker native action native void A_OverlayFlags(int layer, int flags, bool set); action native void A_OverlayAlpha(int layer, double alph); action native void A_OverlayRenderStyle(int layer, int style); + + action native bool A_AttachLightDef(Name lightid, Name lightdef); + action native bool A_AttachLight(Name lightid, int type, Color lightcolor, int radius1, int radius2, int flags, Vector3 ofs, double param, double spoti, double spoto, double spotp); + action mative bool A_RemoveLight(Name lightid); int ACS_NamedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0) {