- added property definitions to the ZScript parser. This will allow defining custom properties for the default block in custom base classes. See 'Health' for an example.

- support transient object member variables for information that does not need to be put in a savegame.
- fixed: special initialization of objects needs to pass the proper defaults along, otherwise the parent classes will use their own, inappropriate one.
This commit is contained in:
Christoph Oelckers 2017-01-15 16:55:30 +01:00
parent 0c3aab794f
commit 179b6e1a39
21 changed files with 313 additions and 109 deletions

View file

@ -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 ||

View file

@ -1971,21 +1971,21 @@ static int PatchMisc (int dummy)
barmor->MaxSaveAmount = deh.MaxArmor;
}
AHealth *health;
health = static_cast<AHealth *> (GetDefaultByName ("HealthBonus"));
AInventory *health;
health = static_cast<AInventory *> (GetDefaultByName ("HealthBonus"));
if (health!=NULL)
{
health->MaxAmount = 2 * deh.MaxHealth;
}
health = static_cast<AHealth *> (GetDefaultByName ("Soulsphere"));
health = static_cast<AInventory *> (GetDefaultByName ("Soulsphere"));
if (health!=NULL)
{
health->Amount = deh.SoulsphereHealth;
health->MaxAmount = deh.MaxSoulsphere;
}
health = static_cast<AHealth *> (GetDefaultByName ("MegasphereHealth"));
health = static_cast<AInventory *> (GetDefaultByName ("MegasphereHealth"));
if (health!=NULL)
{
health->Amount = health->MaxAmount = deh.MegasphereHealth;

View file

@ -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<PField>(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;
}

View file

@ -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.

View file

@ -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<PField *> &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<PClass *>(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.

View file

@ -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<PField *> &variables);
TArray<PField *> Variables;
protected:
PProperty();
};
// Compound types -----------------------------------------------------------
class PEnum : public PNamedType
@ -807,7 +825,7 @@ protected:
enum { MetaClassNum = CLASSREG_PClassClass };
TArray<FTypeAndOffset> 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

View file

@ -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<PClassHealth *>(newclass);
newc->LowHealth = LowHealth;
newc->LowHealthMessage = LowHealthMessage;
}
IMPLEMENT_CLASS(AHealthPickup, false, false)
DEFINE_FIELD(AHealthPickup, autousemode)
//===========================================================================

View file

@ -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
{

View file

@ -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<AInventory *>(Spawn(item));
if (gift->IsKindOf(RUNTIME_CLASS(AHealth)))
if (gift->IsKindOf(PClass::FindActor(NAME_Health)))
{
gift->Amount *= amount;
}

View file

@ -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<APlayerPawn*>(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;

View file

@ -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); }

View file

@ -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'")

View file

@ -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<PPointer *>(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);

View file

@ -1969,18 +1969,6 @@ DEFINE_CLASS_PROPERTY(givequest, I, Inventory)
static_cast<PClassInventory *>(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<PClassHealth *>(info)->LowHealth = i;
static_cast<PClassHealth *>(info)->LowHealthMessage = str;
}
//==========================================================================
//
//==========================================================================

View file

@ -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)

View file

@ -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; }

View file

@ -120,6 +120,10 @@ void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode)
}
break;
case AST_Property:
cls->Properties.Push(static_cast<ZCC_Property *>(node));
break;
case AST_VarDeclarator:
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(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<ZCC_VarDeclarator *> &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<ZCC_VarDeclarator *> &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<ZCC_VarDeclarator *> &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<ZCC_VarDeclarator *> &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<ZCC_Property *> &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<PField *> fields;
ZCC_Identifier *id = (ZCC_Identifier *)p->Body;
do
{
auto f = dyn_cast<PField>(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<PInt*>(f->Type)->SetValue(addr, GetInt(exp));
}
else if (f->Type->IsKindOf(RUNTIME_CLASS(PFloat)))
{
static_cast<PFloat*>(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<PClassPointer*>(f->Type)->ClassRestriction))
{
Error(property, "class %s is not compatible with property type %s", cls->TypeName.GetChars(), static_cast<PClassPointer*>(f->Type)->ClassRestriction->TypeName.GetChars());
}
}
else
{
Error(property, "unhandled property type %s", f->Type->DescriptiveName());
}
exp->ToErrorNode(); // invalidate after processing.
exp = static_cast<ZCC_Expression *>(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<PProperty>(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());
}
}

View file

@ -51,6 +51,7 @@ struct ZCC_ClassWork : public ZCC_StructWork
ZCC_Class *cls;
TArray<ZCC_Default *> Defaults;
TArray<ZCC_States *> States;
TArray<ZCC_Property *> 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<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct, bool hasnativechildren = false);
void CompileAllProperties();
bool CompileProperties(PClass *type, TArray<ZCC_Property *> &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<ZCC_ConstantDef *> Constants;
TArray<ZCC_StructWork *> Structs;
TArray<ZCC_ClassWork *> Classes;
TArray<ZCC_PropertyWork *> Properties;
PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);

View file

@ -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);

View file

@ -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;

View file

@ -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;