diff --git a/src/b_think.cpp b/src/b_think.cpp index e41433fa3..fa6938571 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -360,7 +360,7 @@ void DBot::WhatToGet (AActor *item) } else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere) return; - else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina) + else if (item->IsKindOf (PClass::FindActor(NAME_Health)) && player->mo->health >= player->mo->GetMaxHealth() + player->mo->stamina) return; if ((dest == NULL || diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 41b89f7c4..aea955b8a 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -1971,21 +1971,21 @@ static int PatchMisc (int dummy) barmor->MaxSaveAmount = deh.MaxArmor; } - AHealth *health; - health = static_cast (GetDefaultByName ("HealthBonus")); + AInventory *health; + health = static_cast (GetDefaultByName ("HealthBonus")); if (health!=NULL) { health->MaxAmount = 2 * deh.MaxHealth; } - health = static_cast (GetDefaultByName ("Soulsphere")); + health = static_cast (GetDefaultByName ("Soulsphere")); if (health!=NULL) { health->Amount = deh.SoulsphereHealth; health->MaxAmount = deh.MaxSoulsphere; } - health = static_cast (GetDefaultByName ("MegasphereHealth")); + health = static_cast (GetDefaultByName ("MegasphereHealth")); if (health!=NULL) { health->Amount = health->MaxAmount = deh.MegasphereHealth; diff --git a/src/dobject.cpp b/src/dobject.cpp index a0f046b75..aa3736147 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -566,3 +566,16 @@ DEFINE_ACTION_FUNCTION(DObject, GetClassName) PARAM_SELF_PROLOGUE(DObject); ACTION_RETURN_INT(self->GetClass()->TypeName); } + + +void *DObject::ScriptVar(FName field, PType *type) +{ + auto sym = dyn_cast(GetClass()->Symbols.FindSymbol(field, true)); + if (sym && sym->Type == type) + { + return (((char*)this) + sym->Offset); + } + // This is only for internal use so I_Error is fine. + I_Error("Variable %s not found in %s\n", field.GetChars(), GetClass()->TypeName.GetChars()); + return nullptr; +} diff --git a/src/dobject.h b/src/dobject.h index 0f99361ef..dc550355e 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -39,7 +39,7 @@ #include "i_system.h" class PClass; - +class PType; class FSerializer; class DObject; @@ -94,7 +94,6 @@ enum CLASSREG_PClass, CLASSREG_PClassActor, CLASSREG_PClassInventory, - CLASSREG_PClassHealth, CLASSREG_PClassPuzzleItem, CLASSREG_PClassWeapon, CLASSREG_PClassPlayerPawn, @@ -453,6 +452,8 @@ public: DObject *GCNext; // Next object in this collection list uint32 ObjectFlags; // Flags for this object + void *ScriptVar(FName field, PType *type); + public: DObject (); DObject (PClass *inClass); @@ -476,6 +477,10 @@ public: virtual void OnDestroy() {} void Destroy(); + // Add other types as needed. + int &IntVar(FName field); + double &FloatVar(FName field); + // If you need to replace one object with another and want to // change any pointers from the old object to the new object, // use this method. diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index bde74f649..2c4b06521 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2609,6 +2609,27 @@ PField::PField(FName name, PType *type, DWORD flags, size_t offset, int bitvalue else BitValue = -1; } +/* PProperty *****************************************************************/ + +IMPLEMENT_CLASS(PProperty, false, false) + +//========================================================================== +// +// PField - Default Constructor +// +//========================================================================== + +PProperty::PProperty() + : PSymbol(NAME_None) +{ +} + +PProperty::PProperty(FName name, TArray &fields) + : PSymbol(name) +{ + Variables = std::move(fields); +} + /* PPrototype *************************************************************/ IMPLEMENT_CLASS(PPrototype, false, false) @@ -3094,7 +3115,6 @@ PClass *ClassReg::RegisterClass() &PClass::RegistrationInfo, &PClassActor::RegistrationInfo, &PClassInventory::RegistrationInfo, - &PClassHealth::RegistrationInfo, &PClassPuzzleItem::RegistrationInfo, &PClassWeapon::RegistrationInfo, &PClassPlayerPawn::RegistrationInfo, @@ -3234,7 +3254,7 @@ DObject *PClass::CreateNew() const ConstructNative (mem); ((DObject *)mem)->SetClass (const_cast(this)); - InitializeSpecials(mem); + InitializeSpecials(mem, Defaults); return (DObject *)mem; } @@ -3246,7 +3266,7 @@ DObject *PClass::CreateNew() const // //========================================================================== -void PClass::InitializeSpecials(void *addr) const +void PClass::InitializeSpecials(void *addr, void *defaults) const { // Once we reach a native class, we can stop going up the family tree, // since native classes handle initialization natively. @@ -3255,10 +3275,10 @@ void PClass::InitializeSpecials(void *addr) const return; } assert(ParentClass != NULL); - ParentClass->InitializeSpecials(addr); + ParentClass->InitializeSpecials(addr, defaults); for (auto tao : SpecialInits) { - tao.first->InitializeValue((BYTE*)addr + tao.second, Defaults == nullptr? nullptr : Defaults + tao.second); + tao.first->InitializeValue((char*)addr + tao.second, defaults == nullptr? nullptr : ((char*)defaults) + tao.second); } } @@ -3332,7 +3352,7 @@ void PClass::InitializeDefaults() { // Copy parent values from the parent defaults. assert(ParentClass != NULL); - ParentClass->InitializeSpecials(Defaults); + ParentClass->InitializeSpecials(Defaults, ParentClass->Defaults); for (const PField *field : Fields) { @@ -3923,6 +3943,13 @@ PSymbol *PSymbolTable::AddSymbol (PSymbol *sym) return sym; } +void PSymbolTable::RemoveSymbol(PSymbol *sym) +{ + auto mysym = Symbols.CheckKey(sym->SymbolName); + if (mysym == nullptr || *mysym != sym) return; + Symbols.Remove(sym->SymbolName); +} + PSymbol *PSymbolTable::ReplaceSymbol(PSymbol *newsym) { // If a symbol with a matching name exists, take its place and return it. diff --git a/src/dobjtype.h b/src/dobjtype.h index 6f33483fc..156818147 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -29,11 +29,12 @@ enum VARF_In = (1<<10), VARF_Out = (1<<11), VARF_Implicit = (1<<12), // implicitly created parameters (i.e. do not compare types when checking function signatures) - VARF_Static = (1<<13), // static class data (by necessity read only.) + VARF_Static = (1<<13), VARF_InternalAccess = (1<<14), // overrides VARF_ReadOnly for internal script code. VARF_Override = (1<<15), // overrides a virtual function from the parent class. VARF_Ref = (1<<16), // argument is passed by reference. - VARF_Transient = (1<<17) // don't auto serialize field. + VARF_Transient = (1<<17), // don't auto serialize field. + VARF_Meta = (1<<18), // static class data (by necessity read only.) }; // Symbol information ------------------------------------------------------- @@ -137,6 +138,8 @@ struct PSymbolTable // to be in the table with this name, if any. PSymbol *ReplaceSymbol(PSymbol *sym); + void RemoveSymbol(PSymbol *sym); + // Frees all symbols from this table. void ReleaseSymbols(); @@ -621,6 +624,21 @@ protected: PField(); }; +// Struct/class fields ------------------------------------------------------ + +// A PField describes a symbol that takes up physical space in the struct. +class PProperty : public PSymbol +{ + DECLARE_CLASS(PProperty, PSymbol); +public: + PProperty(FName name, TArray &variables); + + TArray Variables; + +protected: + PProperty(); +}; + // Compound types ----------------------------------------------------------- class PEnum : public PNamedType @@ -807,7 +825,7 @@ protected: enum { MetaClassNum = CLASSREG_PClassClass }; TArray SpecialInits; void Derive(PClass *newclass, FName name); - void InitializeSpecials(void *addr) const; + void InitializeSpecials(void *addr, void *defaults) const; void SetSuper(); public: typedef PClassClass MetaClass; @@ -1041,4 +1059,15 @@ enum ETypeVal : BYTE VAL_Class, }; +inline int &DObject::IntVar(FName field) +{ + return *(int*)ScriptVar(field, TypeSInt32); +} + +inline double &DObject::FloatVar(FName field) +{ + return *(double*)ScriptVar(field, TypeFloat64); +} + + #endif diff --git a/src/g_inventory/a_health.cpp b/src/g_inventory/a_health.cpp index 86b4b3e86..005e0f399 100644 --- a/src/g_inventory/a_health.cpp +++ b/src/g_inventory/a_health.cpp @@ -45,42 +45,7 @@ // //=========================================================================== -IMPLEMENT_CLASS(PClassHealth, false, false) -IMPLEMENT_CLASS(AHealth, false, false) -DEFINE_FIELD(AHealth, PrevHealth) -DEFINE_FIELD(PClassHealth, LowHealth) -DEFINE_FIELD(PClassHealth, LowHealthMessage) - -//=========================================================================== -// -// PClassHealth Constructor -// -//=========================================================================== - -PClassHealth::PClassHealth() -{ - LowHealth = 0; -} - -//=========================================================================== -// -// PClassHealth :: DeriveData -// -//=========================================================================== - -void PClassHealth::DeriveData(PClass *newclass) -{ - assert(newclass->IsKindOf(RUNTIME_CLASS(PClassHealth))); - Super::DeriveData(newclass); - PClassHealth *newc = static_cast(newclass); - - newc->LowHealth = LowHealth; - newc->LowHealthMessage = LowHealthMessage; -} - - IMPLEMENT_CLASS(AHealthPickup, false, false) - DEFINE_FIELD(AHealthPickup, autousemode) //=========================================================================== diff --git a/src/g_inventory/a_health.h b/src/g_inventory/a_health.h index 826d729f9..c44d13659 100644 --- a/src/g_inventory/a_health.h +++ b/src/g_inventory/a_health.h @@ -2,27 +2,6 @@ #include "a_pickups.h" -// Health is some item that gives the player health when picked up. -class PClassHealth : public PClassInventory -{ - DECLARE_CLASS(PClassHealth, PClassInventory) -protected: -public: - PClassHealth(); - virtual void DeriveData(PClass *newclass); - - FString LowHealthMessage; - int LowHealth; -}; - -class AHealth : public AInventory -{ - DECLARE_CLASS_WITH_META(AHealth, AInventory, PClassHealth) - -public: - int PrevHealth; -}; - // HealthPickup is some item that gives the player health when used. class AHealthPickup : public AInventory { diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index b47788212..5d4e83f73 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -2400,7 +2400,7 @@ static bool DoGiveInventory(AActor *receiver, bool orresult, VM_ARGS) { return false; } - if (item->IsKindOf(RUNTIME_CLASS(AHealth))) + if (item->IsKindOf(PClass::FindActor(NAME_Health))) { item->Amount *= amount; } @@ -5655,7 +5655,7 @@ static bool DoRadiusGive(AActor *self, AActor *thing, PClassActor *item, int amo if ((flags & RGF_NOSIGHT) || P_CheckSight(thing, self, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. AInventory *gift = static_cast(Spawn(item)); - if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) + if (gift->IsKindOf(PClass::FindActor(NAME_Health))) { gift->Amount *= amount; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 9ee26cd44..21b2d8563 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1341,7 +1341,7 @@ bool P_GiveBody(AActor *actor, int num, int max) if (player != NULL) { // Max is 0 by default, preserving default behavior for P_GiveBody() - // calls while supporting AHealth. + // calls while supporting health pickups. if (max <= 0) { max = static_cast(actor)->GetMaxHealth() + player->mo->stamina; @@ -1396,7 +1396,7 @@ bool P_GiveBody(AActor *actor, int num, int max) else { // Parameter value for max is ignored on monsters, preserving original - // behaviour on AHealth as well as on existing calls to P_GiveBody(). + // behaviour of health as well as on existing calls to P_GiveBody(). max = actor->SpawnHealth(); if (num < 0) { @@ -5663,9 +5663,10 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // [RH] Other things that shouldn't be spawned depending on dmflags if (deathmatch || alwaysapplydmflags) { + // Fixme: This needs to be done differently, it's quite broken. if (dmflags & DF_NO_HEALTH) { - if (i->IsDescendantOf (RUNTIME_CLASS(AHealth))) + if (i->IsDescendantOf (PClass::FindActor(NAME_Health))) return NULL; if (i->TypeName == NAME_Berserk) return NULL; diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index 8e4b42f69..31a9c022f 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -146,7 +146,7 @@ std2: 'instanceof' { RET(TK_InstanceOf); } 'auto' { RET(TK_Auto); } 'exec' { RET(TK_Exec); } - 'defaultproperties' { RET(TK_DefaultProperties); } + 'property' { RET(TK_Property); } 'native' { RET(TK_Native); } 'var' { RET(TK_Var); } 'out' { RET(TK_Out); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index efa479cf3..fb8f1c5ae 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -67,6 +67,7 @@ xx(TK_Long, "'long'") xx(TK_ULong, "'ulong'") xx(TK_Void, "'void'") xx(TK_Struct, "'struct'") +xx(TK_Property, "'property'") xx(TK_Class, "'class'") xx(TK_Enum, "'enum'") xx(TK_Name, "'name'") diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index c072e7799..7f401720b 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -208,10 +208,6 @@ ExpEmit::ExpEmit(VMFunctionBuilder *build, int type, int count) void ExpEmit::Free(VMFunctionBuilder *build) { - if (RegType == REGT_INT && RegNum == 0) - { - int a = 0; - } if (!Fixed && !Konst && RegType <= REGT_TYPE) { build->Registers[RegType].Return(RegNum, RegCount); @@ -4447,10 +4443,6 @@ FxExpression *FxDynamicCast::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); SAFE_RESOLVE(expr, ctx); - if (expr->ExprType == EFX_GetDefaultByType) - { - int a = 0; - } bool constflag = expr->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast(expr->ValueType)->IsConst; if (constflag) { @@ -6472,7 +6464,7 @@ FxStructMember::~FxStructMember() bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) { // Cannot take the address of metadata variables. - if (membervar->Flags & VARF_Static) + if (membervar->Flags & VARF_Meta) { return false; } @@ -6615,7 +6607,7 @@ ExpEmit FxStructMember::Emit(VMFunctionBuilder *build) obj = newobj; } - if (membervar->Flags & VARF_Static) + if (membervar->Flags & VARF_Meta) { obj.Free(build); ExpEmit meta(build, REGT_POINTER); diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 32f7dd622..69ef02e88 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -1969,18 +1969,6 @@ DEFINE_CLASS_PROPERTY(givequest, I, Inventory) static_cast(info)->GiveQuest = i; } -//========================================================================== -// -//========================================================================== -DEFINE_CLASS_PROPERTY(lowmessage, IT, Health) -{ - PROP_INT_PARM(i, 0); - PROP_STRING_PARM(str, 1); - assert(info->IsKindOf(RUNTIME_CLASS(PClassHealth))); - static_cast(info)->LowHealth = i; - static_cast(info)->LowHealthMessage = str; -} - //========================================================================== // //========================================================================== diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index 3bdc2e244..b29e1eeb2 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -336,6 +336,16 @@ static void PrintStruct(FLispString &out, ZCC_TreeNode *node) out.Close(); } +static void PrintProperty(FLispString &out, ZCC_TreeNode *node) +{ + ZCC_Property *snode = (ZCC_Property *)node; + out.Break(); + out.Open("property"); + out.AddName(snode->NodeName); + PrintNodes(out, snode->Body, false, true); + out.Close(); +} + static void PrintEnum(FLispString &out, ZCC_TreeNode *node) { ZCC_Enum *enode = (ZCC_Enum *)node; @@ -934,6 +944,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintVectorInitializer, PrintDeclFlags, PrintExprClassCast, + PrintProperty, }; FString ZCC_PrintAST(ZCC_TreeNode *root) diff --git a/src/scripting/zscript/zcc-parse.lemon b/src/scripting/zscript/zcc-parse.lemon index 9305b9f71..d0ff03625 100644 --- a/src/scripting/zscript/zcc-parse.lemon +++ b/src/scripting/zscript/zcc-parse.lemon @@ -209,7 +209,6 @@ class_ancestry(X) ::= COLON dottable_id(A). { X = A; /*X-overwrites-A*/ } class_flags(X) ::= . { X.Flags = 0; X.Replaces = NULL; } class_flags(X) ::= class_flags(A) ABSTRACT. { X.Flags = A.Flags | ZCC_Abstract; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) NATIVE. { X.Flags = A.Flags | ZCC_Native; X.Replaces = A.Replaces; } -class_flags(X) ::= class_flags(A) ACTION. { X.Flags = A.Flags | ZCC_Action; X.Replaces = A.Replaces; } class_flags(X) ::= class_flags(A) REPLACES dottable_id(B). { X.Flags = A.Flags; X.Replaces = B; } /*----- Dottable Identifier -----*/ @@ -265,6 +264,7 @@ class_body(X) ::= LBRACE class_innards(A) RBRACE. { X = A; /*X-overwrites-A*/ } class_innards(X) ::= . { X = NULL; } class_innards(X) ::= class_innards(X) class_member(B). { SAFE_APPEND(X,B); } +%type property_def{ZCC_Property *} %type struct_def{ZCC_Struct *} %type enum_def {ZCC_Enum *} %type states_def {ZCC_States *} @@ -276,6 +276,7 @@ class_member(X) ::= struct_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= states_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= default_def(A). { X = A; /*X-overwrites-A*/ } class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } +class_member(X) ::= property_def(A). { X = A; /*X-overwrites-A*/ } /*----- Struct Definition -----*/ /* Structs can define variables and enums. */ @@ -283,6 +284,30 @@ class_member(X) ::= const_def(A). { X = A; /*X-overwrites-A*/ } %type opt_struct_body{ZCC_TreeNode *} %type struct_body{ZCC_TreeNode *} %type struct_member{ZCC_TreeNode *} +%type identifier_list{ZCC_Identifier *} + +property_def(X) ::= PROPERTY(T) IDENTIFIER(A) COLON identifier_list(B) SEMICOLON. +{ + NEW_AST_NODE(Property,def,T); + def->NodeName = A.Name(); + def->Body = B; + X = def; +} + +identifier_list(X) ::= IDENTIFIER(A). +{ + NEW_AST_NODE(Identifier,id,A); + id->Id = A.Name(); + X = id; +} + +identifier_list(X) ::= states_opt(A) COMMA IDENTIFIER(B). +{ + NEW_AST_NODE(Identifier,id,B); + id->Id = B.Name(); + X = A; /*X-overwrites-A*/ + AppendTreeNodeSibling(X, id); +} struct_def(X) ::= STRUCT(T) IDENTIFIER(A) struct_flags(S) LBRACE opt_struct_body(B) RBRACE opt_semicolon. { @@ -747,7 +772,7 @@ type_name(X) ::= DOT dottable_id(A). /* Type names can also be used as identifiers in contexts where type names * are not normally allowed. */ %fallback IDENTIFIER - SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16. + SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY. /* Aggregate types */ %type aggregate_type {ZCC_Type *} @@ -964,6 +989,7 @@ decl_flag(X) ::= PROTECTED(T). { X.Int = ZCC_Protected; X.SourceLoc = T.SourceL decl_flag(X) ::= LATENT(T). { X.Int = ZCC_Latent; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= FINAL(T). { X.Int = ZCC_Final; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= META(T). { X.Int = ZCC_Meta; X.SourceLoc = T.SourceLoc; } +decl_flag(X) ::= TRANSIENT(T). { X.Int = ZCC_Transient; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= READONLY(T). { X.Int = ZCC_ReadOnly; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= DEPRECATED(T). { X.Int = ZCC_Deprecated; X.SourceLoc = T.SourceLoc; } decl_flag(X) ::= VIRTUAL(T). { X.Int = ZCC_Virtual; X.SourceLoc = T.SourceLoc; } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 817e28b24..281f707d2 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -120,6 +120,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode) } break; + case AST_Property: + cls->Properties.Push(static_cast(node)); + break; + case AST_VarDeclarator: cls->Fields.Push(static_cast(node)); break; @@ -396,6 +400,7 @@ int ZCCCompiler::Compile() CreateStructTypes(); CompileAllConstants(); CompileAllFields(); + CompileAllProperties(); InitDefaults(); InitFunctions(); CompileStates(); @@ -1269,6 +1274,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (field->Flags & ZCC_Protected) varflags |= VARF_Protected; if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated; if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly; + if (field->Flags & ZCC_Transient) varflags |= VARF_Transient; if (field->Flags & ZCC_Native) { @@ -1277,7 +1283,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (field->Flags & ZCC_Meta) { - varflags |= VARF_Static|VARF_ReadOnly; // metadata implies readonly + varflags |= VARF_Meta | VARF_Static | VARF_ReadOnly; // metadata implies readonly if (!(field->Flags & ZCC_Native)) { // Non-native meta data is not implemented yet and requires some groundwork in the class copy code. @@ -1303,7 +1309,7 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel if (varflags & VARF_Native) { - auto querytype = (varflags & VARF_Static) ? type->GetClass() : type; + auto querytype = (varflags & VARF_Meta) ? type->GetClass() : type; fd = FindField(querytype, FName(name->Name).GetChars()); if (fd == nullptr) { @@ -1337,6 +1343,68 @@ bool ZCCCompiler::CompileFields(PStruct *type, TArray &Fiel return Fields.Size() == 0; } +//========================================================================== +// +// ZCCCompiler :: CompileAllProperties +// +// builds the property lists of all actor classes +// +//========================================================================== + +void ZCCCompiler::CompileAllProperties() +{ + for (auto c : Classes) + { + if (c->Properties.Size() > 0) + CompileProperties(c->Type(), c->Properties, c->Type()->TypeName); + } +} + +//========================================================================== +// +// ZCCCompiler :: CompileProperties +// +// builds the internal structure of a single class or struct +// +//========================================================================== + +bool ZCCCompiler::CompileProperties(PClass *type, TArray &Properties, FName prefix) +{ + if (!type->IsKindOf(RUNTIME_CLASS(PClassActor))) + { + 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; + + do + { + auto f = dyn_cast(type->Symbols.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. + if (prefix == NAME_None) qualifiedname.Format("@property@%s", FName(p->NodeName).GetChars()); + else qualifiedname.Format("@property@%s.%s", prefix.GetChars(), FName(p->NodeName).GetChars()); + fields.ShrinkToFit(); + if (!type->Symbols.AddSymbol(new PProperty(qualifiedname, fields))) + { + Error(id, "Unable to add property %s to class %s", FName(p->NodeName).GetChars(), type->TypeName.GetChars()); + } + } + return true; +} + //========================================================================== // // ZCCCompiler :: FieldFlagsToString @@ -1841,6 +1909,73 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper } } + +//========================================================================== +// +// Parses an actor property's parameters and calls the handler +// +//========================================================================== + +void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag) +{ + unsigned parmcount = 1; + ZCC_TreeNode *x = property->Values; + while (x->SiblingNext != property->Values) + { + x = x->SiblingNext; + parmcount++; + } + if (parmcount != prop->Variables.Size()) + { + Error(x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size()); + return; + } + + auto values = Simplify(property->Values, &bag.Info->Symbols, true); // need to do this before the loop so that we can find the head node again. + auto exp = values; + for (auto f : prop->Variables) + { + void *addr; + + if (f->Flags & VARF_Meta) + { + addr = ((char*)bag.Info) + f->Offset; + } + else + { + addr = ((char*)defaults) + f->Offset; + } + + if (f->Type->IsKindOf(RUNTIME_CLASS(PInt))) + { + static_cast(f->Type)->SetValue(addr, GetInt(exp)); + } + else if (f->Type->IsKindOf(RUNTIME_CLASS(PFloat))) + { + static_cast(f->Type)->SetValue(addr, GetDouble(exp)); + } + else if (f->Type->IsKindOf(RUNTIME_CLASS(PString))) + { + *(FString*)addr = GetString(exp); + } + else if (f->Type->IsKindOf(RUNTIME_CLASS(PClassPointer))) + { + auto cls = PClass::FindClass(GetString(exp)); + *(PClass**)addr = cls; + if (!cls->IsDescendantOf(static_cast(f->Type)->ClassRestriction)) + { + Error(property, "class %s is not compatible with property type %s", cls->TypeName.GetChars(), static_cast(f->Type)->ClassRestriction->TypeName.GetChars()); + } + } + else + { + Error(property, "unhandled property type %s", f->Type->DescriptiveName()); + } + exp->ToErrorNode(); // invalidate after processing. + exp = static_cast(exp->SiblingNext); + } +} + //========================================================================== // // Parses an actor property @@ -1893,6 +2028,17 @@ void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *pro } else { + propname.Insert(0, "@property@"); + FName name(propname, true); + if (name != NAME_None) + { + auto propp = dyn_cast(cls->Symbols.FindSymbol(name, true)); + if (propp != nullptr) + { + DispatchScriptProperty(propp, prop, (AActor *)bag.Info->Defaults, bag); + return; + } + } Error(prop, "'%s' is an unknown actor property\n", propname.GetChars()); } } diff --git a/src/scripting/zscript/zcc_compile.h b/src/scripting/zscript/zcc_compile.h index f9ebc62d4..8675b1af7 100644 --- a/src/scripting/zscript/zcc_compile.h +++ b/src/scripting/zscript/zcc_compile.h @@ -51,6 +51,7 @@ struct ZCC_ClassWork : public ZCC_StructWork ZCC_Class *cls; TArray Defaults; TArray States; + TArray Properties; ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n) { @@ -67,6 +68,12 @@ struct ZCC_ClassWork : public ZCC_StructWork } }; +struct ZCC_PropertyWork +{ + ZCC_Property *prop; + PSymbolTable *outputtable; +}; + struct ZCC_ConstantWork { ZCC_ConstantDef *node; @@ -92,6 +99,8 @@ private: void CompileAllFields(); bool CompileFields(PStruct *type, TArray &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false); + void CompileAllProperties(); + bool CompileProperties(PClass *type, TArray &Properties, FName prefix); FString FlagsToString(uint32_t flags); PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember); PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym); @@ -101,6 +110,7 @@ private: void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg); void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *flg, Baggage &bag); void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag); + void DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag); int GetInt(ZCC_Expression *expr); double GetDouble(ZCC_Expression *expr); const char *GetString(ZCC_Expression *expr, bool silent = false); @@ -114,6 +124,7 @@ private: TArray Constants; TArray Structs; TArray Classes; + TArray Properties; PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false); diff --git a/src/scripting/zscript/zcc_parser.cpp b/src/scripting/zscript/zcc_parser.cpp index 07cc695d8..a0ecbe06c 100644 --- a/src/scripting/zscript/zcc_parser.cpp +++ b/src/scripting/zscript/zcc_parser.cpp @@ -144,6 +144,8 @@ static void InitTokenMap() TOKENDEF ('{', ZCC_LBRACE); TOKENDEF ('}', ZCC_RBRACE); TOKENDEF (TK_Struct, ZCC_STRUCT); + TOKENDEF (TK_Property, ZCC_PROPERTY); + TOKENDEF (TK_Transient, ZCC_TRANSIENT); TOKENDEF (TK_Enum, ZCC_ENUM); TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte); TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte); diff --git a/src/scripting/zscript/zcc_parser.h b/src/scripting/zscript/zcc_parser.h index d02b1a12f..19c327fc9 100644 --- a/src/scripting/zscript/zcc_parser.h +++ b/src/scripting/zscript/zcc_parser.h @@ -35,6 +35,7 @@ enum ZCC_Extension = 1 << 12, ZCC_Virtual = 1 << 13, ZCC_Override = 1 << 14, + ZCC_Transient = 1 << 15, }; // Function parameter modifiers @@ -104,6 +105,7 @@ enum EZCCTreeNodeType AST_DeclFlags, AST_ClassCast, AST_StaticArrayStatement, + AST_Property, NUM_AST_NODE_TYPES }; @@ -189,6 +191,11 @@ struct ZCC_Struct : ZCC_NamedNode PStruct *Type; }; +struct ZCC_Property : ZCC_NamedNode +{ + ZCC_TreeNode *Body; +}; + struct ZCC_Class : ZCC_Struct { ZCC_Identifier *ParentName; diff --git a/wadsrc/static/zscript/inventory/health.txt b/wadsrc/static/zscript/inventory/health.txt index f5d0f6628..81ffeb164 100644 --- a/wadsrc/static/zscript/inventory/health.txt +++ b/wadsrc/static/zscript/inventory/health.txt @@ -33,11 +33,13 @@ ** */ -class Health : Inventory native +class Health : Inventory { - native int PrevHealth; - native meta int LowHealth; - native meta String LowHealthMessage; + transient int PrevHealth; + /*meta*/ int LowHealth; + /*meta*/ String LowHealthMessage; + + property LowMessage: LowHealth, LowHealthMessage; Default { @@ -56,7 +58,6 @@ class Health : Inventory native if (PrevHealth < LowHealth) { String message = LowHealthMessage; - if (message.Length() != 0) { return message;