- Removed the Actor uservar array and replaced it with user-defined variables.

A_SetUserVar/SetUserVariable/GetUserVariable now take a variable name
  instead of an array index. A_SetUserArray/SetUserArray/GetUserArray
  have been added to access elements in user-defined arrays.


SVN r1933 (trunk)
This commit is contained in:
Randy Heit 2009-10-25 02:19:51 +00:00
parent f40462dfb4
commit 19b23f2cf3
15 changed files with 374 additions and 28 deletions

View file

@ -1,4 +1,8 @@
October 24, 2009
- Removed the Actor uservar array and replaced it with user-defined variables.
A_SetUserVar/SetUserVariable/GetUserVariable now take a variable name
instead of an array index. A_SetUserArray/SetUserArray/GetUserArray
have been added to access elements in user-defined arrays.
- Rewrote wide sky texture scaling again. This time, it should work for any
size textures at any scale. I also tried doing sky scrolling on the sky
cylinder, but that didn't look so good, so I left it in screen space.

View file

@ -789,7 +789,6 @@ public:
int tid; // thing identifier
int special; // special
int args[5]; // special arguments
int uservar[10]; // user variables, accessible by DECORATE and ACS
AActor *inext, **iprev;// Links to other mobjs in same bucket
TObjPtr<AActor> goal; // Monster's goal if not chasing anything

View file

@ -506,6 +506,88 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld)
return changed;
}
void DObject::SerializeUserVars(FArchive &arc)
{
PSymbolTable *symt;
FName varname;
DWORD count, j;
int *varloc;
if (SaveVersion < 1933)
{
return;
}
symt = &GetClass()->Symbols;
if (arc.IsStoring())
{
// Write all user variables.
for (; symt != NULL; symt = symt->ParentSymbolTable)
{
for (unsigned i = 0; i < symt->Symbols.Size(); ++i)
{
PSymbol *sym = symt->Symbols[i];
if (sym->SymbolType == SYM_Variable)
{
PSymbolVariable *var = static_cast<PSymbolVariable *>(sym);
if (var->bUserVar)
{
count = var->ValueType.Type == VAL_Array ? var->ValueType.size : 1;
varloc = (int *)(reinterpret_cast<BYTE *>(this) + var->offset);
arc << var->SymbolName;
arc.WriteCount(count);
for (j = 0; j < count; ++j)
{
arc << varloc[j];
}
}
}
}
}
// Write terminator.
varname = NAME_None;
arc << varname;
}
else
{
// Read user variables until 'None' is encountered.
arc << varname;
while (varname != NAME_None)
{
PSymbol *sym = symt->FindSymbol(varname, true);
DWORD wanted = 0;
if (sym != NULL && sym->SymbolType == SYM_Variable)
{
PSymbolVariable *var = static_cast<PSymbolVariable *>(sym);
if (var->bUserVar)
{
wanted = var->ValueType.Type == VAL_Array ? var->ValueType.size : 1;
varloc = (int *)(reinterpret_cast<BYTE *>(this) + var->offset);
}
}
count = arc.ReadCount();
for (j = 0; j < MIN(wanted, count); ++j)
{
arc << varloc[j];
}
if (wanted < count)
{
// Ignore remaining values from archive.
for (; j < count; ++j)
{
int foo;
arc << foo;
}
}
arc << varname;
}
}
}
void DObject::Serialize (FArchive &arc)
{
ObjectFlags |= OF_SerialSuccess;

View file

@ -456,6 +456,7 @@ public:
inline bool IsKindOf (const PClass *base) const;
inline bool IsA (const PClass *type) const;
void SerializeUserVars(FArchive &arc);
virtual void Serialize (FArchive &arc);
// For catching Serialize functions in derived classes

View file

@ -118,7 +118,7 @@ void PClass::StaticFreeData (PClass *type)
{
if (type->Defaults != NULL)
{
delete[] type->Defaults;
M_Free(type->Defaults);
type->Defaults = NULL;
}
type->FreeStateList ();
@ -281,7 +281,7 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
type->Meta = Meta;
// Set up default instance of the new class.
type->Defaults = new BYTE[size];
type->Defaults = (BYTE *)M_Malloc(size);
memcpy (type->Defaults, Defaults, Size);
if (size > Size)
{
@ -315,6 +315,19 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size)
return type;
}
// Add <extension> bytes to the end of this class. Returns the
// previous size of the class.
unsigned int PClass::Extend(unsigned int extension)
{
assert(this->bRuntimeClass);
unsigned int oldsize = Size;
Size += extension;
Defaults = (BYTE *)M_Realloc(Defaults, Size);
memset(Defaults + oldsize, 0, extension);
return oldsize;
}
// Like FindClass but creates a placeholder if no class
// is found. CreateDerivedClass will automatcally fill in
// the placeholder when the actual class is defined.

View file

@ -46,8 +46,9 @@ struct PSymbolConst : public PSymbol
struct PSymbolVariable : public PSymbol
{
FExpressionType ValueType;
int size;
//int size;
intptr_t offset;
bool bUserVar;
PSymbolVariable(FName name) : PSymbol(name, SYM_Variable) {}
};
@ -113,6 +114,8 @@ public:
private:
PSymbolTable *ParentSymbolTable;
TArray<PSymbol *> Symbols;
friend class DObject;
};
// Meta-info for every class derived from DObject ---------------------------
@ -143,6 +146,7 @@ struct PClass
void InsertIntoHash ();
DObject *CreateNew () const;
PClass *CreateDerivedClass (FName name, unsigned int size);
unsigned int Extend(unsigned int extension);
void InitializeActorInfo ();
void BuildFlatPointers ();
void FreeStateList();

View file

@ -3,7 +3,7 @@
** Implements an archiver for DObject serialization.
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** Copyright 1998-2009 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -1074,6 +1074,7 @@ FArchive &FArchive::WriteObject (DObject *obj)
WriteClass (type);
// Printf ("Make class %s (%u)\n", type->Name, m_File->Tell());
MapObject (obj);
obj->SerializeUserVars (*this);
obj->Serialize (*this);
obj->CheckIfSerialized ();
}
@ -1105,6 +1106,7 @@ FArchive &FArchive::WriteObject (DObject *obj)
WriteCount (m_TypeMap[type->ClassIndex].toArchive);
// Printf ("Reuse class %s (%u)\n", type->Name, m_File->Tell());
MapObject (obj);
obj->SerializeUserVars (*this);
obj->Serialize (*this);
obj->CheckIfSerialized ();
}
@ -1160,6 +1162,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype)
// stored in the archive.
AActor *tempobj = static_cast<AActor *>(type->CreateNew ());
MapObject (obj != NULL ? obj : tempobj);
tempobj->SerializeUserVars (*this);
tempobj->Serialize (*this);
tempobj->CheckIfSerialized ();
// If this player is not present anymore, keep the new body
@ -1187,6 +1190,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype)
// Printf ("New class: %s (%u)\n", type->Name, m_File->Tell());
obj = type->CreateNew ();
MapObject (obj);
obj->SerializeUserVars (*this);
obj->Serialize (*this);
obj->CheckIfSerialized ();
break;
@ -1201,6 +1205,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype)
AActor *tempobj = static_cast<AActor *>(type->CreateNew ());
MapObject (obj != NULL ? obj : tempobj);
tempobj->SerializeUserVars (*this);
tempobj->Serialize (*this);
tempobj->CheckIfSerialized ();
if (obj != NULL)
@ -1225,6 +1230,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype)
// Printf ("Use class: %s (%u)\n", type->Name, m_File->Tell());
obj = type->CreateNew ();
MapObject (obj);
obj->SerializeUserVars (*this);
obj->Serialize (*this);
obj->CheckIfSerialized ();
break;

View file

@ -2908,6 +2908,8 @@ enum EACSFunctions
ACSF_GetUserVariable,
ACSF_Radius_Quake2,
ACSF_CheckActorClass,
ACSF_SetUserArray,
ACSF_GetUserArray,
};
int DLevelScript::SideFromID(int id, int side)
@ -2942,6 +2944,67 @@ int DLevelScript::LineFromID(int id)
}
}
static void SetUserVariable(AActor *self, FName varname, int index, int value)
{
PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true);
int max;
PSymbolVariable *var;
if (sym == NULL || sym->SymbolType != SYM_Variable ||
!(var = static_cast<PSymbolVariable *>(sym))->bUserVar)
{
return;
}
if (var->ValueType.Type == VAL_Int)
{
max = 1;
}
else if (var->ValueType.Type == VAL_Array && var->ValueType.BaseType == VAL_Int)
{
max = var->ValueType.size;
}
else
{
return;
}
// Set the value of the specified user variable.
if (index >= 0 && index < max)
{
((int *)(reinterpret_cast<BYTE *>(self) + var->offset))[index] = value;
}
}
static int GetUserVariable(AActor *self, FName varname, int index)
{
PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true);
int max;
PSymbolVariable *var;
if (sym == NULL || sym->SymbolType != SYM_Variable ||
!(var = static_cast<PSymbolVariable *>(sym))->bUserVar)
{
return 0;
}
if (var->ValueType.Type == VAL_Int)
{
max = 1;
}
else if (var->ValueType.Type == VAL_Array && var->ValueType.BaseType == VAL_Int)
{
max = var->ValueType.size;
}
else
{
return 0;
}
// Get the value of the specified user variable.
if (index >= 0 && index < max)
{
return ((int *)(reinterpret_cast<BYTE *>(self) + var->offset))[index];
}
return 0;
}
int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args)
{
AActor *actor;
@ -3109,23 +3172,24 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args)
case ACSF_SetUserVariable:
{
int cnt = 0;
if (args[1] >= 0 && args[1] < 10)
FName varname(FBehavior::StaticLookupString(args[1]), true);
if (varname != NAME_None)
{
if (args[0] == 0)
{
if (activator != NULL)
{
activator->uservar[args[1]] = args[2];
SetUserVariable(activator, varname, 0, args[2]);
}
cnt++;
}
else
{
TActorIterator<AActor> iterator (args[0]);
TActorIterator<AActor> iterator(args[0]);
while ( (actor = iterator.Next ()) )
while ( (actor = iterator.Next()) )
{
actor->uservar[args[1]] = args[2];
SetUserVariable(actor, varname, 0, args[2]);
cnt++;
}
}
@ -3135,12 +3199,52 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args)
case ACSF_GetUserVariable:
{
if (args[1] >= 0 && args[1] < 10)
FName varname(FBehavior::StaticLookupString(args[1]), true);
if (varname != NAME_None)
{
AActor *a = args[0] == 0 ? (AActor *)activator : SingleActorFromTID(args[0], NULL);
return a != NULL? a->uservar[args[1]] : 0;
return a != NULL ? GetUserVariable(a, varname, 0) : 0;
}
else return 0;
return 0;
}
case ACSF_SetUserArray:
{
int cnt = 0;
FName varname(FBehavior::StaticLookupString(args[1]), true);
if (varname != NAME_None)
{
if (args[0] == 0)
{
if (activator != NULL)
{
SetUserVariable(activator, varname, args[2], args[3]);
}
cnt++;
}
else
{
TActorIterator<AActor> iterator(args[0]);
while ( (actor = iterator.Next()) )
{
SetUserVariable(actor, varname, args[2], args[3]);
cnt++;
}
}
}
return cnt;
}
case ACSF_GetUserArray:
{
FName varname(FBehavior::StaticLookupString(args[1]), true);
if (varname != NAME_None)
{
AActor *a = args[0] == 0 ? (AActor *)activator : SingleActorFromTID(args[0], NULL);
return a != NULL ? GetUserVariable(a, varname, args[2]) : 0;
}
return 0;
}
case ACSF_Radius_Quake2:

View file

@ -314,7 +314,13 @@ void AActor::Serialize (FArchive &arc)
arc << DamageFactor;
}
for(int i=0; i<10; i++) arc << uservar[i];
// Skip past uservar array in old savegames
if (SaveVersion < 1933)
{
int foo;
for (int i = 0; i < 10; ++i)
arc << foo;
}
if (arc.IsStoring ())
{

View file

@ -175,6 +175,7 @@ enum
TK_Exec,
TK_DefaultProperties,
TK_Native,
TK_Var,
TK_Out,
TK_Ref,
TK_Event,

View file

@ -109,6 +109,7 @@ std2:
'exec' { RET(TK_Exec); }
'defaultproperties' { RET(TK_DefaultProperties); }
'native' { RET(TK_Native); }
'var' { RET(TK_Var); }
'out' { RET(TK_Out); }
'ref' { RET(TK_Ref); }
'event' { RET(TK_Event); }

View file

@ -1742,6 +1742,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log)
Printf("%s\n", text);
}
//===========================================================================
//
// A_LogInt
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt)
{
ACTION_PARAM_START(1);
ACTION_PARAM_INT(num, 0);
Printf("%d\n", num);
}
//===========================================================================
//
// A_SetTranslucent
@ -2900,21 +2913,63 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecial)
//===========================================================================
//
// A_SetVar
// A_SetUserVar
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar)
{
ACTION_PARAM_START(2);
ACTION_PARAM_INT(pos, 0);
ACTION_PARAM_NAME(varname, 0);
ACTION_PARAM_INT(value, 1);
if (pos < 0 || pos > 9)
PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true);
PSymbolVariable *var;
if (sym == NULL || sym->SymbolType != SYM_Variable ||
!(var = static_cast<PSymbolVariable *>(sym))->bUserVar ||
var->ValueType.Type != VAL_Int)
{
Printf("%s is not a user variable in class %s\n", varname.GetChars(),
self->GetClass()->TypeName.GetChars());
return;
// Set the value of the specified arg
self->uservar[pos] = value;
}
// Set the value of the specified user variable.
*(int *)(reinterpret_cast<BYTE *>(self) + var->offset) = value;
}
//===========================================================================
//
// A_SetUserArray
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray)
{
ACTION_PARAM_START(3);
ACTION_PARAM_NAME(varname, 0);
ACTION_PARAM_INT(pos, 1);
ACTION_PARAM_INT(value, 2);
PSymbol *sym = self->GetClass()->Symbols.FindSymbol(varname, true);
PSymbolVariable *var;
if (sym == NULL || sym->SymbolType != SYM_Variable ||
!(var = static_cast<PSymbolVariable *>(sym))->bUserVar ||
var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int)
{
Printf("%s is not a user array in class %s\n", varname.GetChars(),
self->GetClass()->TypeName.GetChars());
return;
}
if (pos < 0 || pos >= var->ValueType.size)
{
Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(),
self->GetClass()->TypeName.GetChars());
return;
}
// Set the value of the specified user array at index pos.
((int *)(reinterpret_cast<BYTE *>(self) + var->offset))[pos] = value;
}
//===========================================================================

View file

@ -78,7 +78,6 @@ DEFINE_MEMBER_VARIABLE_ALIAS(momy, vely, AActor)
DEFINE_MEMBER_VARIABLE_ALIAS(momz, velz, AActor)
DEFINE_MEMBER_VARIABLE(Damage, AActor)
DEFINE_MEMBER_VARIABLE(Score, AActor)
DEFINE_MEMBER_VARIABLE(uservar, AActor)
//==========================================================================
//

View file

@ -248,13 +248,13 @@ static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls)
//==========================================================================
//
// ActorConstDef
// ParseNativeVariable
//
// Parses a constant definition.
// Parses a native variable declaration.
//
//==========================================================================
static void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls)
static void ParseNativeVariable (FScanner &sc, PSymbolTable * symt, PClass *cls)
{
FExpressionType valuetype;
@ -319,6 +319,7 @@ static void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls)
PSymbolVariable *sym = new PSymbolVariable(symname);
sym->offset = vi->address; // todo
sym->ValueType = valuetype;
sym->bUserVar = false;
if (symt->AddSymbol (sym) == NULL)
{
@ -328,6 +329,70 @@ static void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls)
}
}
//==========================================================================
//
// ParseUserVariable
//
// Parses a user variable declaration.
//
//==========================================================================
static void ParseUserVariable (FScanner &sc, PSymbolTable *symt, PClass *cls)
{
FExpressionType valuetype;
// Only non-native classes may have user variables.
if (!cls->bRuntimeClass)
{
sc.ScriptError("Native classes may not have user variables");
}
// Read the type and make sure it's int.
sc.MustGetAnyToken();
if (sc.TokenType == TK_Int)
{
valuetype = VAL_Int;
}
else
{
sc.ScriptError("User variables must be of type int");
}
sc.MustGetToken(TK_Identifier);
// For now, restrict user variables to those that begin with "user_" to guarantee
// no clashes with internal member variable names.
if (sc.StringLen < 6 || strnicmp("user_", sc.String, 5) != 0)
{
sc.ScriptError("User variable names must begin with \"user_\"");
}
FName symname = sc.String;
if (sc.CheckToken('['))
{
FxExpression *expr = ParseExpression(sc, cls);
int maxelems = expr->EvalExpression(NULL).GetInt();
delete expr;
sc.MustGetToken(']');
if (maxelems <= 0)
{
sc.ScriptError("Array size must be positive");
}
valuetype.MakeArray(maxelems);
}
sc.MustGetToken(';');
PSymbolVariable *sym = new PSymbolVariable(symname);
sym->offset = cls->Extend(sizeof(int) * (valuetype.Type == VAL_Array ? valuetype.size : 1));
sym->ValueType = valuetype;
sym->bUserVar = true;
if (symt->AddSymbol(sym) == NULL)
{
delete sym;
sc.ScriptError ("'%s' is already defined in '%s'.",
symname.GetChars(), cls ? cls->TypeName.GetChars() : "Global");
}
}
//==========================================================================
//
// Parses a flag name
@ -1064,7 +1129,11 @@ static void ParseActor(FScanner &sc)
break;
case TK_Native:
ParseVariable (sc, &info->Class->Symbols, info->Class);
ParseNativeVariable (sc, &info->Class->Symbols, info->Class);
break;
case TK_Var:
ParseUserVariable (sc, &info->Class->Symbols, info->Class);
break;
case TK_Identifier:
@ -1125,7 +1194,7 @@ void ParseDecorate (FScanner &sc)
break;
case TK_Native:
ParseVariable(sc, &GlobalSymbols, NULL);
ParseNativeVariable(sc, &GlobalSymbols, NULL);
break;
case ';':

View file

@ -27,7 +27,6 @@ ACTOR Actor native //: Thinker
native fixed_t alpha;
native angle_t angle;
native int args[5];
native int uservar[10];
native fixed_t ceilingz;
native fixed_t floorz;
native int health;
@ -47,6 +46,7 @@ ACTOR Actor native //: Thinker
native fixed_t momy; // alias for vely
native fixed_t momz; // alias for velz
native int score;
// Meh, MBF redundant functions. Only for DeHackEd support.
action native A_Turn(float angle = 0);
action native A_LineEffect(int boomspecial = 0, int tag = 0);
@ -195,6 +195,7 @@ ACTOR Actor native //: Thinker
action native A_Print(string whattoprint, float time = 0, string fontname = "");
action native A_PrintBold(string whattoprint, float time = 0, string fontname = "");
action native A_Log(string whattoprint);
action native A_LogInt(int whattoprint);
action native A_SetTranslucent(float alpha, int style = 0);
action native A_FadeIn(float reduce = 0.1);
action native A_FadeOut(float reduce = 0.1, bool remove = true);
@ -261,7 +262,8 @@ ACTOR Actor native //: Thinker
action native A_ScaleVelocity(float scale);
action native A_ChangeVelocity(float x = 0, float y = 0, float z = 0, int flags = 0);
action native A_SetArg(int pos, int value);
action native A_SetUserVar(int pos, int value);
action native A_SetUserVar(name varname, int value);
action native A_SetUserArray(name varname, int index, int value);
action native A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0);
action native A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake");