mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-28 06:53:58 +00:00
- 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:
parent
0c3aab794f
commit
179b6e1a39
21 changed files with 313 additions and 109 deletions
|
@ -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 ||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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'")
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//==========================================================================
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue