- 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:
parent
4c6b7f6752
commit
d753d41752
23 changed files with 1467 additions and 967 deletions
|
|
@ -51,7 +51,6 @@
|
|||
#include "thingdef_exp.h"
|
||||
#include "autosegs.h"
|
||||
|
||||
extern int thingdef_terminate;
|
||||
int testglobalvar = 1337; // just for having one global variable to test with
|
||||
DEFINE_GLOBAL_VARIABLE(testglobalvar)
|
||||
|
||||
|
|
@ -74,19 +73,6 @@ DEFINE_MEMBER_VARIABLE(momx, AActor)
|
|||
DEFINE_MEMBER_VARIABLE(momy, 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
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -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;
|
||||
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:
|
||||
ret.Type = VAL_Int;
|
||||
ret.Int = *(bool*)address;
|
||||
|
|
@ -186,62 +224,6 @@ static ExpVal GetVariableValue (void *address, FExpressionType &type)
|
|||
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)
|
||||
{
|
||||
isresolved = true;
|
||||
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 (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");
|
||||
delete this;
|
||||
return NULL;
|
||||
}
|
||||
else if (left->isConstant() && right->isConstant())
|
||||
cont:
|
||||
if (left->isConstant() && right->isConstant())
|
||||
{
|
||||
int v;
|
||||
|
||||
|
|
@ -2405,4 +2426,403 @@ ExpVal FxGlobalFunctionCall::EvalExpression (AActor *self)
|
|||
if (Name == NAME_Sin) ret.Float = FIXED2FLOAT (finesine[angle>>ANGLETOFINESHIFT]);
|
||||
else ret.Float = FIXED2FLOAT (finecosine[angle>>ANGLETOFINESHIFT]);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue