- Added NULL checks to all places where class names are passed as DECORATE

parameters.
- All DECORATE parameters are passed as expressions now. This change allows
  for compile time checks of all class names being used in DECORATE so many
  incorrect definitions may output warnings now.
- Changed DECORATE sound and color parameters to use expressions.
- Changed: S_StopChannel now resets the actor's sound flags. The previous bug
  made me think that delaying this until FMod calls the end of sound callback 
  may simply be too late.


SVN r1276 (trunk)
This commit is contained in:
Christoph Oelckers 2008-10-25 17:38:00 +00:00
parent 4c6b7f6752
commit d753d41752
23 changed files with 1476 additions and 976 deletions

View file

@ -1,8 +1,26 @@
October 25, 2008 (Changes by Graf Zahl)
- Added NULL checks to all places where class names are passed as DECORATE
parameters.
- All DECORATE parameters are passed as expressions now. This change allows
for compile time checks of all class names being used in DECORATE so many
incorrect definitions may output warnings now.
October 23, 2008 (Changes by Graf Zahl) October 23, 2008 (Changes by Graf Zahl)
- Changed: S_StopChannel now resets the actor's sound flags. The previous bug
made me think that delaying this until FMod calls the end of sound callback
may simply be too late.
- Fixed: The high level sound code must not rely on FMod immediately returning - Fixed: The high level sound code must not rely on FMod immediately returning
the sound channel data when a sound is being stopped. This caused the sound channel data when a sound is being stopped. This caused
an endless loop when changing levels with Strife's Flamethrower active. an endless loop when changing levels with Strife's Flamethrower active.
October 22, 2008 (Changes by Graf Zahl)
- Changed DECORATE sound and color parameters to use expressions.
October 21, 2008 (Changes by Graf Zahl)
- Added a proper function parser to the expression evaluator and converted
sin/cos and action specials to use it. The old evaluator is gone now.
- fixed some GCC problems with autosegs.
October 20, 2008 October 20, 2008
- Game time is now frozen during screen wipes. This obsoletes the DEM_WIPEON - Game time is now frozen during screen wipes. This obsoletes the DEM_WIPEON
and DEM_WIPEOFF commands. Fixes multimap demos desyncing when played back and DEM_WIPEOFF commands. Fixes multimap demos desyncing when played back

View file

@ -627,7 +627,6 @@ add_executable( zdoom WIN32
thingdef/thingdef_data.cpp thingdef/thingdef_data.cpp
thingdef/thingdef_exp.cpp thingdef/thingdef_exp.cpp
thingdef/thingdef_expression.cpp thingdef/thingdef_expression.cpp
thingdef/thingdef_main.cpp
thingdef/thingdef_parse.cpp thingdef/thingdef_parse.cpp
thingdef/thingdef_properties.cpp thingdef/thingdef_properties.cpp
thingdef/thingdef_states.cpp thingdef/thingdef_states.cpp

View file

@ -81,6 +81,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StaffAttack)
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire))
return; return;
} }
if (puff == NULL) puff = PClass::FindClass(NAME_BulletPuff); // just to be sure
angle = self->angle; angle = self->angle;
angle += pr_sap.Random2() << 18; angle += pr_sap.Random2() << 18;
slope = P_AimLineAttack (self, angle, MELEERANGE, &linetarget); slope = P_AimLineAttack (self, angle, MELEERANGE, &linetarget);

View file

@ -654,11 +654,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireGrenade)
if (!weapon->DepleteAmmo (weapon->bAltFire)) if (!weapon->DepleteAmmo (weapon->bAltFire))
return; return;
// Make it flash P_SetPsprite (player, ps_flash, flash);
FState *jumpto = P_GetState(weapon, NULL, flash);
P_SetPsprite (player, ps_flash, jumpto);
if (grenadetype != NULL)
{
self->z += 32*FRACUNIT; self->z += 32*FRACUNIT;
grenade = P_SpawnSubMissile (self, grenadetype, self); grenade = P_SpawnSubMissile (self, grenadetype, self);
self->z -= 32*FRACUNIT; self->z -= 32*FRACUNIT;
@ -681,6 +680,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireGrenade)
an >>= ANGLETOFINESHIFT; an >>= ANGLETOFINESHIFT;
grenade->x += FixedMul (finecosine[an], 15*FRACUNIT); grenade->x += FixedMul (finecosine[an], 15*FRACUNIT);
grenade->y += FixedMul (finesine[an], 15*FRACUNIT); grenade->y += FixedMul (finesine[an], 15*FRACUNIT);
}
} }
// The Almighty Sigil! ------------------------------------------------------ // The Almighty Sigil! ------------------------------------------------------

View file

@ -49,7 +49,7 @@
#include "templates.h" #include "templates.h"
#include "cmdlib.h" #include "cmdlib.h"
extern void LoadDecorations (); extern void LoadActors ();
//========================================================================== //==========================================================================
// //
@ -102,8 +102,8 @@ void FActorInfo::StaticInit ()
sprites.Push (temp); sprites.Push (temp);
} }
Printf ("LoadDecorations: Load external actors.\n"); Printf ("LoadActors: Load actor definitions.\n");
LoadDecorations (); LoadActors ();
} }
//========================================================================== //==========================================================================

View file

@ -2232,15 +2232,12 @@ enum ChaseFlags
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Chase) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Chase)
{ {
ACTION_PARAM_START(3); ACTION_PARAM_START(3);
ACTION_PARAM_STATE(i_melee, 0); ACTION_PARAM_STATE(melee, 0);
ACTION_PARAM_STATE(i_missile, 1); ACTION_PARAM_STATE(missile, 1);
ACTION_PARAM_INT(flags, 2); ACTION_PARAM_INT(flags, 2);
if (i_melee != INT_MIN) if (melee != (FState*)-1)
{ {
FState *melee = P_GetState(self, CallingState, i_melee);
FState *missile = P_GetState(self, CallingState, i_missile);
if (flags & CHF_RESURRECT && P_CheckForResurrection(self, false)) return; if (flags & CHF_RESURRECT && P_CheckForResurrection(self, false)) return;
A_DoChase(self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE), A_DoChase(self, !!(flags&CHF_FASTCHASE), melee, missile, !(flags&CHF_NOPLAYACTIVE),

View file

@ -719,9 +719,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LookEx)
ACTION_PARAM_FIXED(maxseedist, 2); ACTION_PARAM_FIXED(maxseedist, 2);
ACTION_PARAM_FIXED(maxheardist, 3); ACTION_PARAM_FIXED(maxheardist, 3);
ACTION_PARAM_ANGLE(fov, 4); ACTION_PARAM_ANGLE(fov, 4);
ACTION_PARAM_STATE(i_state, 5); ACTION_PARAM_STATE(seestate, 5);
FState *seestate = P_GetState(self, CallingState, i_state);
AActor *targ = NULL; // Shuts up gcc AActor *targ = NULL; // Shuts up gcc
fixed_t dist; fixed_t dist;

View file

@ -53,8 +53,6 @@
#define NULL_STATE_INDEX 127 #define NULL_STATE_INDEX 127
TArray<FName> JumpParameters;
//========================================================================== //==========================================================================
// //
// //
@ -210,51 +208,6 @@ bool AActor::HasSpecialDeathStates () const
return false; return false;
} }
//==========================================================================
//
// Resolves a label parameter
//
//==========================================================================
FState *P_GetState(AActor *self, FState *CallingState, int offset)
{
if (offset == 0 || offset == INT_MIN)
{
return NULL; // 0 means 'no state'
}
else if (offset>0)
{
if (CallingState == NULL) return NULL;
return CallingState + offset;
}
else if (self != NULL)
{
FName *params = &JumpParameters[-offset];
FName classname = params[0];
const PClass *cls;
cls = classname==NAME_None? RUNTIME_TYPE(self) : PClass::FindClass(classname);
if (cls==NULL || cls->ActorInfo==NULL) return NULL; // shouldn't happen
int numnames = (int)params[1];
FState *jumpto = cls->ActorInfo->FindState(numnames, &params[2]);
if (jumpto == NULL)
{
const char *dot="";
Printf("Jump target '");
if (classname != NAME_None) Printf("%s::", classname.GetChars());
for (int i=0;i<numnames;i++)
{
Printf("%s%s", dot, params[2+i].GetChars());
dot = ".";
}
Printf("' not found in %s\n", self->GetClass()->TypeName.GetChars());
}
return jumpto;
}
else return NULL;
}
//========================================================================== //==========================================================================
// //
// Creates a list of names from a string. Dots are used as separator // Creates a list of names from a string. Dots are used as separator

View file

@ -1910,6 +1910,13 @@ void S_StopChannel(FSoundChan *chan)
{ {
chan->ChanFlags |= CHAN_FORGETTABLE; chan->ChanFlags |= CHAN_FORGETTABLE;
} }
if (chan->SourceType == SOURCE_Actor && chan->Actor != NULL)
{
chan->Actor->SoundChans &= ~(1 << chan->EntChannel);
chan->Actor = NULL;
}
GSnd->StopChannel(chan); GSnd->StopChannel(chan);
} }
else else

View file

@ -21,6 +21,7 @@
#include "cmdlib.h" #include "cmdlib.h"
#include "m_misc.h" #include "m_misc.h"
#include "templates.h" #include "templates.h"
#include "doomstat.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -1023,3 +1024,91 @@ void FScanner::CheckOpen()
I_FatalError ("SC_ call before SC_Open()."); I_FatalError ("SC_ call before SC_Open().");
} }
} }
//==========================================================================
//
// a class that remembers a parser position
//
//==========================================================================
FScriptPosition::FScriptPosition(const FScriptPosition &other)
{
FileName = other.FileName;
ScriptLine = other.ScriptLine;
}
FScriptPosition::FScriptPosition(FString fname, int line)
{
FileName = fname;
ScriptLine = line;
}
FScriptPosition::FScriptPosition(FScanner &sc)
{
FileName = sc.ScriptName;
ScriptLine = sc.GetMessageLine();
}
FScriptPosition &FScriptPosition::operator=(const FScriptPosition &other)
{
FileName = other.FileName;
ScriptLine = other.ScriptLine;
return *this;
}
//==========================================================================
//
// FScriptPosition::Message
//
//==========================================================================
void STACK_ARGS FScriptPosition::Message (int severity, const char *message, ...) const
{
FString composed;
if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && !developer) return;
if (message == NULL)
{
composed = "Bad syntax.";
}
else
{
va_list arglist;
va_start (arglist, message);
composed.VFormat (message, arglist);
va_end (arglist);
}
const char *type = "";
int level = PRINT_HIGH;
switch (severity)
{
default:
return;
case MSG_WARNING:
type = "warning";
break;
case MSG_ERROR:
type = "error";
break;
case MSG_DEBUG:
type = "message";
break;
case MSG_DEBUGLOG:
case MSG_LOG:
type = "message";
level = PRINT_LOG;
break;
case MSG_FATAL:
I_Error ("Script error, \"%s\" line %d:\n%s\n",
FileName.GetChars(), ScriptLine, composed.GetChars());
}
}

View file

@ -215,4 +215,44 @@ enum
TK_LastToken TK_LastToken
}; };
//==========================================================================
//
//
//
//==========================================================================
enum
{
MSG_WARNING,
MSG_FATAL,
MSG_ERROR,
MSG_DEBUG,
MSG_LOG,
MSG_DEBUGLOG
};
//==========================================================================
//
// a class that remembers a parser position
//
//==========================================================================
struct FScriptPosition
{
FString FileName;
int ScriptLine;
FScriptPosition()
{
ScriptLine=0;
}
FScriptPosition(const FScriptPosition &other);
FScriptPosition(FString fname, int line);
FScriptPosition(FScanner &sc);
FScriptPosition &operator=(const FScriptPosition &other);
void Message(int severity, const char *message,...) const;
};
#endif //__SC_MAN_H__ #endif //__SC_MAN_H__

View file

@ -4,8 +4,8 @@
** Actor definitions ** Actor definitions
** **
**--------------------------------------------------------------------------- **---------------------------------------------------------------------------
** Copyright 2002-2007 Christoph Oelckers ** Copyright 2002-2008 Christoph Oelckers
** Copyright 2004-2007 Randy Heit ** Copyright 2004-2008 Randy Heit
** All rights reserved. ** All rights reserved.
** **
** Redistribution and use in source and binary forms, with or without ** Redistribution and use in source and binary forms, with or without
@ -56,302 +56,29 @@
#include "m_random.h" #include "m_random.h"
#include "i_system.h" #include "i_system.h"
#include "p_local.h" #include "p_local.h"
#include "v_palette.h"
#include "doomerrors.h" #include "doomerrors.h"
#include "a_hexenglobal.h" #include "a_hexenglobal.h"
#include "a_weaponpiece.h" #include "a_weaponpiece.h"
#include "p_conversation.h" #include "p_conversation.h"
#include "v_text.h" #include "v_text.h"
#include "thingdef.h" #include "thingdef.h"
#include "thingdef_exp.h"
#include "a_sharedglobal.h" #include "a_sharedglobal.h"
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void InitThingdef();
void ParseDecorate (FScanner &sc);
// STATIC FUNCTION PROTOTYPES --------------------------------------------
const PClass *QuestItemClasses[31]; const PClass *QuestItemClasses[31];
PSymbolTable GlobalSymbols;
//==========================================================================
//
// ParseParameter
//
// Parses aparameter - either a default in a function declaration
// or an argument in a function call.
//
//==========================================================================
int ParseParameter(FScanner &sc, PClass *cls, char type, bool constant)
{
int v;
switch(type)
{
case 'S':
case 's': // Sound name
sc.MustGetString();
return S_FindSound(sc.String);
case 'M':
case 'm': // Actor name
case 'T':
case 't': // String
sc.SetEscape(true);
sc.MustGetString();
sc.SetEscape(false);
return (int)(sc.String[0] ? FName(sc.String) : NAME_None);
case 'C':
case 'c': // Color
sc.MustGetString ();
if (sc.Compare("none"))
{
return -1;
}
else if (sc.Compare(""))
{
return 0;
}
else
{
int c = V_GetColor (NULL, sc.String);
// 0 needs to be the default so we have to mark the color.
return MAKEARGB(1, RPART(c), GPART(c), BPART(c));
}
case 'L':
case 'l':
{
if (JumpParameters.Size()==0) JumpParameters.Push(NAME_None);
v = -(int)JumpParameters.Size();
// This forces quotation marks around the state name.
sc.MustGetToken(TK_StringConst);
if (sc.String[0] == 0 || sc.Compare("None"))
{
return 0;
}
if (sc.Compare("*"))
{
if (constant) return INT_MIN;
else sc.ScriptError("Invalid state name '*'");
}
FString statestring = sc.String; // ParseStateString(sc);
const PClass *stype=NULL;
int scope = statestring.IndexOf("::");
if (scope >= 0)
{
FName scopename = FName(statestring, scope, false);
if (scopename == NAME_Super)
{
// Super refers to the direct superclass
scopename = cls->ParentClass->TypeName;
}
JumpParameters.Push(scopename);
statestring = statestring.Right(statestring.Len()-scope-2);
stype = PClass::FindClass (scopename);
if (stype == NULL)
{
sc.ScriptError ("%s is an unknown class.", scopename.GetChars());
}
if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor)))
{
sc.ScriptError ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars());
}
if (!stype->IsAncestorOf (cls))
{
sc.ScriptError ("%s is not derived from %s so cannot access its states.",
cls->TypeName.GetChars(), stype->TypeName.GetChars());
}
}
else
{
// No class name is stored. This allows 'virtual' jumps to
// labels in subclasses.
// It also means that the validity of the given state cannot
// be checked here.
JumpParameters.Push(NAME_None);
}
TArray<FName> &names = MakeStateNameList(statestring);
if (stype != NULL)
{
if (!stype->ActorInfo->FindState(names.Size(), &names[0]))
{
sc.ScriptError("Jump to unknown state '%s' in class '%s'",
statestring.GetChars(), stype->TypeName.GetChars());
}
}
JumpParameters.Push((ENamedName)names.Size());
for(unsigned i=0;i<names.Size();i++)
{
JumpParameters.Push(names[i]);
}
// No offsets here. The point of jumping to labels is to avoid such things!
return v;
}
case 'X':
case 'x':
v = ParseExpression (sc, false, cls);
if (constant && !IsExpressionConst(v))
{
sc.ScriptError("Default parameter must be constant.");
}
return v;
default:
assert(false);
return -1;
}
}
//==========================================================================
//
// ActorActionDef
//
// Parses an action function definition. A lot of this is essentially
// documentation in the declaration for when I have a proper language
// ready.
//
//==========================================================================
static void ParseActionDef (FScanner &sc, PClass *cls)
{
enum
{
OPTIONAL = 1
};
AFuncDesc *afd;
FName funcname;
FString args;
TArray<int> DefaultParams;
bool hasdefaults = false;
if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0)
{
sc.ScriptError ("action functions can only be imported by internal class and actor definitions!");
}
sc.MustGetToken(TK_Native);
sc.MustGetToken(TK_Identifier);
funcname = sc.String;
afd = FindFunction(sc.String);
if (afd == NULL)
{
sc.ScriptError ("The function '%s' has not been exported from the executable.", sc.String);
}
sc.MustGetToken('(');
if (!sc.CheckToken(')'))
{
while (sc.TokenType != ')')
{
int flags = 0;
char type = '@';
// Retrieve flags before type name
for (;;)
{
if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native))
{
}
else
{
break;
}
}
// Read the variable type
sc.MustGetAnyToken();
switch (sc.TokenType)
{
case TK_Bool:
case TK_Int:
case TK_Float:
type = 'x';
break;
case TK_Sound: type = 's'; break;
case TK_String: type = 't'; break;
case TK_Name: type = 't'; break;
case TK_State: type = 'l'; break;
case TK_Color: type = 'c'; break;
case TK_Class:
sc.MustGetToken('<');
sc.MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care
sc.MustGetToken('>');
type = 'm';
break;
case TK_Ellipsis:
type = '+';
sc.MustGetToken(')');
sc.UnGet();
break;
default:
sc.ScriptError ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars());
break;
}
// Read the optional variable name
if (!sc.CheckToken(',') && !sc.CheckToken(')'))
{
sc.MustGetToken(TK_Identifier);
}
else
{
sc.UnGet();
}
int def;
if (sc.CheckToken('='))
{
hasdefaults = true;
flags|=OPTIONAL;
def = ParseParameter(sc, cls, type, true);
}
else
{
def = 0;
}
DefaultParams.Push(def);
if (!(flags & OPTIONAL) && type != '+')
{
type -= 'a' - 'A';
}
args += type;
sc.MustGetAnyToken();
if (sc.TokenType != ',' && sc.TokenType != ')')
{
sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars());
}
}
}
sc.MustGetToken(';');
PSymbolActionFunction *sym = new PSymbolActionFunction(funcname);
sym->Arguments = args;
sym->Function = afd->Function;
if (hasdefaults)
{
sym->defaultparameterindex = StateParameters.Size();
for(unsigned int i = 0; i < DefaultParams.Size(); i++)
StateParameters.Push(DefaultParams[i]);
}
else
{
sym->defaultparameterindex = -1;
}
if (cls->Symbols.AddSymbol (sym) == NULL)
{
delete sym;
sc.ScriptError ("'%s' is already defined in class '%s'.",
funcname.GetChars(), cls->TypeName.GetChars());
}
}
//========================================================================== //==========================================================================
// //
// Starts a new actor definition // Starts a new actor definition
// //
//========================================================================== //==========================================================================
static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replaceName, FActorInfo *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, FName replaceName,
int DoomEdNum, bool native) int DoomEdNum, bool native)
{ {
const PClass *replacee = NULL; const PClass *replacee = NULL;
@ -366,15 +93,15 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac
if (parent == NULL) if (parent == NULL)
{ {
I_Error ("Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars()); sc.Message(MSG_FATAL, "Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars());
} }
else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor))) else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor)))
{ {
I_Error ("Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars()); sc.Message(MSG_FATAL, "Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars());
} }
else if (parent->ActorInfo == NULL) else if (parent->ActorInfo == NULL)
{ {
I_Error ("uninitialized parent type '%s' in %s", parentName.GetChars(), typeName.GetChars()); sc.Message(MSG_FATAL, "uninitialized parent type '%s' in %s", parentName.GetChars(), typeName.GetChars());
} }
} }
@ -386,11 +113,11 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac
if (replacee == NULL) if (replacee == NULL)
{ {
I_Error ("Replaced type '%s' not found in %s", replaceName.GetChars(), typeName.GetChars()); sc.Message(MSG_FATAL, "Replaced type '%s' not found in %s", replaceName.GetChars(), typeName.GetChars());
} }
else if (replacee->ActorInfo == NULL) else if (replacee->ActorInfo == NULL)
{ {
I_Error ("Replaced type '%s' is not an actor in %s", replaceName.GetChars(), typeName.GetChars()); sc.Message(MSG_FATAL, "Replaced type '%s' is not an actor in %s", replaceName.GetChars(), typeName.GetChars());
} }
} }
@ -399,15 +126,15 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac
ti = (PClass*)PClass::FindClass(typeName); ti = (PClass*)PClass::FindClass(typeName);
if (ti == NULL) if (ti == NULL)
{ {
I_Error("Unknown native class '%s'", typeName.GetChars()); sc.Message(MSG_FATAL, "Unknown native class '%s'", typeName.GetChars());
} }
else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass()) else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass())
{ {
I_Error("Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars()); sc.Message(MSG_FATAL, "Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars());
} }
else if (ti->ActorInfo != NULL) else if (ti->ActorInfo != NULL)
{ {
I_Error("Redefinition of internal class '%s'", typeName.GetChars()); sc.Message(MSG_FATAL, "Redefinition of internal class '%s'", typeName.GetChars());
} }
ti->InitializeActorInfo(); ti->InitializeActorInfo();
info = ti->ActorInfo; info = ti->ActorInfo;
@ -444,145 +171,43 @@ static FActorInfo *CreateNewActor(FName typeName, FName parentName, FName replac
//========================================================================== //==========================================================================
// //
// Starts a new actor definition // Finalizes an actor definition
// //
//========================================================================== //==========================================================================
static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag)
void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag)
{ {
FName typeName; AActor *defaults = (AActor*)info->Class->Defaults;
FName parentName;
FName replaceName;
bool native = false;
int DoomEdNum = -1;
// Get actor name
sc.MustGetString();
char *colon = strchr(sc.String, ':');
if (colon != NULL)
{
*colon++ = 0;
}
typeName = sc.String;
// Do some tweaking so that a definition like 'Actor:Parent' which is read as a single token is recognized as well
// without having resort to C-mode (which disallows periods in actor names.)
if (colon == NULL)
{
sc.MustGetString ();
if (sc.String[0]==':')
{
colon = sc.String + 1;
}
}
if (colon != NULL)
{
if (colon[0] == 0)
{
sc.MustGetString ();
colon = sc.String;
}
}
if (colon == NULL)
{
sc.UnGet();
}
parentName = colon;
// Check for "replaces"
if (sc.CheckString ("replaces"))
{
// Get actor name
sc.MustGetString ();
replaceName = sc.String;
}
// Now, after the actor names have been parsed, it is time to switch to C-mode
// for the rest of the actor definition.
sc.SetCMode (true);
if (sc.CheckNumber())
{
if (sc.Number>=-1 && sc.Number<32768) DoomEdNum = sc.Number;
else sc.ScriptError ("DoomEdNum must be in the range [-1,32767]");
}
if (sc.CheckString("native"))
{
native = true;
}
try try
{ {
FActorInfo *info = CreateNewActor(typeName, parentName, replaceName, DoomEdNum, native); bag.statedef.FinishStates (info, defaults, bag.StateArray);
ResetBaggage (bag, info->Class->ParentClass);
bag->Info = info;
bag->Lumpnum = sc.LumpNum;
#ifdef _DEBUG
bag->ClassName = typeName;
#endif
return info;
} }
catch (CRecoverableError &err) catch (CRecoverableError &err)
{ {
sc.ScriptError("%s", err.GetMessage()); sc.Message(MSG_FATAL, "%s", err.GetMessage());
return NULL;
} }
} bag.statedef.InstallStates (info, defaults);
bag.StateArray.Clear ();
//========================================================================== if (bag.DropItemSet)
//
// Reads an actor definition
//
//==========================================================================
void ParseActor(FScanner &sc)
{
FActorInfo * info=NULL;
Baggage bag;
info = ParseActorHeader(sc, &bag);
sc.MustGetToken('{');
while (sc.MustGetAnyToken(), sc.TokenType != '}')
{ {
switch (sc.TokenType) if (bag.DropItemList == NULL)
{ {
case TK_Action: if (info->Class->Meta.GetMetaInt (ACMETA_DropItems) != 0)
ParseActionDef (sc, info->Class); {
break; info->Class->Meta.SetMetaInt (ACMETA_DropItems, 0);
case TK_Const:
ParseConstant (sc, &info->Class->Symbols, info->Class);
break;
case TK_Enum:
ParseEnum (sc, &info->Class->Symbols, info->Class);
break;
case TK_Native:
ParseVariable (sc, &info->Class->Symbols, info->Class);
break;
case TK_Identifier:
// other identifier related checks here
case TK_Projectile: // special case: both keyword and property name
ParseActorProperty(sc, bag);
break;
case '+':
case '-':
ParseActorFlag(sc, bag, sc.TokenType);
break;
default:
sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars());
break;
} }
} }
FinishActor(sc, info, bag); else
sc.SetCMode (false); {
info->Class->Meta.SetMetaInt (ACMETA_DropItems,
StoreDropItemChain(bag.DropItemList));
}
}
if (info->Class->IsDescendantOf (RUNTIME_CLASS(AInventory)))
{
defaults->flags |= MF_SPECIAL;
}
} }
//========================================================================== //==========================================================================
@ -617,10 +242,12 @@ static int ResolvePointer(const PClass **pPtr, const PClass *owner, const PClass
} }
void FinishThingdef() static void FinishThingdef()
{ {
unsigned int i; unsigned int i;
int errorcount=0; int errorcount;
errorcount = StateParams.ResolveAll();
for (i = 0;i < PClass::m_Types.Size(); i++) for (i = 0;i < PClass::m_Types.Size(); i++)
{ {
@ -747,3 +374,27 @@ void FinishThingdef()
} }
//==========================================================================
//
// LoadActors
//
// Called from FActor::StaticInit()
//
//==========================================================================
void LoadActors ()
{
int lastlump, lump;
InitThingdef();
lastlump = 0;
while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1)
{
FScanner sc(lump);
ParseDecorate (sc);
}
FinishThingdef();
}

View file

@ -3,6 +3,8 @@
#include "doomtype.h" #include "doomtype.h"
#include "info.h" #include "info.h"
#include "s_sound.h"
#include "sc_man.h"
class FScanner; class FScanner;
@ -55,9 +57,7 @@ public:
// State parser // State parser
// //
//========================================================================== //==========================================================================
class FxExpression;
extern TArray<int> StateParameters;
extern TArray<FName> JumpParameters;
struct FStateLabels; struct FStateLabels;
@ -112,6 +112,38 @@ public:
}; };
//==========================================================================
//
//
//
//==========================================================================
struct FStateExpression
{
FxExpression *expr;
const PClass *owner;
bool constant;
bool cloned;
};
class FStateExpressions
{
TArray<FStateExpression> expressions;
public:
~FStateExpressions();
int Add(FxExpression *x, const PClass *o, bool c);
int Reserve(int num, const PClass *cls);
void Set(int num, FxExpression *x);
void Copy(int dest, int src, int cnt);
int ResolveAll();
FxExpression *Get(int no);
int Size() { return expressions.Size(); }
};
extern FStateExpressions StateParams;
//========================================================================== //==========================================================================
// //
// Extra info maintained while defining an actor. // Extra info maintained while defining an actor.
@ -161,7 +193,6 @@ AFuncDesc * FindFunction(const char * string);
FState *P_GetState(AActor *self, FState *CallingState, int offset);
int ParseStates(FScanner &sc, FActorInfo *actor, AActor *defaults, Baggage &bag); int ParseStates(FScanner &sc, FActorInfo *actor, AActor *defaults, Baggage &bag);
PSymbolActionFunction *FindGlobalActionFunction(const char *name); PSymbolActionFunction *FindGlobalActionFunction(const char *name);
@ -172,23 +203,13 @@ PSymbolActionFunction *FindGlobalActionFunction(const char *name);
// //
//========================================================================== //==========================================================================
void ParseActorProperty(FScanner &sc, Baggage &bag); FActorInfo *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, FName replaceName,
int DoomEdNum, bool native);
void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod); void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod);
void ParseActorFlag (FScanner &sc, Baggage &bag, int mod); void FinishActor(const FScriptPosition &sc, FActorInfo *info, Baggage &bag);
void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag); FxExpression *ParseParameter(FScanner &sc, PClass *cls, char type, bool constant);
void ParseConstant (FScanner &sc, PSymbolTable *symt, PClass *cls);
void ParseVariable (FScanner &sc, PSymbolTable *symt, PClass *cls);
void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls);
int ParseParameter(FScanner &sc, PClass *cls, char type, bool constant);
int ParseExpression (FScanner &sc, bool _not, PClass *cls);
bool IsExpressionConst(int id);
int EvalExpressionI (int id, AActor *self);
double EvalExpressionF (int id, AActor *self);
fixed_t EvalExpressionFix (int id, AActor *self);
enum enum
{ {
@ -360,40 +381,45 @@ struct StateCallData
MSVC_ASEG AFuncDesc *infoptr_##cls##_##name GCC_ASEG = &info_##cls##_##name; \ MSVC_ASEG AFuncDesc *infoptr_##cls##_##name GCC_ASEG = &info_##cls##_##name; \
void AFP_##name (AActor *self, FState *CallingState, int ParameterIndex, StateCallData *statecall) void AFP_##name (AActor *self, FState *CallingState, int ParameterIndex, StateCallData *statecall)
#define DECLARE_PARAMINFO FState *CallingState, int ParameterIndex, StateCallData *statecall #define DECLARE_PARAMINFO AActor *self, FState *CallingState, int ParameterIndex, StateCallData *statecall
#define PUSH_PARAMINFO CallingState, ParameterIndex, statecall #define PUSH_PARAMINFO self, CallingState, ParameterIndex, statecall
#define CALL_ACTION(name,self) AF_##name(self, NULL, 0, NULL) #define CALL_ACTION(name,self) AF_##name(self, NULL, 0, NULL)
int EvalExpressionI (DWORD x, AActor *self);
int EvalExpressionCol (DWORD x, AActor *self);
FSoundID EvalExpressionSnd (DWORD x, AActor *self);
double EvalExpressionF (DWORD x, AActor *self);
fixed_t EvalExpressionFix (DWORD x, AActor *self);
FState *EvalExpressionState (DWORD x, AActor *self);
const PClass *EvalExpressionClass (DWORD x, AActor *self);
FName EvalExpressionName (DWORD x, AActor *self);
#define ACTION_PARAM_START(count) #define ACTION_PARAM_START(count)
#define ACTION_PARAM_CONST(var, i) \
int var = StateParameters[ParameterIndex+i];
#define ACTION_PARAM_INT(var, i) \ #define ACTION_PARAM_INT(var, i) \
int var = EvalExpressionI(StateParameters[ParameterIndex+i], self); int var = EvalExpressionI(ParameterIndex+i, self);
#define ACTION_PARAM_BOOL(var,i) \ #define ACTION_PARAM_BOOL(var,i) \
bool var = !!EvalExpressionI(StateParameters[ParameterIndex+i], self); bool var = !!EvalExpressionI(ParameterIndex+i, self);
#define ACTION_PARAM_FIXED(var,i) \ #define ACTION_PARAM_FIXED(var,i) \
fixed_t var = EvalExpressionFix(StateParameters[ParameterIndex+i], self); fixed_t var = EvalExpressionFix(ParameterIndex+i, self);
#define ACTION_PARAM_FLOAT(var,i) \ #define ACTION_PARAM_FLOAT(var,i) \
float var = EvalExpressionF(StateParameters[ParameterIndex+i], self); float var = EvalExpressionF(ParameterIndex+i, self);
#define ACTION_PARAM_CLASS(var,i) \ #define ACTION_PARAM_CLASS(var,i) \
const PClass *var = PClass::FindClass(ENamedName(StateParameters[ParameterIndex+i])); const PClass *var = EvalExpressionClass(ParameterIndex+i, self);
#define ACTION_PARAM_STATE(var,i) \ #define ACTION_PARAM_STATE(var,i) \
int var = StateParameters[ParameterIndex+i]; FState *var = EvalExpressionState(ParameterIndex+i, self);
#define ACTION_PARAM_COLOR(var,i) \ #define ACTION_PARAM_COLOR(var,i) \
PalEntry var = StateParameters[ParameterIndex+i]; PalEntry var = EvalExpressionCol(ParameterIndex+i, self);
#define ACTION_PARAM_SOUND(var,i) \ #define ACTION_PARAM_SOUND(var,i) \
FSoundID var = StateParameters[ParameterIndex+i]; FSoundID var = EvalExpressionSnd(ParameterIndex+i, self);
#define ACTION_PARAM_STRING(var,i) \ #define ACTION_PARAM_STRING(var,i) \
const char *var = FName(ENamedName(StateParameters[ParameterIndex+i])); const char *var = EvalExpressionName(ParameterIndex+i, self);
#define ACTION_PARAM_NAME(var,i) \ #define ACTION_PARAM_NAME(var,i) \
FName var = ENamedName(StateParameters[ParameterIndex+i]); FName var = EvalExpressionName(ParameterIndex+i, self);
#define ACTION_PARAM_VARARG(var, i) \
int *var = &StateParameters[ParameterIndex+i];
#define ACTION_PARAM_ANGLE(var,i) \ #define ACTION_PARAM_ANGLE(var,i) \
angle_t var = angle_t(EvalExpressionF(StateParameters[ParameterIndex+i], self)*ANGLE_90/90.f); angle_t var = angle_t(EvalExpressionF(ParameterIndex+i, self)*ANGLE_90/90.f);
#define ACTION_SET_RESULT(v) if (statecall != NULL) statecall->Result = v; #define ACTION_SET_RESULT(v) if (statecall != NULL) statecall->Result = v;

View file

@ -232,6 +232,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BasicAttack)
ACTION_PARAM_CLASS(MissileType, 2); ACTION_PARAM_CLASS(MissileType, 2);
ACTION_PARAM_FIXED(MissileHeight, 3); ACTION_PARAM_FIXED(MissileHeight, 3);
if (MissileType == NULL) return;
DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight); DoAttack(self, true, true, MeleeDamage, MeleeSound, MissileType, MissileHeight);
} }
@ -359,31 +360,24 @@ DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack)
// Do the state jump // Do the state jump
// //
//========================================================================== //==========================================================================
static void DoJump(AActor * self, FState * CallingState, int offset, StateCallData *statecall) static void DoJump(AActor * self, FState * CallingState, FState *jumpto, StateCallData *statecall)
{ {
if (jumpto == NULL) return;
if (statecall != NULL) if (statecall != NULL)
{ {
FState *jumpto = P_GetState(statecall->Item, CallingState, offset);
if (jumpto == NULL) return;
statecall->State = jumpto; statecall->State = jumpto;
} }
else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state) else if (self->player != NULL && CallingState == self->player->psprites[ps_weapon].state)
{ {
FState *jumpto = P_GetState(self->player->ReadyWeapon, CallingState, offset);
if (jumpto == NULL) return;
P_SetPsprite(self->player, ps_weapon, jumpto); P_SetPsprite(self->player, ps_weapon, jumpto);
} }
else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state) else if (self->player != NULL && CallingState == self->player->psprites[ps_flash].state)
{ {
FState *jumpto = P_GetState(self->player->ReadyWeapon, CallingState, offset);
if (jumpto == NULL) return;
P_SetPsprite(self->player, ps_flash, jumpto); P_SetPsprite(self->player, ps_flash, jumpto);
} }
else if (CallingState == self->state) else if (CallingState == self->state)
{ {
FState *jumpto = P_GetState(self, CallingState, offset);
if (jumpto == NULL) return;
self->SetState (jumpto); self->SetState (jumpto);
} }
else else
@ -405,20 +399,14 @@ static void DoJump(AActor * self, FState * CallingState, int offset, StateCallDa
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump)
{ {
ACTION_PARAM_START(3); ACTION_PARAM_START(3);
ACTION_PARAM_CONST(count, 0); ACTION_PARAM_INT(count, 0);
ACTION_PARAM_INT(maxchance, 1); ACTION_PARAM_INT(maxchance, 1);
ACTION_PARAM_VARARG(jumps, 2);
if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance)) if (count >= 2 && (maxchance >= 256 || pr_cajump() < maxchance))
{ {
if (count == 2) int jumps = 2 + (count == 2? 0 : (pr_cajump() % (count - 1)));
{ ACTION_PARAM_STATE(jumpto, jumps);
ACTION_JUMP(*jumps); ACTION_JUMP(jumpto);
}
else
{
ACTION_JUMP(jumps[(pr_cajump() % (count - 1))]);
}
} }
ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains!
} }
@ -482,7 +470,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser)
// State jump function // State jump function
// //
//========================================================================== //==========================================================================
void DoJumpIfInventory(AActor * self, AActor * owner, DECLARE_PARAMINFO) void DoJumpIfInventory(AActor * owner, DECLARE_PARAMINFO)
{ {
ACTION_PARAM_START(3); ACTION_PARAM_START(3);
ACTION_PARAM_CLASS(Type, 0); ACTION_PARAM_CLASS(Type, 0);
@ -504,12 +492,12 @@ void DoJumpIfInventory(AActor * self, AActor * owner, DECLARE_PARAMINFO)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory)
{ {
DoJumpIfInventory(self, self, PUSH_PARAMINFO); DoJumpIfInventory(self, PUSH_PARAMINFO);
} }
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory)
{ {
DoJumpIfInventory(self, self->target, PUSH_PARAMINFO); DoJumpIfInventory(self->target, PUSH_PARAMINFO);
} }
//========================================================================== //==========================================================================
@ -1143,7 +1131,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun)
// //
//=========================================================================== //===========================================================================
static void DoGiveInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO) static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO)
{ {
ACTION_PARAM_START(2); ACTION_PARAM_START(2);
ACTION_PARAM_CLASS(mi, 0); ACTION_PARAM_CLASS(mi, 0);
@ -1184,12 +1172,12 @@ static void DoGiveInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory)
{ {
DoGiveInventory(self, self, PUSH_PARAMINFO); DoGiveInventory(self, PUSH_PARAMINFO);
} }
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget)
{ {
DoGiveInventory(self, self->target, PUSH_PARAMINFO); DoGiveInventory(self->target, PUSH_PARAMINFO);
} }
//=========================================================================== //===========================================================================
@ -1198,13 +1186,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget)
// //
//=========================================================================== //===========================================================================
void DoTakeInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO) void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO)
{ {
ACTION_PARAM_START(2); ACTION_PARAM_START(2);
ACTION_PARAM_CLASS(item, 0); ACTION_PARAM_CLASS(item, 0);
ACTION_PARAM_INT(amount, 1); ACTION_PARAM_INT(amount, 1);
if (receiver == NULL) return; if (item == NULL || receiver == NULL) return;
bool res = false; bool res = false;
@ -1228,12 +1216,12 @@ void DoTakeInventory(AActor * self, AActor * receiver, DECLARE_PARAMINFO)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory)
{ {
DoTakeInventory(self, self, PUSH_PARAMINFO); DoTakeInventory(self, PUSH_PARAMINFO);
} }
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget)
{ {
DoTakeInventory(self, self->target, PUSH_PARAMINFO); DoTakeInventory(self->target, PUSH_PARAMINFO);
} }
//=========================================================================== //===========================================================================
@ -1463,6 +1451,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ThrowGrenade)
ACTION_PARAM_FIXED(zmom, 3); ACTION_PARAM_FIXED(zmom, 3);
ACTION_PARAM_BOOL(useammo, 4); ACTION_PARAM_BOOL(useammo, 4);
if (missile == NULL) return;
if (ACTION_CALL_FROM_WEAPON()) if (ACTION_CALL_FROM_WEAPON())
{ {
// Used from a weapon so use some ammo // Used from a weapon so use some ammo
@ -1529,7 +1519,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SelectWeapon)
ACTION_PARAM_START(1); ACTION_PARAM_START(1);
ACTION_PARAM_CLASS(cls, 0); ACTION_PARAM_CLASS(cls, 0);
if (self->player == NULL) return; if (cls == NULL || self->player == NULL)
{
ACTION_SET_RESULT(false);
return;
}
AWeapon * weaponitem = static_cast<AWeapon*>(self->FindInventory(cls)); AWeapon * weaponitem = static_cast<AWeapon*>(self->FindInventory(cls));
@ -1714,11 +1708,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropInventory)
ACTION_PARAM_START(1); ACTION_PARAM_START(1);
ACTION_PARAM_CLASS(drop, 0); ACTION_PARAM_CLASS(drop, 0);
if (drop)
{
AInventory * inv = self->FindInventory(drop); AInventory * inv = self->FindInventory(drop);
if (inv) if (inv)
{ {
self->DropInventory(inv); self->DropInventory(inv);
} }
}
} }

View file

@ -84,11 +84,6 @@ FxExpression *ParseExpression (FScanner &sc, PClass *cls)
return data; return data;
} }
int ParseExpression (FScanner &sc, bool _not, PClass *cls)
{
return AddExpression(ParseExpression (sc, cls));
}
static FxExpression *ParseExpressionM (FScanner &sc, const PClass *cls) static FxExpression *ParseExpressionM (FScanner &sc, const PClass *cls)
{ {
FxExpression *condition = ParseExpressionL (sc, cls); FxExpression *condition = ParseExpressionL (sc, cls);

View file

@ -56,65 +56,11 @@ extern PSymbolTable GlobalSymbols;
// //
//========================================================================== //==========================================================================
enum
{
MSG_WARNING,
MSG_ERROR,
MSG_DEBUG,
MSG_LOG,
MSG_DEBUGLOG
};
//==========================================================================
//
//
//
//==========================================================================
struct FScriptPosition
{
FString FileName;
int ScriptLine;
FScriptPosition()
{
ScriptLine=0;
}
FScriptPosition(const FScriptPosition &other)
{
FileName = other.FileName;
ScriptLine = other.ScriptLine;
}
FScriptPosition(FString fname, int line)
{
FileName = fname;
ScriptLine = line;
}
FScriptPosition(FScanner &sc)
{
FileName = sc.ScriptName;
ScriptLine = sc.GetMessageLine();
}
FScriptPosition &operator=(const FScriptPosition &other)
{
FileName = other.FileName;
ScriptLine = other.ScriptLine;
return *this;
}
void Message(int severity, const char *message,...) const;
};
//==========================================================================
//
//
//
//==========================================================================
struct FCompileContext struct FCompileContext
{ {
const PClass *cls; const PClass *cls;
bool lax; bool lax;
bool isconst;
PSymbol *FindInClass(FName identifier) PSymbol *FindInClass(FName identifier)
@ -143,26 +89,51 @@ struct ExpVal
void *pointer; void *pointer;
}; };
int GetInt() int GetInt() const
{ {
return Type == VAL_Int? Int : Type == VAL_Float? int(Float) : 0; return Type == VAL_Int? Int : Type == VAL_Float? int(Float) : 0;
} }
double GetFloat() double GetFloat() const
{ {
return Type == VAL_Int? double(Int) : Type == VAL_Float? Float : 0; return Type == VAL_Int? double(Int) : Type == VAL_Float? Float : 0;
} }
bool GetBool() bool GetBool() const
{ {
return Type == VAL_Int? !!Int : Type == VAL_Float? Float!=0. : false; return (Type == VAL_Int || Type == VAL_Sound) ? !!Int : Type == VAL_Float? Float!=0. : false;
} }
template<class T> T *GetPointer() template<class T> T *GetPointer() const
{ {
return Type == VAL_Object || Type == VAL_Pointer? (T*)pointer : NULL; return Type == VAL_Object || Type == VAL_Pointer? (T*)pointer : NULL;
} }
FSoundID GetSoundID() const
{
return Type == VAL_Sound? Int : 0;
}
int GetColor() const
{
return Type == VAL_Color? Int : 0;
}
FName GetName() const
{
return Type == VAL_Name? ENamedName(Int) : NAME_None;
}
FState *GetState() const
{
return Type == VAL_State? (FState*)pointer : NULL;
}
const PClass *GetClass() const
{
return Type == VAL_Class? (const PClass *)pointer : NULL;
}
}; };
@ -172,7 +143,7 @@ struct ExpVal
// //
//========================================================================== //==========================================================================
struct FxExpression class FxExpression
{ {
protected: protected:
FxExpression(const FScriptPosition &pos) FxExpression(const FScriptPosition &pos)
@ -181,12 +152,9 @@ protected:
ScriptPosition = pos; ScriptPosition = pos;
} }
public: public:
virtual ~FxExpression() {}
virtual FxExpression *Resolve(FCompileContext &ctx); virtual FxExpression *Resolve(FCompileContext &ctx);
FxExpression *ResolveAsBoolean(FCompileContext &ctx) FxExpression *ResolveAsBoolean(FCompileContext &ctx);
{
// This will need more handling if other types than Int and Float are added
return Resolve(ctx);
}
virtual ExpVal EvalExpression (AActor *self); virtual ExpVal EvalExpression (AActor *self);
virtual bool isConstant() const; virtual bool isConstant() const;
@ -194,7 +162,7 @@ public:
FScriptPosition ScriptPosition; FScriptPosition ScriptPosition;
FExpressionType ValueType; FExpressionType ValueType;
protected:
bool isresolved; bool isresolved;
}; };
@ -263,18 +231,50 @@ public:
{ {
ValueType = value.Type = VAL_Int; ValueType = value.Type = VAL_Int;
value.Int = val; value.Int = val;
isresolved = true;
} }
FxConstant(double val, const FScriptPosition &pos) : FxExpression(pos) FxConstant(double val, const FScriptPosition &pos) : FxExpression(pos)
{ {
ValueType = value.Type = VAL_Float; ValueType = value.Type = VAL_Float;
value.Float = val; value.Float = val;
isresolved = true;
}
FxConstant(FSoundID val, const FScriptPosition &pos) : FxExpression(pos)
{
ValueType = value.Type = VAL_Sound;
value.Int = val;
isresolved = true;
}
FxConstant(FName val, const FScriptPosition &pos) : FxExpression(pos)
{
ValueType = value.Type = VAL_Name;
value.Int = val;
isresolved = true;
} }
FxConstant(ExpVal cv, const FScriptPosition &pos) : FxExpression(pos) FxConstant(ExpVal cv, const FScriptPosition &pos) : FxExpression(pos)
{ {
value = cv; value = cv;
ValueType = cv.Type; ValueType = cv.Type;
isresolved = true;
}
FxConstant(const PClass *val, const FScriptPosition &pos) : FxExpression(pos)
{
value.pointer = (void*)val;
ValueType = val;
value.Type = VAL_Class;
isresolved = true;
}
FxConstant(FState *state, const FScriptPosition &pos) : FxExpression(pos)
{
value.pointer = state;
ValueType = value.Type = VAL_State;
isresolved = true;
} }
static FxExpression *MakeConstant(PSymbol *sym, const FScriptPosition &pos); static FxExpression *MakeConstant(PSymbol *sym, const FScriptPosition &pos);
@ -704,10 +704,64 @@ public:
}; };
//==========================================================================
//
//
//
//==========================================================================
class FxClassTypeCast : public FxExpression
{
const PClass *desttype;
FxExpression *basex;
public:
FxClassTypeCast(const PClass *dtype, FxExpression *x);
~FxClassTypeCast();
FxExpression *Resolve(FCompileContext&);
ExpVal EvalExpression (AActor *self);
};
//==========================================================================
//
// Only used to resolve the old jump by index feature of DECORATE
//
//==========================================================================
class FxStateByIndex : public FxExpression
{
int index;
public:
FxStateByIndex(int i, const FScriptPosition &pos) : FxExpression(pos)
{
index = i;
}
FxExpression *Resolve(FCompileContext&);
};
//==========================================================================
//
//
//
//==========================================================================
class FxMultiNameState : public FxExpression
{
const PClass *scope;
TArray<FName> names;
public:
FxMultiNameState(const char *statestring, const FScriptPosition &pos);
FxExpression *Resolve(FCompileContext&);
ExpVal EvalExpression (AActor *self);
};
FxExpression *ParseExpression (FScanner &sc, PClass *cls); FxExpression *ParseExpression (FScanner &sc, PClass *cls);
int AddExpression (FxExpression *data);
#endif #endif

View file

@ -51,7 +51,6 @@
#include "thingdef_exp.h" #include "thingdef_exp.h"
#include "autosegs.h" #include "autosegs.h"
extern int thingdef_terminate;
int testglobalvar = 1337; // just for having one global variable to test with int testglobalvar = 1337; // just for having one global variable to test with
DEFINE_GLOBAL_VARIABLE(testglobalvar) DEFINE_GLOBAL_VARIABLE(testglobalvar)
@ -74,19 +73,6 @@ DEFINE_MEMBER_VARIABLE(momx, AActor)
DEFINE_MEMBER_VARIABLE(momy, AActor) DEFINE_MEMBER_VARIABLE(momy, AActor)
DEFINE_MEMBER_VARIABLE(momz, AActor) DEFINE_MEMBER_VARIABLE(momz, AActor)
static TDeletingArray<FxExpression *> StateExpressions;
int AddExpression (FxExpression *data)
{
if (StateExpressions.Size()==0)
{
// StateExpressions[0] always is const 0;
FxExpression *data = new FxConstant(0, FScriptPosition());
StateExpressions.Push (data);
}
return StateExpressions.Push (data);
}
//========================================================================== //==========================================================================
// //
// EvalExpression // EvalExpression
@ -95,32 +81,44 @@ int AddExpression (FxExpression *data)
//========================================================================== //==========================================================================
bool IsExpressionConst(int id) int EvalExpressionI (DWORD xi, AActor *self)
{ {
if (StateExpressions.Size() <= (unsigned int)id) return false; FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
return StateExpressions[id]->isConstant(); return x->EvalExpression (self).GetInt();
} }
int EvalExpressionI (int id, AActor *self) int EvalExpressionCol (DWORD xi, AActor *self)
{ {
if (StateExpressions.Size() <= (unsigned int)id) return 0; FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
return StateExpressions[id]->EvalExpression (self).GetInt(); return x->EvalExpression (self).GetColor();
} }
double EvalExpressionF (int id, AActor *self) FSoundID EvalExpressionSnd (DWORD xi, AActor *self)
{ {
if (StateExpressions.Size() <= (unsigned int)id) return 0.f; FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
return StateExpressions[id]->EvalExpression (self).GetFloat(); return x->EvalExpression (self).GetSoundID();
} }
fixed_t EvalExpressionFix (int id, AActor *self) double EvalExpressionF (DWORD xi, AActor *self)
{ {
if (StateExpressions.Size() <= (unsigned int)id) return 0; FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
ExpVal val = StateExpressions[id]->EvalExpression (self); return x->EvalExpression (self).GetFloat();
}
fixed_t EvalExpressionFix (DWORD xi, AActor *self)
{
FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
ExpVal val = x->EvalExpression (self);
switch (val.Type) switch (val.Type)
{ {
@ -133,6 +131,31 @@ fixed_t EvalExpressionFix (int id, AActor *self)
} }
} }
FName EvalExpressionName (DWORD xi, AActor *self)
{
FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
return x->EvalExpression (self).GetName();
}
const PClass * EvalExpressionClass (DWORD xi, AActor *self)
{
FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
return x->EvalExpression (self).GetClass();
}
FState *EvalExpressionState (DWORD xi, AActor *self)
{
FxExpression *x = StateParams.Get(xi);
if (x == NULL) return 0;
return x->EvalExpression (self).GetState();
}
//========================================================================== //==========================================================================
// //
// //
@ -152,6 +175,21 @@ static ExpVal GetVariableValue (void *address, FExpressionType &type)
ret.Int = *(int*)address; ret.Int = *(int*)address;
break; break;
case VAL_Sound:
ret.Type = VAL_Sound;
ret.Int = *(FSoundID*)address;
break;
case VAL_Name:
ret.Type = VAL_Name;
ret.Int = *(FName*)address;
break;
case VAL_Color:
ret.Type = VAL_Color;
ret.Int = *(int*)address;
break;
case VAL_Bool: case VAL_Bool:
ret.Type = VAL_Int; ret.Type = VAL_Int;
ret.Int = *(bool*)address; ret.Int = *(bool*)address;
@ -186,62 +224,6 @@ static ExpVal GetVariableValue (void *address, FExpressionType &type)
return ret; return ret;
} }
//==========================================================================
//
// FScriptPosition::Message
//
//==========================================================================
void STACK_ARGS FScriptPosition::Message (int severity, const char *message, ...) const
{
FString composed;
if ((severity == MSG_DEBUG || severity == MSG_DEBUGLOG) && !developer) return;
if (message == NULL)
{
composed = "Bad syntax.";
}
else
{
va_list arglist;
va_start (arglist, message);
composed.VFormat (message, arglist);
va_end (arglist);
}
const char *type = "";
int level = PRINT_HIGH;
switch (severity)
{
default:
return;
case MSG_WARNING:
type = "warning";
break;
case MSG_ERROR:
thingdef_terminate++;
type = "error";
break;
case MSG_DEBUG:
type = "message";
break;
case MSG_DEBUGLOG:
case MSG_LOG:
type = "message";
level = PRINT_LOG;
break;
}
Printf (level, "Script %s, \"%s\" line %d:\n%s\n", type,
FileName.GetChars(), ScriptLine, composed.GetChars());
}
//========================================================================== //==========================================================================
// //
// //
@ -278,9 +260,37 @@ bool FxExpression::isConstant() const
FxExpression *FxExpression::Resolve(FCompileContext &ctx) FxExpression *FxExpression::Resolve(FCompileContext &ctx)
{ {
isresolved = true;
return this; return this;
} }
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxExpression::ResolveAsBoolean(FCompileContext &ctx)
{
FxExpression *x = Resolve(ctx);
if (x != NULL)
{
switch (x->ValueType.Type)
{
case VAL_Sound:
case VAL_Color:
case VAL_Name:
x->ValueType = VAL_Int;
break;
default:
break;
}
}
return x;
}
//========================================================================== //==========================================================================
// //
// //
@ -1113,11 +1123,22 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx)
if (!ValueType.isNumeric() && !ValueType.isPointer()) if (!ValueType.isNumeric() && !ValueType.isPointer())
{ {
if (left->ValueType.Type == right->ValueType.Type)
{
// compare other types?
if (left->ValueType == VAL_Sound || left->ValueType == VAL_Color || left->ValueType == VAL_Name)
{
left->ValueType = right->ValueType = VAL_Int;
goto cont;
}
}
ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); ScriptPosition.Message(MSG_ERROR, "Numeric type expected");
delete this; delete this;
return NULL; return NULL;
} }
else if (left->isConstant() && right->isConstant()) cont:
if (left->isConstant() && right->isConstant())
{ {
int v; int v;
@ -2406,3 +2427,402 @@ ExpVal FxGlobalFunctionCall::EvalExpression (AActor *self)
else ret.Float = FIXED2FLOAT (finecosine[angle>>ANGLETOFINESHIFT]); else ret.Float = FIXED2FLOAT (finecosine[angle>>ANGLETOFINESHIFT]);
return ret; return ret;
} }
//==========================================================================
//
//
//
//==========================================================================
FxClassTypeCast::FxClassTypeCast(const PClass *dtype, FxExpression *x)
: FxExpression(x->ScriptPosition)
{
desttype = dtype;
basex=x;
}
//==========================================================================
//
//
//
//==========================================================================
FxClassTypeCast::~FxClassTypeCast()
{
SAFE_DELETE(basex);
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxClassTypeCast::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(basex, ctx);
if (basex->ValueType != VAL_Name)
{
ScriptPosition.Message(MSG_ERROR, "Cannot convert to class type");
delete this;
return NULL;
}
if (basex->isConstant())
{
FName clsname = basex->EvalExpression(NULL).GetName();
const PClass *cls;
if (clsname != NAME_None || !ctx.isconst)
{
cls= PClass::FindClass(clsname);
if (cls == NULL)
{
if (!ctx.lax)
{
ScriptPosition.Message(MSG_ERROR,"Unknown class name '%s'", clsname.GetChars());
delete this;
return NULL;
}
// Since this happens in released WADs it must pass without a terminal error... :(
ScriptPosition.Message(MSG_WARNING,
"Unknown class name '%s'",
clsname.GetChars(), desttype->TypeName.GetChars());
}
else
{
if (!cls->IsDescendantOf(desttype))
{
ScriptPosition.Message(MSG_ERROR,"class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars());
delete this;
return NULL;
}
}
ScriptPosition.Message(MSG_DEBUG,"resolving '%s' as class name", clsname.GetChars());
}
FxExpression *x = new FxConstant(cls, ScriptPosition);
delete this;
return x;
}
return this;
}
//==========================================================================
//
//
//
//==========================================================================
ExpVal FxClassTypeCast::EvalExpression (AActor *self)
{
FName clsname = basex->EvalExpression(NULL).GetName();
const PClass *cls = PClass::FindClass(clsname);
if (!cls->IsDescendantOf(desttype))
{
Printf("class '%s' is not compatible with '%s'", clsname.GetChars(), desttype->TypeName.GetChars());
cls = NULL;
}
ExpVal ret;
ret.Type = VAL_Class;
ret.pointer = (void*)cls;
return ret;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
if (ctx.cls->ActorInfo == NULL || ctx.cls->ActorInfo->NumOwnedStates == 0)
{
// This can't really happen
assert(false);
}
if (ctx.cls->ActorInfo->NumOwnedStates <= index)
{
ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d",
ctx.cls->TypeName.GetChars(), index);
delete this;
return NULL;
}
FxExpression *x = new FxConstant(ctx.cls->ActorInfo->OwnedStates + index, ScriptPosition);
delete this;
return x;
}
//==========================================================================
//
//
//
//==========================================================================
FxMultiNameState::FxMultiNameState(const char *_statestring, const FScriptPosition &pos)
:FxExpression(pos)
{
FName scopename;
FString statestring = _statestring;
int scopeindex = statestring.IndexOf("::");
if (scopeindex >= 0)
{
scopename = FName(statestring, scopeindex, false);
statestring = statestring.Right(statestring.Len() - scopeindex - 2);
}
else
{
scopename = NULL;
}
names = MakeStateNameList(statestring);
names.Insert(0, scopename);
scope = NULL;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
if (names[0] == NAME_None)
{
scope = NULL;
}
else if (names[0] == NAME_Super)
{
scope = ctx.cls->ParentClass;
}
else
{
scope = PClass::FindClass(names[0]);
if (scope == NULL)
{
ScriptPosition.Message(MSG_ERROR, "Unknown class '%s' in state label", names[0].GetChars());
delete this;
return NULL;
}
else if (!scope->IsDescendantOf(ctx.cls))
{
ScriptPosition.Message(MSG_ERROR, "'%s' is not an ancestor of '%s'", names[0].GetChars(),ctx.cls->TypeName.GetChars());
delete this;
return NULL;
}
}
if (scope != NULL)
{
// If the label is class specific we can resolve it right here
if (scope->ActorInfo == NULL)
{
ScriptPosition.Message(MSG_ERROR, "'%s' has no actorinfo", names[0].GetChars());
delete this;
return NULL;
}
FState *destination = scope->ActorInfo->FindState(names.Size()-1, &names[1], false);
if (destination == NULL)
{
ScriptPosition.Message(ctx.lax? MSG_WARNING:MSG_ERROR, "Unknown state jump destination");
delete this;
return NULL;
}
FxExpression *x = new FxConstant(destination, ScriptPosition);
delete this;
return x;
}
names.Delete(0);
names.ShrinkToFit();
return this;
}
//==========================================================================
//
//
//
//==========================================================================
ExpVal FxMultiNameState::EvalExpression (AActor *self)
{
ExpVal ret;
ret.Type = VAL_State;
ret.pointer = self->GetClass()->ActorInfo->FindState(names.Size(), &names[0]);
if (ret.pointer == NULL)
{
const char *dot="";
Printf("Jump target '");
for (int i=0;i<names.Size();i++)
{
Printf("%s%s", dot, names[i].GetChars());
dot = ".";
}
Printf("' not found in %s\n", self->GetClass()->TypeName.GetChars());
}
return ret;
}
//==========================================================================
//
// NOTE: I don't expect any of the following to survive Doomscript ;)
//
//==========================================================================
FStateExpressions StateParams;
//==========================================================================
//
//
//
//==========================================================================
FStateExpressions::~FStateExpressions()
{
for(unsigned i=0; i<Size(); i++)
{
if (expressions[i].expr != NULL && !expressions[i].cloned)
{
delete expressions[i].expr;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
int FStateExpressions::Add(FxExpression *x, const PClass *o, bool c)
{
int idx = expressions.Reserve(1);
FStateExpression &exp = expressions[idx];
exp.expr = x;
exp.owner = o;
exp.constant = c;
exp.cloned = false;
return idx;
}
//==========================================================================
//
//
//
//==========================================================================
int FStateExpressions::Reserve(int num, const PClass *cls)
{
int idx = expressions.Reserve(num);
FStateExpression *exp = &expressions[idx];
for(int i=0; i<num; i++)
{
exp[i].expr = NULL;
exp[i].owner = cls;
exp[i].constant = false;
exp[i].cloned = false;
}
return idx;
}
//==========================================================================
//
//
//
//==========================================================================
void FStateExpressions::Set(int num, FxExpression *x)
{
if (num >= 0 && num < int(Size()))
{
assert(expressions[num].expr == NULL || expressions[num].cloned);
expressions[num].expr = x;
expressions[num].cloned = false;
}
}
//==========================================================================
//
//
//
//==========================================================================
void FStateExpressions::Copy(int dest, int src, int cnt)
{
for(int i=0; i<cnt; i++)
{
expressions[dest+i].expr = expressions[src+i].expr;
expressions[dest+i].cloned = true;
}
}
//==========================================================================
//
//
//
//==========================================================================
int FStateExpressions::ResolveAll()
{
int errorcount = 0;
FCompileContext ctx;
ctx.lax = true;
for(unsigned i=0; i<Size(); i++)
{
if (expressions[i].expr != NULL && !expressions[i].cloned)
{
ctx.cls = expressions[i].owner;
ctx.isconst = expressions[i].constant;
expressions[i].expr = expressions[i].expr->Resolve(ctx);
if (expressions[i].expr == NULL)
{
errorcount++;
}
else if (expressions[i].constant && !expressions[i].expr->isConstant())
{
expressions[i].expr->ScriptPosition.Message(MSG_ERROR, "Constant expression expected");
errorcount++;
}
}
}
for(unsigned i=0; i<Size(); i++)
{
if (expressions[i].expr != NULL)
{
if (!expressions[i].expr->isresolved)
{
expressions[i].expr->ScriptPosition.Message(MSG_ERROR, "Expression at index %d not resolved\n", i);
errorcount++;
}
}
}
return errorcount;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FStateExpressions::Get(int num)
{
if (num >= 0 && num < int(Size()))
return expressions[num].expr;
return NULL;
}

View file

@ -1,166 +0,0 @@
/*
** decorations.cpp
** Loads custom actors out of DECORATE lumps.
**
**---------------------------------------------------------------------------
** Copyright 2002-2007 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include "actor.h"
#include "info.h"
#include "sc_man.h"
#include "tarray.h"
#include "w_wad.h"
#include "templates.h"
#include "s_sound.h"
#include "cmdlib.h"
#include "thingdef.h"
#include "i_system.h"
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void InitThingdef();
void ParseActor(FScanner &sc);
void FinishThingdef();
void ParseOldDecoration(FScanner &sc, EDefinitionType def);
// STATIC FUNCTION PROTOTYPES --------------------------------------------
PSymbolTable GlobalSymbols;
int thingdef_terminate;
//==========================================================================
//
// ParseDecorate
//
// Parses a single DECORATE lump
//
//==========================================================================
static void ParseDecorate (FScanner &sc)
{
// Get actor class name.
for(;;)
{
FScanner::SavedPos pos = sc.SavePos();
if (!sc.GetToken ())
{
return;
}
switch (sc.TokenType)
{
case TK_Include:
{
sc.MustGetString();
FScanner newscanner;
newscanner.Open(sc.String);
ParseDecorate(newscanner);
break;
}
case TK_Const:
ParseConstant (sc, &GlobalSymbols, NULL);
break;
case TK_Enum:
ParseEnum (sc, &GlobalSymbols, NULL);
break;
case TK_Pickup:
ParseOldDecoration (sc, DEF_Pickup);
break;
case TK_Breakable:
ParseOldDecoration (sc, DEF_BreakableDecoration);
break;
case TK_Projectile:
ParseOldDecoration (sc, DEF_Projectile);
break;
case TK_Native:
ParseVariable(sc, &GlobalSymbols, NULL);
break;
case ';':
// ';' is the start of a comment in the non-cmode parser which
// is used to parse parts of the DECORATE lump. If we don't add
// a check here the user will only get weird non-informative
// error messages if a semicolon is found.
sc.ScriptError("Unexpected ';'");
break;
case TK_Identifier:
// 'ACTOR' cannot be a keyword because it is also needed as a class identifier
// so let's do a special case for this.
if (sc.Compare("ACTOR"))
{
ParseActor (sc);
break;
}
default:
// without the option of game filters following, anything but an opening brace
// here means a syntax error.
sc.MustGetStringName("{");
sc.RestorePos(pos);
ParseOldDecoration(sc, DEF_Decoration);
break;
}
}
}
//==========================================================================
//
// LoadDecorations
//
// Called from FActor::StaticInit()
//
//==========================================================================
void LoadDecorations ()
{
int lastlump, lump;
InitThingdef();
lastlump = 0;
while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1)
{
FScanner sc(lump);
ParseDecorate (sc);
}
if (thingdef_terminate)
{
I_Error("%d errors found", thingdef_terminate);
}
FinishThingdef();
}

View file

@ -52,7 +52,112 @@
#include "i_system.h" #include "i_system.h"
#include "thingdef_exp.h" #include "thingdef_exp.h"
#include "w_wad.h" #include "w_wad.h"
#include "v_video.h"
void ParseOldDecoration(FScanner &sc, EDefinitionType def);
//==========================================================================
//
// ParseParameter
//
// Parses aparameter - either a default in a function declaration
// or an argument in a function call.
//
//==========================================================================
FxExpression *ParseParameter(FScanner &sc, PClass *cls, char type, bool constant)
{
FxExpression *x = NULL;
int v;
switch(type)
{
case 'S':
case 's': // Sound name
sc.MustGetString();
x = new FxConstant(FSoundID(sc.String), sc);
break;
case 'M':
case 'm': // Actor name
sc.SetEscape(true);
sc.MustGetString();
sc.SetEscape(false);
x = new FxClassTypeCast(RUNTIME_CLASS(AActor), new FxConstant(FName(sc.String), sc));
break;
case 'T':
case 't': // String
sc.SetEscape(true);
sc.MustGetString();
sc.SetEscape(false);
x = new FxConstant(sc.String[0]? FName(sc.String) : NAME_None, sc);
break;
case 'C':
case 'c': // Color
sc.MustGetString ();
if (sc.Compare("none"))
{
v = -1;
}
else if (sc.Compare(""))
{
v = 0;
}
else
{
int c = V_GetColor (NULL, sc.String);
// 0 needs to be the default so we have to mark the color.
v = MAKEARGB(1, RPART(c), GPART(c), BPART(c));
}
{
ExpVal val;
val.Type = VAL_Color;
val.Int = v;
x = new FxConstant(val, sc);
break;
}
case 'L':
case 'l':
{
// This forces quotation marks around the state name.
sc.MustGetToken(TK_StringConst);
if (sc.String[0] == 0 || sc.Compare("None"))
{
x = new FxConstant((FState*)NULL, sc);
}
else if (sc.Compare("*"))
{
if (constant)
{
x = new FxConstant((FState*)(intptr_t)-1, sc);
}
else sc.ScriptError("Invalid state name '*'");
}
else
{
x = new FxMultiNameState(sc.String, sc);
}
break;
}
case 'X':
case 'x':
x = ParseExpression (sc, cls);
if (constant && !x->isConstant())
{
sc.ScriptError("Default parameter must be constant.");
}
break;
default:
assert(false);
return NULL;
}
return x;
}
//========================================================================== //==========================================================================
// //
@ -62,7 +167,7 @@
// //
//========================================================================== //==========================================================================
void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) static void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls)
{ {
// Read the type and make sure it's int or float. // Read the type and make sure it's int or float.
if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float)) if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Float))
@ -108,7 +213,7 @@ void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls)
// //
//========================================================================== //==========================================================================
void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) static void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls)
{ {
int currvalue = 0; int currvalue = 0;
@ -148,7 +253,7 @@ void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls)
// //
//========================================================================== //==========================================================================
void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls) static void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls)
{ {
FExpressionType valuetype; FExpressionType valuetype;
@ -227,7 +332,7 @@ void ParseVariable (FScanner &sc, PSymbolTable * symt, PClass *cls)
// Parses a flag name // Parses a flag name
// //
//========================================================================== //==========================================================================
void ParseActorFlag (FScanner &sc, Baggage &bag, int mod) static void ParseActorFlag (FScanner &sc, Baggage &bag, int mod)
{ {
sc.MustGetString (); sc.MustGetString ();
@ -335,7 +440,7 @@ static int ParseMorphStyle (FScanner &sc)
// //
//========================================================================== //==========================================================================
FState *CheckState(FScanner &sc, PClass *type) static FState *CheckState(FScanner &sc, PClass *type)
{ {
int v=0; int v=0;
@ -388,7 +493,7 @@ FState *CheckState(FScanner &sc, PClass *type)
// //
//========================================================================== //==========================================================================
bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Baggage &bag) static bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Baggage &bag)
{ {
static TArray<FPropParam> params; static TArray<FPropParam> params;
static TArray<FString> strings; static TArray<FString> strings;
@ -417,7 +522,8 @@ bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Ba
if (sc.CheckString ("(")) if (sc.CheckString ("("))
{ {
conv.i = 0x40000000 | ParseExpression (sc, false, bag.Info->Class); FxExpression *x = ParseExpression(sc, bag.Info->Class);
conv.i = 0x40000000 | StateParams.Add(x, bag.Info->Class, false);
params.Push(conv); params.Push(conv);
sc.MustGetStringName(")"); sc.MustGetStringName(")");
break; break;
@ -564,7 +670,7 @@ bool ParsePropertyParams(FScanner &sc, FPropertyInfo *prop, AActor *defaults, Ba
// //
//========================================================================== //==========================================================================
void ParseActorProperty(FScanner &sc, Baggage &bag) static void ParseActorProperty(FScanner &sc, Baggage &bag)
{ {
static const char *statenames[] = { static const char *statenames[] = {
"Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn", "Spawn", "See", "Melee", "Missile", "Pain", "Death", "XDeath", "Burn",
@ -615,44 +721,372 @@ void ParseActorProperty(FScanner &sc, Baggage &bag)
} }
} }
//========================================================================== //==========================================================================
// //
// Finalizes an actor definition // ActorActionDef
//
// Parses an action function definition. A lot of this is essentially
// documentation in the declaration for when I have a proper language
// ready.
// //
//========================================================================== //==========================================================================
void FinishActor(FScanner &sc, FActorInfo *info, Baggage &bag) static void ParseActionDef (FScanner &sc, PClass *cls)
{ {
AActor *defaults = (AActor*)info->Class->Defaults; enum
{
OPTIONAL = 1
};
try AFuncDesc *afd;
FName funcname;
FString args;
TArray<FxExpression *> DefaultParams;
bool hasdefaults = false;
if (sc.LumpNum == -1 || Wads.GetLumpFile(sc.LumpNum) > 0)
{ {
bag.statedef.FinishStates (info, defaults, bag.StateArray); sc.ScriptError ("action functions can only be imported by internal class and actor definitions!");
} }
catch (CRecoverableError &err)
sc.MustGetToken(TK_Native);
sc.MustGetToken(TK_Identifier);
funcname = sc.String;
afd = FindFunction(sc.String);
if (afd == NULL)
{ {
sc.ScriptError(err.GetMessage()); sc.ScriptError ("The function '%s' has not been exported from the executable.", sc.String);
} }
bag.statedef.InstallStates (info, defaults); sc.MustGetToken('(');
bag.StateArray.Clear (); if (!sc.CheckToken(')'))
if (bag.DropItemSet)
{ {
if (bag.DropItemList == NULL) while (sc.TokenType != ')')
{ {
if (info->Class->Meta.GetMetaInt (ACMETA_DropItems) != 0) int flags = 0;
char type = '@';
// Retrieve flags before type name
for (;;)
{ {
info->Class->Meta.SetMetaInt (ACMETA_DropItems, 0); if (sc.CheckToken(TK_Coerce) || sc.CheckToken(TK_Native))
{
}
else
{
break;
}
}
// Read the variable type
sc.MustGetAnyToken();
switch (sc.TokenType)
{
case TK_Bool:
case TK_Int:
case TK_Float:
type = 'x';
break;
case TK_Sound: type = 's'; break;
case TK_String: type = 't'; break;
case TK_Name: type = 't'; break;
case TK_State: type = 'l'; break;
case TK_Color: type = 'c'; break;
case TK_Class:
sc.MustGetToken('<');
sc.MustGetToken(TK_Identifier); // Skip class name, since the parser doesn't care
sc.MustGetToken('>');
type = 'm';
break;
case TK_Ellipsis:
type = '+';
sc.MustGetToken(')');
sc.UnGet();
break;
default:
sc.ScriptError ("Unknown variable type %s", sc.TokenName(sc.TokenType, sc.String).GetChars());
break;
}
// Read the optional variable name
if (!sc.CheckToken(',') && !sc.CheckToken(')'))
{
sc.MustGetToken(TK_Identifier);
}
else
{
sc.UnGet();
}
FxExpression *def;
if (sc.CheckToken('='))
{
hasdefaults = true;
flags |= OPTIONAL;
def = ParseParameter(sc, cls, type, true);
}
else
{
def = NULL;
}
DefaultParams.Push(def);
if (!(flags & OPTIONAL) && type != '+')
{
type -= 'a' - 'A';
}
args += type;
sc.MustGetAnyToken();
if (sc.TokenType != ',' && sc.TokenType != ')')
{
sc.ScriptError ("Expected ',' or ')' but got %s instead", sc.TokenName(sc.TokenType, sc.String).GetChars());
}
}
}
sc.MustGetToken(';');
PSymbolActionFunction *sym = new PSymbolActionFunction(funcname);
sym->Arguments = args;
sym->Function = afd->Function;
if (hasdefaults)
{
sym->defaultparameterindex = StateParams.Size();
for(unsigned int i = 0; i < DefaultParams.Size(); i++)
{
StateParams.Add(DefaultParams[i], cls, true);
} }
} }
else else
{ {
info->Class->Meta.SetMetaInt (ACMETA_DropItems, sym->defaultparameterindex = -1;
StoreDropItemChain(bag.DropItemList));
} }
} if (cls->Symbols.AddSymbol (sym) == NULL)
if (info->Class->IsDescendantOf (RUNTIME_CLASS(AInventory)))
{ {
defaults->flags |= MF_SPECIAL; delete sym;
sc.ScriptError ("'%s' is already defined in class '%s'.",
funcname.GetChars(), cls->TypeName.GetChars());
}
}
//==========================================================================
//
// Starts a new actor definition
//
//==========================================================================
static FActorInfo *ParseActorHeader(FScanner &sc, Baggage *bag)
{
FName typeName;
FName parentName;
FName replaceName;
bool native = false;
int DoomEdNum = -1;
// Get actor name
sc.MustGetString();
char *colon = strchr(sc.String, ':');
if (colon != NULL)
{
*colon++ = 0;
}
typeName = sc.String;
// Do some tweaking so that a definition like 'Actor:Parent' which is read as a single token is recognized as well
// without having resort to C-mode (which disallows periods in actor names.)
if (colon == NULL)
{
sc.MustGetString ();
if (sc.String[0]==':')
{
colon = sc.String + 1;
}
}
if (colon != NULL)
{
if (colon[0] == 0)
{
sc.MustGetString ();
colon = sc.String;
}
}
if (colon == NULL)
{
sc.UnGet();
}
parentName = colon;
// Check for "replaces"
if (sc.CheckString ("replaces"))
{
// Get actor name
sc.MustGetString ();
replaceName = sc.String;
}
// Now, after the actor names have been parsed, it is time to switch to C-mode
// for the rest of the actor definition.
sc.SetCMode (true);
if (sc.CheckNumber())
{
if (sc.Number>=-1 && sc.Number<32768) DoomEdNum = sc.Number;
else sc.ScriptError ("DoomEdNum must be in the range [-1,32767]");
}
if (sc.CheckString("native"))
{
native = true;
}
try
{
FActorInfo *info = CreateNewActor(sc, typeName, parentName, replaceName, DoomEdNum, native);
ResetBaggage (bag, info->Class->ParentClass);
bag->Info = info;
bag->Lumpnum = sc.LumpNum;
#ifdef _DEBUG
bag->ClassName = typeName;
#endif
return info;
}
catch (CRecoverableError &err)
{
sc.ScriptError("%s", err.GetMessage());
return NULL;
}
}
//==========================================================================
//
// Reads an actor definition
//
//==========================================================================
static void ParseActor(FScanner &sc)
{
FActorInfo * info=NULL;
Baggage bag;
info = ParseActorHeader(sc, &bag);
sc.MustGetToken('{');
while (sc.MustGetAnyToken(), sc.TokenType != '}')
{
switch (sc.TokenType)
{
case TK_Action:
ParseActionDef (sc, info->Class);
break;
case TK_Const:
ParseConstant (sc, &info->Class->Symbols, info->Class);
break;
case TK_Enum:
ParseEnum (sc, &info->Class->Symbols, info->Class);
break;
case TK_Native:
ParseVariable (sc, &info->Class->Symbols, info->Class);
break;
case TK_Identifier:
// other identifier related checks here
case TK_Projectile: // special case: both keyword and property name
ParseActorProperty(sc, bag);
break;
case '+':
case '-':
ParseActorFlag(sc, bag, sc.TokenType);
break;
default:
sc.ScriptError("Unexpected '%s' in definition of '%s'", sc.String, bag.Info->Class->TypeName.GetChars());
break;
}
}
FinishActor(sc, info, bag);
sc.SetCMode (false);
}
//==========================================================================
//
// ParseDecorate
//
// Parses a single DECORATE lump
//
//==========================================================================
void ParseDecorate (FScanner &sc)
{
// Get actor class name.
for(;;)
{
FScanner::SavedPos pos = sc.SavePos();
if (!sc.GetToken ())
{
return;
}
switch (sc.TokenType)
{
case TK_Include:
{
sc.MustGetString();
FScanner newscanner;
newscanner.Open(sc.String);
ParseDecorate(newscanner);
break;
}
case TK_Const:
ParseConstant (sc, &GlobalSymbols, NULL);
break;
case TK_Enum:
ParseEnum (sc, &GlobalSymbols, NULL);
break;
case TK_Pickup:
ParseOldDecoration (sc, DEF_Pickup);
break;
case TK_Breakable:
ParseOldDecoration (sc, DEF_BreakableDecoration);
break;
case TK_Projectile:
ParseOldDecoration (sc, DEF_Projectile);
break;
case TK_Native:
ParseVariable(sc, &GlobalSymbols, NULL);
break;
case ';':
// ';' is the start of a comment in the non-cmode parser which
// is used to parse parts of the DECORATE lump. If we don't add
// a check here the user will only get weird non-informative
// error messages if a semicolon is found.
sc.ScriptError("Unexpected ';'");
break;
case TK_Identifier:
// 'ACTOR' cannot be a keyword because it is also needed as a class identifier
// so let's do a special case for this.
if (sc.Compare("ACTOR"))
{
ParseActor (sc);
break;
}
default:
// without the option of game filters following, anything but an opening brace
// here means a syntax error.
sc.MustGetStringName("{");
sc.RestorePos(pos);
ParseOldDecoration(sc, DEF_Decoration);
break;
}
} }
} }

View file

@ -55,10 +55,7 @@
#include "s_sound.h" #include "s_sound.h"
#include "i_system.h" #include "i_system.h"
#include "colormatcher.h" #include "colormatcher.h"
#include "thingdef_exp.h"
TArray<int> StateParameters;
//========================================================================== //==========================================================================
//*** //***
@ -66,13 +63,9 @@ TArray<int> StateParameters;
// creates an empty parameter list for a parameterized function call // creates an empty parameter list for a parameterized function call
// //
//========================================================================== //==========================================================================
int PrepareStateParameters(FState * state, int numparams) static int PrepareStateParameters(FState * state, int numparams, const PClass *cls)
{ {
int paramindex=StateParameters.Size(); int paramindex=StateParams.Reserve(numparams, cls);
int i, v;
v=0;
for(i=0;i<numparams;i++) StateParameters.Push(v);
state->ParameterIndex = paramindex+1; state->ParameterIndex = paramindex+1;
return paramindex; return paramindex;
} }
@ -94,21 +87,16 @@ bool DoActionSpecials(FScanner &sc, FState & state, bool multistate, int * state
if (special > 0 && min_args >= 0) if (special > 0 && min_args >= 0)
{ {
int paramindex=PrepareStateParameters(&state, 6); int paramindex=PrepareStateParameters(&state, 6, bag.Info->Class);
// The function expects the special to be passed as expression so we StateParams.Set(paramindex, new FxConstant(special, sc));
// have to convert it.
specname.Format("%d", special);
FScanner sc2;
sc2.OpenMem("", (char*)specname.GetChars(), int(specname.Len()));
StateParameters[paramindex] = ParseExpression(sc2, false, bag.Info->Class);
// Make this consistent with all other parameter parsing // Make this consistent with all other parameter parsing
if (sc.CheckToken('(')) if (sc.CheckToken('('))
{ {
for (i = 0; i < 5;) for (i = 0; i < 5;)
{ {
StateParameters[paramindex+i+1] = ParseExpression (sc, false, bag.Info->Class); StateParams.Set(paramindex+i+1, ParseExpression (sc, bag.Info->Class));
i++; i++;
if (!sc.CheckToken (',')) break; if (!sc.CheckToken (',')) break;
} }
@ -351,23 +339,25 @@ do_stop:
} }
} }
int paramindex = PrepareStateParameters(&state, numparams); int paramindex = PrepareStateParameters(&state, numparams, bag.Info->Class);
int paramstart = paramindex; int paramstart = paramindex;
bool varargs = params[numparams - 1] == '+'; bool varargs = params[numparams - 1] == '+';
int varargcount=0;
if (varargs) if (varargs)
{ {
StateParameters[paramindex++] = 0; varargcount++;
paramindex++;
} }
else if (afd->defaultparameterindex > -1) else if (afd->defaultparameterindex > -1)
{ {
memcpy(&StateParameters[paramindex], &StateParameters[afd->defaultparameterindex], StateParams.Copy(paramindex, afd->defaultparameterindex, int(afd->Arguments.Len()));
afd->Arguments.Len() * sizeof (StateParameters[0]));
} }
while (*params) while (*params)
{ {
FxExpression *x;
if ((*params == 'l' || *params == 'L') && sc.CheckNumber()) if ((*params == 'l' || *params == 'L') && sc.CheckNumber())
{ {
// Special case: State label as an offset // Special case: State label as an offset
@ -382,19 +372,18 @@ do_stop:
sc.ScriptError("Negative jump offsets are not allowed"); sc.ScriptError("Negative jump offsets are not allowed");
} }
int minreq=count+v; x = new FxStateByIndex(count+v, sc);
if (minreq>minrequiredstate) minrequiredstate=minreq;
} }
else else
{ {
// Use the generic parameter parser for everything else // Use the generic parameter parser for everything else
v = ParseParameter(sc, bag.Info->Class, *params, false); x = ParseParameter(sc, bag.Info->Class, *params, false);
} }
StateParameters[paramindex++] = v; StateParams.Set(paramindex++, x);
params++; params++;
if (varargs) if (varargs)
{ {
StateParameters[paramstart]++; varargcount++;
} }
if (*params) if (*params)
{ {
@ -402,11 +391,11 @@ do_stop:
{ {
if (sc.CheckString(")")) if (sc.CheckString(")"))
{ {
StateParams.Set(paramstart, new FxConstant(varargcount, sc));
goto endofstate; goto endofstate;
} }
params--; params--;
v = 0; StateParams.Reserve(1, bag.Info->Class);
StateParameters.Push(v);
} }
else if ((islower(*params) || *params=='!') && sc.CheckString(")")) else if ((islower(*params) || *params=='!') && sc.CheckString(")"))
{ {
@ -451,10 +440,6 @@ endofstate:
count++; count++;
} }
} }
if (count<=minrequiredstate)
{
sc.ScriptError("A_Jump offset out of range in %s", actor->Class->TypeName.GetChars());
}
sc.SetEscape(true); // re-enable escape sequences sc.SetEscape(true); // re-enable escape sequences
return count; return count;
} }

View file

@ -8,13 +8,18 @@
enum ExpValType enum ExpValType
{ {
VAL_Int, VAL_Int, // integer number
VAL_Float, VAL_Float, // floating point number
VAL_Unknown, VAL_Unknown, // nothing
VAL_Array, VAL_Array, // Array (very limited right now)
VAL_Object, VAL_Object, // Object reference
VAL_Class, VAL_Class, // Class reference
VAL_Pointer, VAL_Pointer, // Dereferenced variable (only used for addressing arrays for now.)
VAL_Sound, // Sound identifier. Internally it's an int.
VAL_Name, // A Name
VAL_MultiName, // Multiple names for multi-label states
VAL_Color, // A color.
VAL_State, // A State pointer
// only used for accessing external variables to ensure proper conversion // only used for accessing external variables to ensure proper conversion
VAL_Fixed, VAL_Fixed,

View file

@ -83,10 +83,11 @@ actor PhasingZorcher : PlasmaRifle 2004
PLSG B 20 A_ReFire PLSG B 20 A_ReFire
Goto Ready Goto Ready
Flash: Flash:
PLSF A 0 A_Jump(128, 2) PLSF A 0 A_Jump(128, "Flash2")
PLSF A 4 Bright A_Light1 PLSF A 3 Bright A_Light1
Goto LightDone Goto LightDone
PLSF B 4 Bright A_Light1 Flash2:
PLSF B 3 Bright A_Light1
Goto LightDone Goto LightDone
} }
} }

View file

@ -1075,10 +1075,6 @@
RelativePath=".\src\thingdef\thingdef_expression.cpp" RelativePath=".\src\thingdef\thingdef_expression.cpp"
> >
</File> </File>
<File
RelativePath=".\src\thingdef\thingdef_main.cpp"
>
</File>
<File <File
RelativePath=".\src\thingdef\thingdef_parse.cpp" RelativePath=".\src\thingdef\thingdef_parse.cpp"
> >