From 0398ba4ff02faef9bd03ebcc2b9d419cc10b7fc5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 23 Jan 2022 09:54:49 +0100 Subject: [PATCH] - added all parts needed to implement the actor property parser. Not hooked up yet with the rest of the code, this just adds the needed files in compilable form. --- source/CMakeLists.txt | 3 + source/build/include/build.h | 1 - source/core/actorinfo.cpp | 230 ++++++ source/core/actorinfo.h | 48 ++ source/core/coreactor.h | 14 +- source/core/d_net.cpp | 1 - source/core/rendering/scene/hw_drawstructs.h | 2 +- source/core/thingdef.h | 238 ++++++ source/core/thingdef_data.cpp | 372 ++++++++++ source/core/zcc_compile_raze.cpp | 728 +++++++++++++++++++ source/core/zcc_compile_raze.h | 32 + source/games/duke/src/constants.h | 1 + 12 files changed, 1666 insertions(+), 4 deletions(-) create mode 100644 source/core/actorinfo.cpp create mode 100644 source/core/actorinfo.h create mode 100644 source/core/thingdef.h create mode 100644 source/core/thingdef_data.cpp create mode 100644 source/core/zcc_compile_raze.cpp create mode 100644 source/core/zcc_compile_raze.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b888c6bbd..75d8cb155 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1012,6 +1012,9 @@ set (PCH_SOURCES build/src/mdsprite.cpp build/src/polymost.cpp + core/actorinfo.cpp + core/zcc_compile_raze.cpp + core/thingdef_data.cpp core/actorlist.cpp core/automap.cpp core/cheats.cpp diff --git a/source/build/include/build.h b/source/build/include/build.h index 45e15efe9..f1cbbc0ba 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -43,7 +43,6 @@ enum MAXWALLSB = 6144, MAXVOXELS = 1024, - MAXSTATUS = 1024, // Maximum number of component tiles in a multi-psky: MAXSPRITESONSCREEN = 4096, MAXUNIQHUDID = 256, //Extra slots so HUD models can store animation state without messing game sprites diff --git a/source/core/actorinfo.cpp b/source/core/actorinfo.cpp new file mode 100644 index 000000000..4c686fd11 --- /dev/null +++ b/source/core/actorinfo.cpp @@ -0,0 +1,230 @@ +/* +** actorinfo.cpp +** Keeps track of available actors and their states +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** Copyright 2005-2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +#include "actorinfo.h" +#include "c_dispatch.h" +#include "d_net.h" +#include "v_text.h" + +#include "gi.h" +#include "coreactor.h" +#include "stats.h" +#include "types.h" +#include "filesystem.h" +#include "texturemanager.h" + +extern void LoadActors (); + +cycle_t ActionCycles; + + +//========================================================================== +// +// special type for the native ActorInfo. This allows to let this struct +// be handled by the generic object constructors for the VM. +// +//========================================================================== + +class PActorInfo : public PCompoundType +{ +public: + PActorInfo() + :PCompoundType(sizeof(FActorInfo), alignof(FActorInfo)) + { + } + + void SetDefaultValue(void *base, unsigned offset, TArray *special) override + { + if (base != nullptr) new((uint8_t *)base + offset) FActorInfo; + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } + } + + void InitializeValue(void *addr, const void *def) const override + { + new(addr) FActorInfo; + } + + void DestroyValue(void *addr) const override + { + FActorInfo *self = (FActorInfo*)addr; + self->~FActorInfo(); + } + +}; + +void AddActorInfo(PClass *cls) +{ + auto type = new PActorInfo; + TypeTable.AddType(type, NAME_Actor); + cls->AddField("*", type, VARF_Meta); +} + + +//========================================================================== +// +// PClassActor :: StaticInit STATIC +// +//========================================================================== + +void PClassActor::StaticInit() +{ + for (auto cls : AllClasses) + { + if (cls->IsDescendantOf(RUNTIME_CLASS(DCoreActor))) + { + AllActorClasses.Push(static_cast(cls)); + } + } +} + +//========================================================================== +// +// PClassActor :: SetReplacement +// +// Sets as a replacement class for another class. +// +//========================================================================== + +bool PClassActor::SetReplacement(FName replaceName) +{ + // Check for "replaces" + if (replaceName != NAME_None) + { + // Get actor name + PClassActor *replacee = PClass::FindActor(replaceName); + + if (replacee == nullptr) + { + return false; + } + if (replacee != nullptr) + { + replacee->ActorInfo()->Replacement = this; + ActorInfo()->Replacee = replacee; + } + } + return true; +} + +//========================================================================== +// +// PClassActor :: InitializeNativeDefaults +// +//========================================================================== + +void PClassActor::InitializeDefaults() +{ + if (IsDescendantOf(RUNTIME_CLASS(DCoreActor))) + { + assert(Defaults == nullptr); + Defaults = (uint8_t*)M_Malloc(Size); + + ConstructNative(Defaults); + // We must unlink the defaults from the class list because it's just a static block of data to the engine. + DObject* optr = (DObject*)Defaults; + GC::Root = optr->ObjNext; + optr->ObjNext = nullptr; + optr->SetClass(this); + + // Copy the defaults from the parent but leave the DObject part alone because it contains important data. + if (ParentClass->Defaults != nullptr) + { + memcpy(Defaults + sizeof(DObject), ParentClass->Defaults + sizeof(DObject), ParentClass->Size - sizeof(DObject)); + if (Size > ParentClass->Size) + { + memset(Defaults + ParentClass->Size, 0, Size - ParentClass->Size); + } + } + else + { + memset(Defaults + sizeof(DObject), 0, Size - sizeof(DObject)); + } + + assert(MetaSize >= ParentClass->MetaSize); + if (MetaSize != 0) + { + Meta = (uint8_t*)M_Malloc(MetaSize); + + // Copy the defaults from the parent but leave the DObject part alone because it contains important data. + if (ParentClass->Meta != nullptr) + { + memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize); + if (MetaSize > ParentClass->MetaSize) + { + memset(Meta + ParentClass->MetaSize, 0, MetaSize - ParentClass->MetaSize); + } + } + else + { + memset(Meta, 0, MetaSize); + } + + if (MetaSize > 0) memcpy(Meta, ParentClass->Meta, ParentClass->MetaSize); + else memset(Meta, 0, MetaSize); + } + } + PClass::InitializeDefaults(); +} + +//========================================================================== +// +// PClassActor :: GetReplacement +// +//========================================================================== + +PClassActor *PClassActor::GetReplacement() +{ + PClassActor *Replacement = ActorInfo()->Replacement; + if (Replacement == nullptr) return this; + return Replacement; +} + +//========================================================================== +// +// PClassActor :: GetReplacee +// +//========================================================================== + +PClassActor *PClassActor::GetReplacee() +{ + PClassActor *Replacee = ActorInfo()->Replacee; + if (Replacee == nullptr) return this; + return Replacee; +} + diff --git a/source/core/actorinfo.h b/source/core/actorinfo.h new file mode 100644 index 000000000..c0e38c32d --- /dev/null +++ b/source/core/actorinfo.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include "dobject.h" +#include "m_fixed.h" +#include "m_random.h" + +class FScanner; +class FInternalLightAssociation; + +struct FActorInfo +{ + TArray LightAssociations; + PClassActor *Replacement = nullptr; + PClassActor *Replacee = nullptr; + int TypeNum = -1; + + FActorInfo() = default; + FActorInfo(const FActorInfo & other) = delete; +}; + +// No objects of this type will be created ever - its only use is to static_cast +// PClass to it. +class PClassActor : public PClass +{ +protected: +public: + static void StaticInit (); + + void BuildDefaults(); + void ApplyDefaults(uint8_t *defaults); + bool SetReplacement(FName replaceName); + void InitializeDefaults(); + + FActorInfo *ActorInfo() const + { + return (FActorInfo*)Meta; + } + + PClassActor *GetReplacement(); + PClassActor *GetReplacee(); + + // For those times when being able to scan every kind of actor is convenient + inline static TArray AllActorClasses; +}; + diff --git a/source/core/coreactor.h b/source/core/coreactor.h index e5dd442f0..2c2c33319 100644 --- a/source/core/coreactor.h +++ b/source/core/coreactor.h @@ -2,6 +2,13 @@ #include #include "maptypes.h" +#include "build.h" +#include "actorinfo.h" + +enum +{ + MAXSTATUS = 1024 +}; class DCoreActor : public DObject { @@ -292,7 +299,6 @@ struct ActorStatList extern ActorStatList statList[MAXSTATUS]; -// Iterator wrappers that return an actor pointer, not an index. template class TStatIterator { @@ -432,3 +438,9 @@ inline void validateTSpriteSize(tspritetype*& tsprite, int& spritesortcnt) { } + +inline PClassActor* PClass::FindActor(FName name) +{ + auto cls = FindClass(name); + return cls && cls->IsDescendantOf(RUNTIME_CLASS(DCoreActor)) ? static_cast(cls) : nullptr; +} diff --git a/source/core/d_net.cpp b/source/core/d_net.cpp index f66dc49d7..ed21ad603 100644 --- a/source/core/d_net.cpp +++ b/source/core/d_net.cpp @@ -143,7 +143,6 @@ void G_BuildTiccmd (ticcmd_t *cmd); void D_DoAdvanceDemo (void); static void SendSetup (uint32_t playersdetected[MAXNETNODES], uint8_t gotsetup[MAXNETNODES], int len); -static void RunScript(uint8_t **stream, AActor *pawn, int snum, int argn, int always); int reboundpacket; uint8_t reboundstore[MAX_MSGLEN]; diff --git a/source/core/rendering/scene/hw_drawstructs.h b/source/core/rendering/scene/hw_drawstructs.h index 51ea75c49..e250d6cf8 100644 --- a/source/core/rendering/scene/hw_drawstructs.h +++ b/source/core/rendering/scene/hw_drawstructs.h @@ -366,7 +366,7 @@ inline float Dist2(float x1,float y1,float x2,float y2) return sqrtf((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } -void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata); +//void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata); extern const float LARGE_VALUE; diff --git a/source/core/thingdef.h b/source/core/thingdef.h new file mode 100644 index 000000000..7f551b67c --- /dev/null +++ b/source/core/thingdef.h @@ -0,0 +1,238 @@ +/* +** thingdef.h +** +** Actor definitions +** +**--------------------------------------------------------------------------- +** Copyright 2002-2008 Christoph Oelckers +** Copyright 2004-2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +#ifndef __THINGDEF_H +#define __THINGDEF_H + +#include "sc_man.h" +#include "cmdlib.h" +#include "vm.h" +#include "dobject.h" +#include "coreactor.h" + + +class FScanner; + + +//========================================================================== +// +// A flag descriptor +// +//========================================================================== + +struct FFlagDef +{ + unsigned int flagbit; + const char *name; + int structoffset; + int fieldsize; + int varflags; +}; + +#if 0 +void FinalizeClass(PClass *cls); +FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict = false); +void HandleDeprecatedFlags(DCoreActor *defaults, PClassActor *info, bool set, int index); +bool CheckDeprecatedFlags(DCoreActor *actor, PClassActor *info, int index); +const char *GetFlagName(unsigned int flagnum, int flagoffset); +void ModActorFlag(DCoreActor *actor, FFlagDef *fd, bool set); +bool ModActorFlag(DCoreActor *actor, const FString &flagname, bool set, bool printerror = true); +INTBOOL CheckActorFlag(DCoreActor *actor, FFlagDef *fd); +INTBOOL CheckActorFlag(DCoreActor *owner, const char *flagname, bool printerror = true); +#endif + +#define FLAG_NAME(flagnum, flagvar) GetFlagName(flagnum, myoffsetof(DCoreActor, flagvar)) + + +//========================================================================== +// +// State parser +// +//========================================================================== +class FxExpression; + +//========================================================================== +// +// Extra info maintained while defining an actor. +// +//========================================================================== +struct FDropItem; + +struct Baggage +{ + PNamespace *Namespace; + PClassActor *Info; + int Lumpnum; + VersionInfo Version; + + FScriptPosition ScriptPosition; +}; + +inline void ResetBaggage (Baggage *bag, PClassActor *stateclass) +{ +} + +//========================================================================== +// +// Property parser +// +//========================================================================== + +void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod); +FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type); + + +enum +{ + DEPF_UNUSED = 0, + DEPF_FIREDAMAGE = 1, + DEPF_ICEDAMAGE = 2, + DEPF_LOWGRAVITY = 3, + DEPF_LONGMELEERANGE = 4, + DEPF_SHORTMISSILERANGE = 5, + DEPF_PICKUPFLASH = 6, + DEPF_QUARTERGRAVITY = 7, + DEPF_FIRERESIST = 8, + DEPF_HERETICBOUNCE = 9, + DEPF_HEXENBOUNCE = 10, + DEPF_DOOMBOUNCE = 11, + DEPF_INTERHUBSTRIP = 12, + DEPF_HIGHERMPROB = 13, +}; + +// Types of old style decorations +enum EDefinitionType +{ + DEF_Decoration, + DEF_BreakableDecoration, + DEF_Pickup, + DEF_Projectile, +}; + +#if defined(_MSC_VER) +#pragma section(SECTION_GREG,read) + +#define MSVC_PSEG __declspec(allocate(SECTION_GREG)) +#define GCC_PSEG +#else +#define MSVC_PSEG +#define GCC_PSEG __attribute__((section(SECTION_GREG))) __attribute__((used)) +#endif + + +union FPropParam +{ + int i; + double d; + const char *s; + FxExpression *exp; +}; + +typedef void (*PropHandler)(DCoreActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params); + +enum ECategory +{ + CAT_PROPERTY, // Inheritable property + CAT_INFO // non-inheritable info (spawn ID, Doomednum, game filter, conversation ID, not usable in ZScript) +}; + +struct FPropertyInfo +{ + const char *name; + const char *params; + const char *clsname; + PropHandler Handler; + int category; +}; + +FPropertyInfo *FindProperty(const char * string); +int MatchString (const char *in, const char **strings); + + +#define DEFINE_PROPERTY_BASE(name, paramlist, clas, cat) \ + static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \ + static FPropertyInfo Prop_##name##_##paramlist##_##clas = \ + { #name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ + MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \ + static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params) + +#define DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \ + static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \ + static FPropertyInfo Prop_##name##_##paramlist##_##clas = \ +{ #prefix"."#name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ + MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \ + static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params) + +#define DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \ + static void Handler_##name##_##paramlist##_##clas(DCoreActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \ + static FPropertyInfo Prop_##name##_##paramlist##_##clas = \ +{ #prefix"."#name, #paramlist, #clas, (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \ + MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \ + static void Handler_##name##_##paramlist##_##clas(DCoreActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params) + + +#define DEFINE_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_PROPERTY) +#define DEFINE_INFO_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_INFO) + +#define DEFINE_CLASS_PROPERTY(name, paramlist, clas) DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(clas, name, paramlist, clas, CAT_PROPERTY) +#define DEFINE_CLASS_PROPERTY_PREFIX(prefix, name, paramlist, clas) DEFINE_PREFIXED_SCRIPTED_PROPERTY_BASE(prefix, name, paramlist, clas, CAT_PROPERTY) + +#define PROP_PARM_COUNT (params[0].i) + +#define PROP_STRING_PARM(var, no) \ + const char *var = params[(no)+1].s; + +#define PROP_EXP_PARM(var, no) \ + FxExpression *var = params[(no)+1].exp; + +#define PROP_INT_PARM(var, no) \ + int var = params[(no)+1].i; + +#define PROP_FLOAT_PARM(var, no) \ + float var = float(params[(no)+1].d); + +#define PROP_DOUBLE_PARM(var, no) \ + double var = params[(no)+1].d; + +#define PROP_COLOR_PARM(var, no, scriptpos) \ + int var = params[(no)+1].i== 0? params[(no)+2].i : V_GetColor(params[(no)+2].s, scriptpos); + +#endif diff --git a/source/core/thingdef_data.cpp b/source/core/thingdef_data.cpp new file mode 100644 index 000000000..0d0bc6808 --- /dev/null +++ b/source/core/thingdef_data.cpp @@ -0,0 +1,372 @@ +/* +** thingdef_data.cpp +** +** DECORATE data tables +** +**--------------------------------------------------------------------------- +** Copyright 2002-2008 Christoph Oelckers +** Copyright 2004-2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "thingdef.h" +#include "gi.h" +#include "gstrings.h" +#include "v_font.h" +#include "menu.h" +#include "types.h" +#include "dictionary.h" +#include "savegamehelp.h" + +static TArray properties; +static TArray AFTable; +static TArray FieldTable; +extern int BackbuttonTime; +extern float BackbuttonAlpha; + +//========================================================================== +// +// List of all flags +// +//========================================================================== + +// [RH] Keep GCC quiet by not using offsetof on Actor types. +#define DEFINE_FLAG(prefix, name, type, variable) { (unsigned int)prefix##_##name, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native } +#define DEFINE_PROTECTED_FLAG(prefix, name, type, variable) { (unsigned int)prefix##_##name, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native|VARF_ReadOnly|VARF_InternalAccess } +#define DEFINE_FLAG2(symbol, name, type, variable) { (unsigned int)symbol, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native } +#define DEFINE_FLAG2_DEPRECATED(symbol, name, type, variable) { (unsigned int)symbol, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable), VARF_Native|VARF_Deprecated } +#define DEFINE_DEPRECATED_FLAG(name) { DEPF_##name, #name, -1, 0, true } +#define DEFINE_DUMMY_FLAG(name, deprec) { DEPF_UNUSED, #name, -1, 0, deprec? VARF_Deprecated:0 } + +// internal flags. These do not get exposed to actor definitions but scripts need to be able to access them as variables. +static FFlagDef InternalActorFlagDefs[]= +{ + DEFINE_FLAG2(CSTAT2_SPRITE_NOFIND, BLOCK, DCoreActor, spr.cstat2) + // todo +}; + + +static FFlagDef ActorFlagDefs[]= +{ + DEFINE_FLAG2(CSTAT_SPRITE_BLOCK, BLOCK, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT_SPRITE_TRANSLUCENT, TRANSLUCENT, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT_SPRITE_XFLIP, XFLIP, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT_SPRITE_YFLIP, YFLIP, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT_SPRITE_ONE_SIDE, ONE_SIDE, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT_SPRITE_YCENTER, YCENTER, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT_SPRITE_BLOCK_HITSCAN, BLOCK_HITSCAN, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT_SPRITE_INVISIBLE, INVISIBLE, DCoreActor, spr.cstat), + DEFINE_FLAG2(CSTAT2_SPRITE_MAPPED, MAPPED, DCoreActor, spr.cstat2), + DEFINE_FLAG2(CSTAT2_SPRITE_NOSHADOW, NOSHADOW, DCoreActor, spr.cstat2), + DEFINE_FLAG2(CSTAT2_SPRITE_DECAL, DECAL, DCoreActor, spr.cstat2), + // todo: game specific flags +}; + + +static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int NumDefs; int Use; } FlagLists[] = +{ + { &RUNTIME_CLASS_CASTLESS(DCoreActor), ActorFlagDefs, countof(ActorFlagDefs), 3 }, // -1 to account for the terminator + { &RUNTIME_CLASS_CASTLESS(DCoreActor), InternalActorFlagDefs, countof(InternalActorFlagDefs), 2 }, +}; +#define NUM_FLAG_LISTS (countof(FlagLists)) + +static FFlagDef forInternalFlags; + +//========================================================================== +// +// Find a flag by name using a binary search +// +//========================================================================== +static FFlagDef *FindFlag (FFlagDef *flags, int numflags, const char *flag) +{ + int min = 0, max = numflags - 1; + + while (min <= max) + { + int mid = (min + max) / 2; + int lexval = stricmp (flag, flags[mid].name); + if (lexval == 0) + { + return &flags[mid]; + } + else if (lexval > 0) + { + min = mid + 1; + } + else + { + max = mid - 1; + } + } + return NULL; +} + +//========================================================================== +// +// Finds a flag that may have a qualified name +// +//========================================================================== + +FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict) +{ + + if (part2 == nullptr) + { + FStringf internalname("@flagdef@%s", part1); + FName name(internalname, true); + if (name != NAME_None) + { + auto field = dyn_cast(type->FindSymbol(name, true)); + if (field != nullptr && (!strict || !field->decorateOnly)) + { + forInternalFlags.fieldsize = 4; + forInternalFlags.name = ""; + forInternalFlags.flagbit = field->Offset? 1 << field->bitval : field->bitval; + forInternalFlags.structoffset = field->Offset? (int)field->Offset->Offset : -1; + forInternalFlags.varflags = field->Offset == nullptr && field->bitval > 0? VARF_Deprecated : 0; + return &forInternalFlags; + } + } + } + else + { + FStringf internalname("@flagdef@%s.%s", part1, part2); + FName name(internalname, true); + if (name != NAME_None) + { + auto field = dyn_cast(type->FindSymbol(name, true)); + if (field != nullptr) + { + forInternalFlags.fieldsize = 4; + forInternalFlags.name = ""; + forInternalFlags.flagbit = field->Offset ? 1 << field->bitval : field->bitval; + forInternalFlags.structoffset = field->Offset ? (int)field->Offset->Offset : -1; + forInternalFlags.varflags = field->Offset == nullptr && field->bitval > 0? VARF_Deprecated : 0; + return &forInternalFlags; + } + } + } + + // Not found. Try the internal flag definitions. + + + FFlagDef *def; + + if (part2 == NULL) + { // Search all lists + int max = strict ? 2 : NUM_FLAG_LISTS; + for (int i = 0; i < max; ++i) + { + if ((FlagLists[i].Use & 1) && type->IsDescendantOf (*FlagLists[i].Type)) + { + def = FindFlag (FlagLists[i].Defs, FlagLists[i].NumDefs, part1); + if (def != NULL) + { + return def; + } + } + } + } + else + { // Search just the named list + for (size_t i = 0; i < NUM_FLAG_LISTS; ++i) + { + if (stricmp ((*FlagLists[i].Type)->TypeName.GetChars(), part1) == 0) + { + if (type->IsDescendantOf (*FlagLists[i].Type)) + { + return FindFlag (FlagLists[i].Defs, FlagLists[i].NumDefs, part2); + } + else + { + return NULL; + } + } + } + } + + return NULL; +} + + +//========================================================================== +// +// Gets the name of an actor flag +// +//========================================================================== + +const char *GetFlagName(unsigned int flagnum, int flagoffset) +{ + for(size_t i = 0; i < countof(ActorFlagDefs); i++) + { + if (ActorFlagDefs[i].flagbit == flagnum && ActorFlagDefs[i].structoffset == flagoffset) + { + return ActorFlagDefs[i].name; + } + } + return "(unknown)"; // return something printable +} + +//========================================================================== +// +// Find a property by name using a binary search +// +//========================================================================== + +FPropertyInfo *FindProperty(const char * string) +{ + int min = 0, max = properties.Size()-1; + + while (min <= max) + { + int mid = (min + max) / 2; + int lexval = stricmp (string, properties[mid]->name); + if (lexval == 0) + { + return properties[mid]; + } + else if (lexval > 0) + { + min = mid + 1; + } + else + { + max = mid - 1; + } + } + return NULL; +} + +//========================================================================== +// +// Sorting helpers +// +//========================================================================== + +static int flagcmp (const void * a, const void * b) +{ + return stricmp( ((FFlagDef*)a)->name, ((FFlagDef*)b)->name); +} + +static int propcmp(const void * a, const void * b) +{ + return stricmp( (*(FPropertyInfo**)a)->name, (*(FPropertyInfo**)b)->name); +} + +//========================================================================== +// +// Initialization +// +//========================================================================== +void InitImports(); + +void InitThingdef() +{ + // Some native types need size and serialization information added before the scripts get compiled. + //auto secplanestruct = NewStruct("Secplane", nullptr, true); + //secplanestruct->Size = sizeof(secplane_t); + //secplanestruct->Align = alignof(secplane_t); + + auto sectorstruct = NewStruct("Sector", nullptr, true); + sectorstruct->Size = sizeof(sectortype); + sectorstruct->Align = alignof(sectortype); + NewPointer(sectorstruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(sectortype **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(sectortype **)addr, nullptr); + return true; + } + ); + + auto linestruct = NewStruct("Wall", nullptr, true); + linestruct->Size = sizeof(walltype); + linestruct->Align = alignof(walltype); + NewPointer(linestruct, false)->InstallHandlers( + [](FSerializer &ar, const char *key, const void *addr) + { + ar(key, *(walltype **)addr); + }, + [](FSerializer &ar, const char *key, void *addr) + { + Serialize(ar, key, *(walltype **)addr, nullptr); + return true; + } + ); + + auto sidestruct = NewStruct("TSprite", nullptr, true); + sidestruct->Size = sizeof(tspritetype); + sidestruct->Align = alignof(tspritetype); + // This may not be serialized + + // Sort the flag lists + for (size_t i = 0; i < NUM_FLAG_LISTS; ++i) + { + qsort (FlagLists[i].Defs, FlagLists[i].NumDefs, sizeof(FFlagDef), flagcmp); + } + + // Create a sorted list of properties + if (properties.Size() == 0) + { + AutoSegs::Properties.ForEach([](FPropertyInfo* propertyInfo) + { + properties.Push(propertyInfo); + }); + + properties.ShrinkToFit(); + qsort(&properties[0], properties.Size(), sizeof(properties[0]), propcmp); + } + + InitImports(); +} + +void SynthesizeFlagFields() +{ + // synthesize a symbol for each flag from the flag name tables to avoid redundant declaration of them. + for (auto &fl : FlagLists) + { + auto cls = const_cast(*fl.Type); + if (fl.Use & 2) + { + for (int i = 0; i < fl.NumDefs; i++) + { + if (fl.Defs[i].structoffset > 0) // skip the deprecated entries in this list + { + cls->VMType->AddNativeField(FStringf("b%s", fl.Defs[i].name), (fl.Defs[i].fieldsize == 4 ? TypeSInt32 : TypeSInt16), fl.Defs[i].structoffset, fl.Defs[i].varflags, fl.Defs[i].flagbit); + } + } + } + } +} diff --git a/source/core/zcc_compile_raze.cpp b/source/core/zcc_compile_raze.cpp new file mode 100644 index 000000000..18a14d89d --- /dev/null +++ b/source/core/zcc_compile_raze.cpp @@ -0,0 +1,728 @@ +/* +** zcc_compile_raze.cpp +** +** contains the Raze specific parts of the script parser, i.e. +** actor property definitions and associated content. +** +**--------------------------------------------------------------------------- +** Copyright 2016-2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "coreactor.h" +#include "c_console.h" +#include "filesystem.h" +#include "zcc_parser.h" +#include "zcc-parse.h" +#include "zcc_compile_raze.h" +#include "v_text.h" +#include "v_video.h" +#include "actorinfo.h" +#include "thingdef.h" + + +bool isActor(PContainerType *type); +void AddActorInfo(PClass *cls); +int GetIntConst(FxExpression* ex, FCompileContext& ctx); +double GetFloatConst(FxExpression* ex, FCompileContext& ctx); + +//========================================================================== +// +// ZCCCompiler :: Compile +// +// Compile everything defined at this level. +// +//========================================================================== + +int ZCCRazeCompiler::Compile() +{ + CreateClassTypes(); + CreateStructTypes(); + CompileAllConstants(); + CompileAllFields(); + CompileAllProperties(); + InitDefaults(); + InitFunctions(); + return FScriptPosition::ErrorCounter; +} + + +//========================================================================== +// +// ZCCCompiler :: CompileAllProperties +// +// builds the property lists of all actor classes +// +//========================================================================== + +void ZCCRazeCompiler::CompileAllProperties() +{ + for (auto c : Classes) + { + if (c->Properties.Size() > 0) + CompileProperties(c->ClassType(), c->Properties, c->Type()->TypeName); + + if (c->FlagDefs.Size() > 0) + CompileFlagDefs(c->ClassType(), c->FlagDefs, c->Type()->TypeName); + + } +} + +//========================================================================== +// +// ZCCCompiler :: CompileProperties +// +// builds the internal structure of a single class or struct +// +//========================================================================== + +bool ZCCRazeCompiler::CompileProperties(PClass *type, TArray &Properties, FName prefix) +{ + if (!type->IsDescendantOf(RUNTIME_CLASS(DCoreActor))) + { + Error(Properties[0], "Properties can only be defined for actors"); + return false; + } + for(auto p : Properties) + { + TArray fields; + ZCC_Identifier *id = (ZCC_Identifier *)p->Body; + + if (FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0) + { + // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. + prefix = id->Id; + } + else + { + do + { + auto f = dyn_cast(type->FindSymbol(id->Id, true)); + if (f == nullptr) + { + Error(id, "Variable %s not found in %s", FName(id->Id).GetChars(), type->TypeName.GetChars()); + } + fields.Push(f); + id = (ZCC_Identifier*)id->SiblingNext; + } while (id != p->Body); + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + if (prefix == NAME_None) qualifiedname.Format("@property@%s", name.GetChars()); + else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), name.GetChars()); + + fields.ShrinkToFit(); + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, fields))) + { + Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } + } + } + return true; +} + +//========================================================================== +// +// ZCCCompiler :: CompileProperties +// +// builds the internal structure of a single class or struct +// +//========================================================================== + +bool ZCCRazeCompiler::CompileFlagDefs(PClass *type, TArray &Properties, FName prefix) +{ + if (!type->IsDescendantOf(RUNTIME_CLASS(DCoreActor))) + { + Error(Properties[0], "Flags can only be defined for actors"); + return false; + } + for (auto p : Properties) + { + PField *field; + FName referenced = FName(p->RefName); + + if (FName(p->NodeName) == FName("prefix") && fileSystem.GetFileContainer(Lump) == 0) + { + // only for internal definitions: Allow setting a prefix. This is only for compatiblity with the old DECORATE property parser, but not for general use. + prefix = referenced; + } + else + { + if (referenced != NAME_None) + { + field = dyn_cast(type->FindSymbol(referenced, true)); + if (field == nullptr) + { + Error(p, "Variable %s not found in %s", referenced.GetChars(), type->TypeName.GetChars()); + } + else if (!field->Type->isInt() || field->Type->Size != 4) + { + Error(p, "Variable %s in %s must have a size of 4 bytes for use as flag storage", referenced.GetChars(), type->TypeName.GetChars()); + } + } + else field = nullptr; + + + FString qualifiedname; + // Store the full qualified name and prepend some 'garbage' to the name so that no conflicts with other symbol types can happen. + // All these will be removed from the symbol table after the compiler finishes to free up the allocated space. + FName name = FName(p->NodeName); + for (int i = 0; i < 2; i++) + { + if (i == 0) qualifiedname.Format("@flagdef@%s", name.GetChars()); + else + { + if (prefix == NAME_None) continue; + qualifiedname.Format("@flagdef@%s.%s", prefix.GetChars(), name.GetChars()); + } + + if (!type->VMType->Symbols.AddSymbol(Create(qualifiedname, field, p->BitValue, i == 0 && prefix != NAME_None))) + { + Error(p, "Unable to add flag definition %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } + } + + if (field != nullptr) + type->VMType->AddNativeField(FStringf("b%s", name.GetChars()), TypeSInt32, field->Offset, 0, 1 << p->BitValue); + } + } + return true; +} + +//========================================================================== +// +// Parses an actor property's parameters and calls the handler +// +//========================================================================== + +void ZCCRazeCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *property, DCoreActor *defaults, Baggage &bag) +{ + static TArray params; + static TArray strings; + + params.Clear(); + strings.Clear(); + params.Reserve(1); + params[0].i = 0; + if (prop->params[0] != '0') + { + if (property->Values == nullptr) + { + Error(property, "%s: arguments missing", prop->name); + return; + } + const char * p = prop->params; + auto exp = property->Values; + + FCompileContext ctx(OutNamespace, bag.Info->VMType, false); + while (true) + { + FPropParam conv; + FPropParam pref; + + FxExpression *ex = ConvertNode(exp); + ex = ex->Resolve(ctx); + if (ex == nullptr) + { + return; + } + else if (!ex->isConstant()) + { + // If we get TypeError, there has already been a message from deeper down so do not print another one. + if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->name); + return; + } + conv.s = nullptr; + pref.s = nullptr; + pref.i = -1; + switch ((*p) & 223) + { + + case 'X': // Expression in parentheses or number. We only support the constant here. The function will have to be handled by a separate property to get past the parser. + conv.i = GetIntConst(ex, ctx); + params.Push(conv); + conv.exp = nullptr; + break; + + case 'I': + case 'M': // special case for morph styles in DECORATE . This expression-aware parser will not need this. + case 'N': // special case for thing activations in DECORATE. This expression-aware parser will not need this. + conv.i = GetIntConst(ex, ctx); + break; + + case 'F': + conv.d = GetFloatConst(ex, ctx); + break; + + case 'Z': // an optional string. Does not allow any numeric value. + if (ex->ValueType != TypeString) + { + // apply this expression to the next argument on the list. + params.Push(conv); + params[0].i++; + p++; + continue; + } + conv.s = GetStringConst(ex, ctx); + break; + + case 'C': // this parser accepts colors only in string form. + pref.i = 1; + [[fallthrough]]; + case 'S': + case 'T': // a filtered string (ZScript only parses filtered strings so there's nothing to do here.) + conv.s = GetStringConst(ex, ctx); + break; + + case 'L': // Either a number or a list of strings + if (ex->ValueType != TypeString) + { + pref.i = 0; + conv.i = GetIntConst(ex, ctx); + } + else + { + pref.i = 1; + params.Push(pref); + params[0].i++; + + do + { + conv.s = GetStringConst(ex, ctx); + if (conv.s != nullptr) + { + params.Push(conv); + params[0].i++; + } + exp = static_cast(exp->SiblingNext); + if (exp != property->Values) + { + ex = ConvertNode(exp); + ex = ex->Resolve(ctx); + if (ex == nullptr) return; + } + } while (exp != property->Values); + goto endofparm; + } + break; + + default: + assert(false); + break; + + } + if (pref.i != -1) + { + params.Push(pref); + params[0].i++; + } + params.Push(conv); + params[0].i++; + exp = static_cast(exp->SiblingNext); + endofparm: + p++; + // Skip the DECORATE 'no comma' marker + if (*p == '_') p++; + + if (*p == 0) + { + if (exp != property->Values) + { + Error(property, "Too many values for '%s'", prop->name); + return; + } + break; + } + else if (exp == property->Values) + { + if (*p < 'a') + { + Error(property, "Insufficient parameters for %s", prop->name); + return; + } + break; + } + } + } + // call the handler + try + { + prop->Handler(defaults, bag.Info, bag, ¶ms[0]); + } + catch (CRecoverableError &error) + { + Error(property, "%s", error.GetMessage()); + } +} + + +//========================================================================== +// +// Parses an actor property's parameters and calls the handler +// +//========================================================================== + +void ZCCRazeCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, DCoreActor *defaults, Baggage &bag) +{ + ZCC_ExprConstant one; + unsigned parmcount = 1; + ZCC_TreeNode *x = property->Values; + if (x == nullptr) + { + parmcount = 0; + } + else + { + while (x->SiblingNext != property->Values) + { + x = x->SiblingNext; + parmcount++; + } + } + if (parmcount == 0 && prop->Variables.Size() == 1 && prop->Variables[0]->Type == TypeBool) + { + // allow boolean properties to have the parameter omitted + memset(&one, 0, sizeof(one)); + one.SourceName = property->SourceName; // This may not be null! + one.Operation = PEX_ConstValue; + one.NodeType = AST_ExprConstant; + one.Type = TypeBool; + one.IntVal = 1; + property->Values = &one; + } + else if (parmcount != prop->Variables.Size()) + { + Error(x == nullptr? property : x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size()); + return; + } + + auto exp = property->Values; + FCompileContext ctx(OutNamespace, bag.Info->VMType, false); + for (auto f : prop->Variables) + { + void *addr; + if (f == nullptr) + { + // This variable was missing. The error had been reported for the property itself already. + return; + } + + if (f->Flags & VARF_Meta) + { + addr = ((char*)bag.Info->Meta) + f->Offset; + } + else + { + addr = ((char*)defaults) + f->Offset; + } + + FxExpression *ex = ConvertNode(exp); + ex = ex->Resolve(ctx); + if (ex == nullptr) + { + return; + } + else if (!ex->isConstant()) + { + // If we get TypeError, there has already been a message from deeper down so do not print another one. + if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->SymbolName.GetChars()); + return; + } + + if (f->Type == TypeBool) + { + static_cast(f->Type)->SetValue(addr, !!GetIntConst(ex, ctx)); + } + else if (f->Type == TypeName) + { + *(FName*)addr = GetStringConst(ex, ctx); + } + else if (f->Type == TypeSound) + { + *(FSoundID*)addr = GetStringConst(ex, ctx); + } + else if (f->Type == TypeColor && ex->ValueType == TypeString) // colors can also be specified as ints. + { + *(PalEntry*)addr = V_GetColor(GetStringConst(ex, ctx), &ex->ScriptPosition); + } + else if (f->Type->isIntCompatible()) + { + static_cast(f->Type)->SetValue(addr, GetIntConst(ex, ctx)); + } + else if (f->Type->isFloat()) + { + static_cast(f->Type)->SetValue(addr, GetFloatConst(ex, ctx)); + } + else if (f->Type == TypeString) + { + *(FString*)addr = GetStringConst(ex, ctx); + } + else if (f->Type->isClassPointer()) + { + auto clsname = GetStringConst(ex, ctx); + if (*clsname == 0 || !stricmp(clsname, "none")) + { + *(PClass**)addr = nullptr; + } + else + { + auto cls = PClass::FindClass(clsname); + auto cp = static_cast(f->Type); + if (cls == nullptr) + { + cls = cp->ClassRestriction->FindClassTentative(clsname); + } + else if (!cls->IsDescendantOf(cp->ClassRestriction)) + { + Error(property, "class %s is not compatible with property type %s", clsname, cp->ClassRestriction->TypeName.GetChars()); + } + *(PClass**)addr = cls; + } + } + else + { + Error(property, "unhandled property type %s", f->Type->DescriptiveName()); + } + exp->ToErrorNode(); // invalidate after processing. + exp = static_cast(exp->SiblingNext); + } +} + +//========================================================================== +// +// Parses an actor property +// +//========================================================================== + +void ZCCRazeCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *prop, Baggage &bag) +{ + auto namenode = prop->Prop; + FString propname; + + if (namenode->SiblingNext == namenode) + { + // a one-name property + propname = FName(namenode->Id).GetChars(); + + } + else if (namenode->SiblingNext->SiblingNext == namenode) + { + // a two-name property + propname << FName(namenode->Id).GetChars() << "." << FName(static_cast(namenode->SiblingNext)->Id).GetChars(); + } + else + { + Error(prop, "Property name may at most contain two parts"); + return; + } + + + FPropertyInfo *property = FindProperty(propname); + + if (property != nullptr && property->category != CAT_INFO) + { + auto pcls = PClass::FindActor(property->clsname); + if (cls->IsDescendantOf(pcls)) + { + DispatchProperty(property, prop, (DCoreActor *)bag.Info->Defaults, bag); + } + else + { + Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), pcls->TypeName.GetChars()); + } + } + else + { + propname.Insert(0, "@property@"); + FName name(propname, true); + if (name != NAME_None) + { + auto propp = dyn_cast(cls->FindSymbol(name, true)); + if (propp != nullptr) + { + DispatchScriptProperty(propp, prop, (DCoreActor *)bag.Info->Defaults, bag); + return; + } + } + Error(prop, "'%s' is an unknown actor property\n", propname.GetChars()); + } +} + +//========================================================================== +// +// Finds a flag and sets or clears it +// +//========================================================================== + +void ZCCRazeCompiler::ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg) +{ + auto namenode = flg->name; + const char *n1 = FName(namenode->Id).GetChars(), *n2; + + if (namenode->SiblingNext == namenode) + { + // a one-name flag + n2 = nullptr; + } + else if (namenode->SiblingNext->SiblingNext == namenode) + { + // a two-name flag + n2 = FName(static_cast(namenode->SiblingNext)->Id).GetChars(); + } + else + { + Error(flg, "Flag name may at most contain two parts"); + return; + } + +#if 0 + auto fd = FindFlag(cls, n1, n2, true); + if (fd != nullptr) + { + if (fd->varflags & VARF_Deprecated) + { + Warn(flg, "Deprecated flag '%s%s%s' used", n1, n2 ? "." : "", n2 ? n2 : ""); + } + if (fd->structoffset == -1) + { + HandleDeprecatedFlags((DCoreActor*)cls->Defaults, cls, flg->set, fd->flagbit); + } + else + { + ModActorFlag((DCoreActor*)cls->Defaults, fd, flg->set); + } + } + else +#endif + { + Error(flg, "Unknown flag '%s%s%s'", n1, n2 ? "." : "", n2 ? n2 : ""); + } +} + +//========================================================================== +// +// Parses the default list +// +//========================================================================== + +void ZCCRazeCompiler::InitDefaults() +{ + for (auto c : Classes) + { + // This may be removed if the conditions change, but right now only subclasses of Actor can define a Default block. + if (!c->ClassType()->IsDescendantOf(RUNTIME_CLASS(DCoreActor))) + { + if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->ClassType()->TypeName.GetChars()); + if (c->ClassType()->ParentClass) + { + auto ti = c->ClassType(); + ti->InitializeDefaults(); + } + } + else + { + auto cls = c->ClassType(); + // This should never happen. + if (cls->Defaults != nullptr) + { + Error(c->cls, "%s already has defaults", cls->TypeName.GetChars()); + } + // This can only occur if a native parent is not initialized. In all other cases the sorting of the class list should prevent this from ever happening. + else if (cls->ParentClass->Defaults == nullptr && cls != RUNTIME_CLASS(DCoreActor)) + { + Error(c->cls, "Parent class %s of %s is not initialized", cls->ParentClass->TypeName.GetChars(), cls->TypeName.GetChars()); + } + else + { + // Copy the parent's defaults and meta data. + auto ti = static_cast(cls); + + ti->InitializeDefaults(); + + // Replacements require that the meta data has been allocated by InitializeDefaults. + if (c->cls->Replaces != nullptr && !ti->SetReplacement(c->cls->Replaces->Id)) + { + Warn(c->cls, "Replaced type '%s' not found for %s", FName(c->cls->Replaces->Id).GetChars(), ti->TypeName.GetChars()); + } + + + Baggage bag; + bag.Version = mVersion; + bag.Namespace = OutNamespace; + bag.Info = ti; + bag.Lumpnum = c->cls->SourceLump; + // The actual script position needs to be set per property. + + for (auto d : c->Defaults) + { + auto content = d->Content; + if (content != nullptr) do + { + switch (content->NodeType) + { + case AST_PropertyStmt: + bag.ScriptPosition.FileName = *content->SourceName; + bag.ScriptPosition.ScriptLine = content->SourceLoc; + ProcessDefaultProperty(ti, static_cast(content), bag); + break; + + case AST_FlagStmt: + ProcessDefaultFlag(ti, static_cast(content)); + break; + + default: + break; + } + content = static_cast(content->SiblingNext); + } while (content != d->Content); + } + } + } + } +} + +//========================================================================== +// +// DCoreActor needs the actor info manually added to its meta data +// before adding any scripted fields. +// +//========================================================================== + +bool ZCCRazeCompiler::PrepareMetaData(PClass *type) +{ + if (type == RUNTIME_CLASS(DCoreActor)) + { + assert(type->MetaSize == 0); + AddActorInfo(type); + return true; + } + return false; +} + diff --git a/source/core/zcc_compile_raze.h b/source/core/zcc_compile_raze.h new file mode 100644 index 000000000..e23532bee --- /dev/null +++ b/source/core/zcc_compile_raze.h @@ -0,0 +1,32 @@ +#pragma once +#include "zcc_compile.h" + +void SetImplicitArgs(TArray* args, TArray* argflags, TArray* argnames, PContainerType* cls, uint32_t funcflags, int useflags); + +class ZCCRazeCompiler : public ZCCCompiler +{ +public: + ZCCRazeCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PNamespace *outnamespace, int lumpnum, const VersionInfo & ver) + : ZCCCompiler(tree, outer, symbols, outnamespace, lumpnum, ver) + {} + int Compile() override; +protected: + bool PrepareMetaData(PClass *type) override; + void SetImplicitArgs(TArray* args, TArray* argflags, TArray* argnames, PContainerType* cls, uint32_t funcflags, int useflags) override + { + ::SetImplicitArgs(args, argflags, argnames, cls, funcflags, useflags); + } +private: + void CompileAllProperties(); + bool CompileProperties(PClass *type, TArray &Properties, FName prefix); + bool CompileFlagDefs(PClass *type, TArray &Properties, FName prefix); + void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *property, DCoreActor *defaults, Baggage &bag); + void DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, DCoreActor *defaults, Baggage &bag); + void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *prop, Baggage &bag); + void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg); + void InitDefaults() override final; + int CheckActionKeyword(ZCC_FuncDeclarator *f, uint32_t &varflags, int useflags, ZCC_StructWork *c); + +}; + + diff --git a/source/games/duke/src/constants.h b/source/games/duke/src/constants.h index f713580c4..222ebf031 100644 --- a/source/games/duke/src/constants.h +++ b/source/games/duke/src/constants.h @@ -1,5 +1,6 @@ #pragma once #include "tflags.h" +#include "coreactor.h" // all game constants got collected here.