Use action function return value to make state jumps happen

- The A_Jump family of action functions now return the state to jump
  to (NULL if no jump is to be taken) instead of jumping directly.
  It is the caller's responsibility to handle the jump. This will
  make it possible to use their results in if statements and
  do something other than jump.
- DECORATE return statements can now return the result of a function
  (but not any random expression--it must be a function call). To
  make a jump happen from inside a multi-action block, you must
  return the value of an A_Jump function. e.g.:
    { return A_Jump(128, "SomeState"); }
- The VMFunction class now contains its prototype instead of storing
  it at a higher level in PFunction. This is so that
  FState::CallAction can easily tell if a function returns a state.
- Removed the FxTailable class because with explicit return
  statements, it's not useful anymore.
This commit is contained in:
Randy Heit 2016-02-18 20:39:40 -06:00
parent 0f70d10521
commit b2ccd0bd28
18 changed files with 663 additions and 535 deletions

View File

@ -207,7 +207,6 @@ enum EObjectFlags
OF_JustSpawned = 1 << 8, // Thinker was spawned this tic
OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls
OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list
OF_StateChanged = 1 << 11, // Used by A_Jump* functions to feed back to SetState()
};
template<class T> class TObjPtr;

View File

@ -73,7 +73,7 @@
#include "po_man.h"
#include "autosegs.h"
#include "v_video.h"
#include "textures.h"
#include "textures/textures.h"
#include "r_utility.h"
#include "menu/menu.h"
#include "intermission/intermission.h"

View File

@ -1222,6 +1222,38 @@ PStatePointer::PStatePointer()
{
}
//==========================================================================
//
// PStatePointer :: GetStoreOp
//
//==========================================================================
int PStatePointer::GetStoreOp() const
{
return OP_SP;
}
//==========================================================================
//
// PStatePointer :: GetLoadOp
//
//==========================================================================
int PStatePointer::GetLoadOp() const
{
return OP_LP;
}
//==========================================================================
//
// PStatePointer :: GetRegType
//
//==========================================================================
int PStatePointer::GetRegType() const
{
return REGT_POINTER;
}
/* PPointer ***************************************************************/
@ -1948,7 +1980,7 @@ size_t PFunction::PropagateMark()
{
for (unsigned i = 0; i < Variants.Size(); ++i)
{
GC::Mark(Variants[i].Proto);
//GC::Mark(Variants[i].Proto);
GC::Mark(Variants[i].Implementation);
}
return Variants.Size() * sizeof(Variants[0]) + Super::PropagateMark();
@ -1967,9 +1999,10 @@ unsigned PFunction::AddVariant(PPrototype *proto, TArray<DWORD> &argflags, VMFun
{
Variant variant;
variant.Proto = proto;
//variant.Proto = proto;
variant.ArgFlags = argflags;
variant.Implementation = impl;
impl->Proto = proto;
return Variants.Push(variant);
}

View File

@ -415,6 +415,10 @@ class PStatePointer : public PBasicType
DECLARE_CLASS(PStatePointer, PBasicType);
public:
PStatePointer();
virtual int GetStoreOp() const;
virtual int GetLoadOp() const;
virtual int GetRegType() const;
};
class PPointer : public PBasicType
@ -582,7 +586,7 @@ class PFunction : public PSymbol
public:
struct Variant
{
PPrototype *Proto;
//PPrototype *Proto;
VMFunction *Implementation;
TArray<DWORD> ArgFlags; // Should be the same length as Proto->ArgumentTypes
};

View File

@ -52,7 +52,7 @@
#include "cmdlib.h"
#include "g_level.h"
#include "stats.h"
#include "thingdef.h"
#include "thingdef/thingdef.h"
#include "d_player.h"
extern void LoadActors ();
@ -69,14 +69,37 @@ void FState::SetAction(const char *name)
ActionFunc = FindGlobalActionFunction(name)->Variants[0].Implementation;
}
bool FState::CallAction(AActor *self, AActor *stateowner)
bool FState::CallAction(AActor *self, AActor *stateowner, FState **stateret)
{
if (ActionFunc != NULL)
{
ActionCycles.Clock();
static VMFrameStack stack;
VMValue params[3] = { self, stateowner, VMValue(this, ATAG_STATE) };
stack.Call(ActionFunc, params, countof(params), NULL, 0, NULL);
// If the function returns a state, store it at *stateret.
// If it doesn't return a state but stateret is non-NULL, we need
// to set *stateret to NULL.
if (stateret != NULL)
{
*stateret = NULL;
if (ActionFunc->Proto == NULL ||
ActionFunc->Proto->ReturnTypes.Size() == 0 ||
ActionFunc->Proto->ReturnTypes[0] != TypeState)
{
stateret = NULL;
}
}
if (stateret == NULL)
{
stack.Call(ActionFunc, params, countof(params), NULL, 0, NULL);
}
else
{
VMReturn ret;
ret.PointerAt((void **)stateret);
stack.Call(ActionFunc, params, countof(params), &ret, 1, NULL);
}
ActionCycles.Unclock();
return true;
}

View File

@ -128,7 +128,7 @@ struct FState
void SetAction(VMFunction *func) { ActionFunc = func; }
void ClearAction() { ActionFunc = NULL; }
void SetAction(const char *name);
bool CallAction(AActor *self, AActor *stateowner);
bool CallAction(AActor *self, AActor *stateowner, FState **stateret);
static PClassActor *StaticFindStateOwner (const FState *state);
static PClassActor *StaticFindStateOwner (const FState *state, PClassActor *info);
static FRandom pr_statetics;

View File

@ -614,21 +614,22 @@ bool AActor::SetState (FState *newstate, bool nofunction)
}
}
if (!nofunction && newstate->CallAction(this, this))
if (!nofunction)
{
// Check whether the called action function resulted in destroying the actor
if (ObjectFlags & OF_EuthanizeMe)
FState *returned_state;
if (newstate->CallAction(this, this, &returned_state))
{
return false;
}
if (ObjectFlags & OF_StateChanged)
{ // The action was an A_Jump-style function that wants to change the next state.
ObjectFlags &= ~OF_StateChanged;
FState *saved = newstate;
newstate = state;
state = saved; // we need this for comparison of sprites.
tics = 0; // make sure we loop and set the new state properly
continue;
// Check whether the called action function resulted in destroying the actor
if (ObjectFlags & OF_EuthanizeMe)
{
return false;
}
if (returned_state != NULL)
{ // The action was an A_Jump-style function that wants to change the next state.
newstate = returned_state;
tics = 0; // make sure we loop and set the new state properly
continue;
}
}
}
newstate = newstate->GetNextState();
@ -3904,16 +3905,16 @@ bool AActor::CheckNoDelay()
{
// For immediately spawned objects with the NoDelay flag set for their
// Spawn state, explicitly call the current state's function.
if (state->CallAction(this, this))
FState *newstate;
if (state->CallAction(this, this, &newstate))
{
if (ObjectFlags & OF_EuthanizeMe)
{
return false; // freed itself
}
if (ObjectFlags & OF_StateChanged)
if (newstate != NULL)
{
ObjectFlags &= ~OF_StateChanged;
return SetState(state);
return SetState(newstate);
}
}
}

View File

@ -178,9 +178,16 @@ void P_SetPsprite (player_t *player, int position, FState *state, bool nofunctio
if (!nofunction && player->mo != NULL)
{
if (state->CallAction(player->mo, player->ReadyWeapon))
FState *newstate;
if (state->CallAction(player->mo, player->ReadyWeapon, &newstate))
{
if (!psp->state)
if (newstate != NULL)
{
state = newstate;
psp->tics = 0;
continue;
}
if (psp->state == NULL)
{
break;
}

View File

@ -277,11 +277,11 @@ static void FinishThingdef()
// Can we call this function directly without wrapping it in an
// anonymous function? e.g. Are we passing any parameters to it?
func = tcall->Code->GetDirectFunction();
func = NULL;//tcall->Code->GetDirectFunction();
if (func == NULL)
{
FCompileContext ctx(tcall->ActorClass);
tcall->Code = static_cast<FxTailable *>(tcall->Code->Resolve(ctx));
tcall->Code = tcall->Code->Resolve(ctx);
// Make sure resolving it didn't obliterate it.
if (tcall->Code != NULL)
@ -292,11 +292,25 @@ static void FinishThingdef()
// self, stateowner, state (all are pointers)
buildit.Registers[REGT_POINTER].Get(3);
// Emit a tail call via FxVMFunctionCall
tcall->Code->Emit(&buildit, true);
// Emit code
tcall->Code->Emit(&buildit);
VMScriptFunction *sfunc = buildit.MakeFunction();
sfunc->NumArgs = NAP;
// Generate prototype for this anonymous function
TArray<PType *> args(3);
SetImplicitArgs(&args, NULL, tcall->ActorClass, VARF_Method | VARF_Action);
if (tcall->Proto != NULL)
{
sfunc->Proto = NewPrototype(tcall->Proto->ReturnTypes, args);
}
else
{
TArray<PType *> norets(0);
sfunc->Proto = NewPrototype(norets, args);
}
func = sfunc;
if (dump != NULL)
@ -354,6 +368,7 @@ static void FinishThingdef()
dmg->Emit(&buildit);
sfunc = buildit.MakeFunction();
sfunc->NumArgs = 1;
sfunc->Proto = NULL; ///FIXME: Need a proper prototype here
// Save this function in case this damage value was reused
// (which happens quite easily with inheritance).
dmg->SetFunction(sfunc);

View File

@ -122,7 +122,8 @@ struct FStateTempCall
FStateTempCall() : ActorClass(NULL), Code(NULL), FirstState(0), NumStates(0) {}
PClassActor *ActorClass;
class FxTailable *Code;
class FxExpression *Code;
class PPrototype *Proto;
int FirstState;
int NumStates;
};
@ -189,8 +190,10 @@ AFuncDesc *FindFunction(const char * string);
void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag);
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
PFunction *afd, FString statestring, FStateDefinitions *statedef);
FxTailable *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag);
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, PPrototype *&proto, bool &endswithret);
class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
void AddImplicitReturn(class FxSequence *code, const PPrototype *proto, FScanner &sc);
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClassActor *cls, DWORD funcflags);
PFunction *FindGlobalActionFunction(const char *name);
@ -354,8 +357,9 @@ int MatchString (const char *in, const char **strings);
}
#define ACTION_SET_RESULT(v) do { if (numret > 0) { assert(ret != NULL); ret->SetInt(v); numret = 1; } } while(0)
#define ACTION_OR_RESULT(v) do { if (numret > 0) { assert(ret != NULL); ret->SetInt(*(int *)ret->Location | int(v)); numret = 1; } } while(0)
#define ACTION_RETURN_STATE(state) if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_STATE); return 1; } return 0
#define ACTION_RETURN_INT(v) if (numret > 0) { assert(ret != NULL); ret->SetInt(v); return 1; } return 0
#define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v)
// Checks to see what called the current action function
#define ACTION_CALL_FROM_ACTOR() (callingstate == self->state)

File diff suppressed because it is too large Load Diff

View File

@ -204,6 +204,7 @@ public:
virtual bool isConstant() const;
virtual void RequestAddress();
virtual VMFunction *GetDirectFunction();
virtual ExpEmit Emit(VMFunctionBuilder *build);
@ -817,39 +818,22 @@ public:
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
//
// FxTailable
//
// An expression that can produce a tail call
//
//==========================================================================
class FxTailable : public FxExpression
{
public:
FxTailable(const FScriptPosition &pos) : FxExpression(pos) {}
virtual ExpEmit Emit(VMFunctionBuilder *build, bool tailcall) = 0;
ExpEmit Emit(VMFunctionBuilder *build);
virtual VMFunction *GetDirectFunction();
};
//==========================================================================
//
// FxVMFunctionCall
//
//==========================================================================
class FxVMFunctionCall : public FxTailable
class FxVMFunctionCall : public FxExpression
{
PFunction *Function;
FArgumentList *ArgList;
PType *ReturnType;
public:
FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos);
~FxVMFunctionCall();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
ExpEmit Emit(VMFunctionBuilder *build, bool tailcall);
unsigned GetArgCount() { return ArgList == NULL ? 0 : ArgList->Size(); }
VMFunction *GetVMFunction() { return Function->Variants[0].Implementation; }
@ -862,15 +846,15 @@ public:
//
//==========================================================================
class FxSequence : public FxTailable
class FxSequence : public FxExpression
{
TDeletingArray<FxTailable *> Expressions;
TDeletingArray<FxExpression *> Expressions;
public:
FxSequence(const FScriptPosition &pos) : FxTailable(pos) {}
FxSequence(const FScriptPosition &pos) : FxExpression(pos) {}
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build, bool tailcall);
void Add(FxTailable *expr) { if (expr != NULL) Expressions.Push(expr); }
ExpEmit Emit(VMFunctionBuilder *build);
void Add(FxExpression *expr) { if (expr != NULL) Expressions.Push(expr); }
VMFunction *GetDirectFunction();
};
@ -880,17 +864,17 @@ public:
//
//==========================================================================
class FxIfStatement : public FxTailable
class FxIfStatement : public FxExpression
{
FxExpression *Condition;
FxTailable *WhenTrue;
FxTailable *WhenFalse;
FxExpression *WhenTrue;
FxExpression *WhenFalse;
public:
FxIfStatement(FxExpression *cond, FxTailable *true_part, FxTailable *false_part, const FScriptPosition &pos);
FxIfStatement(FxExpression *cond, FxExpression *true_part, FxExpression *false_part, const FScriptPosition &pos);
~FxIfStatement();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build, bool tailcall);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================
@ -899,11 +883,14 @@ public:
//
//==========================================================================
class FxReturnStatement : public FxTailable
class FxReturnStatement : public FxExpression
{
FxVMFunctionCall *Call;
public:
FxReturnStatement(const FScriptPosition &pos);
ExpEmit Emit(VMFunctionBuilder *build, bool tailcall);
FxReturnStatement(FxVMFunctionCall *call, const FScriptPosition &pos);
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================

View File

@ -129,6 +129,17 @@ bool FxExpression::isConstant() const
//
//==========================================================================
VMFunction *FxExpression::GetDirectFunction()
{
return NULL;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxExpression::Resolve(FCompileContext &ctx)
{
isresolved = true;
@ -3064,28 +3075,6 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
return dest;
}
//==========================================================================
//
//
//
//==========================================================================
ExpEmit FxTailable::Emit(VMFunctionBuilder *build)
{
return Emit(build, false);
}
//==========================================================================
//
//
//
//==========================================================================
VMFunction *FxTailable::GetDirectFunction()
{
return NULL;
}
//==========================================================================
//
// FxVMFunctionCall
@ -3093,7 +3082,7 @@ VMFunction *FxTailable::GetDirectFunction()
//==========================================================================
FxVMFunctionCall::FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos)
: FxTailable(pos)
: FxExpression(pos)
{
Function = func;
ArgList = args;
@ -3134,14 +3123,9 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
delete this;
return NULL;
}
TArray<PType *> &rets = Function->Variants[0].Proto->ReturnTypes;
if (rets.Size() == 0)
TArray<PType *> &rets = Function->Variants[0].Implementation->Proto->ReturnTypes;
if (rets.Size() > 0)
{
ReturnType = TypeVoid;
}
else
{
ReturnType = rets[0];
// If more types are added to ParseNativeFunction(), add them here too.
if (rets[0] == TypeSInt32) ValueType = VAL_Int;
else if (rets[0] == TypeFloat64) ValueType = VAL_Float;
@ -3165,6 +3149,11 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
//
//==========================================================================
ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
{
return Emit(build, false);
}
ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall)
{
assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3);
@ -3191,17 +3180,17 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall)
}
}
// Get a constant register for this function
int funcaddr = build->GetConstantAddress(Function->Variants[0].Implementation, ATAG_OBJECT);
VMFunction *vmfunc = Function->Variants[0].Implementation;
int funcaddr = build->GetConstantAddress(vmfunc, ATAG_OBJECT);
// Emit the call
if (tailcall)
{ // Tail call
build->Emit(OP_TAIL_K, funcaddr, count, 0);
return ExpEmit();
}
else if (ReturnType != TypeVoid)
else if (vmfunc->Proto->ReturnTypes.Size() > 0)
{ // Call, expecting one result
assert(ReturnType != NULL);
ExpEmit reg(build, ReturnType->GetRegType());
ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType());
build->Emit(OP_CALL_K, funcaddr, count, 1);
build->Emit(OP_RESULT, 0, reg.RegType, reg.RegNum);
return reg;
@ -3331,7 +3320,7 @@ FxExpression *FxSequence::Resolve(FCompileContext &ctx)
CHECKRESOLVED();
for (unsigned i = 0; i < Expressions.Size(); ++i)
{
if (NULL == (Expressions[i] = static_cast<FxTailable *>(Expressions[i]->Resolve(ctx))))
if (NULL == (Expressions[i] = Expressions[i]->Resolve(ctx)))
{
delete this;
return NULL;
@ -3346,11 +3335,11 @@ FxExpression *FxSequence::Resolve(FCompileContext &ctx)
//
//==========================================================================
ExpEmit FxSequence::Emit(VMFunctionBuilder *build, bool tailcall)
ExpEmit FxSequence::Emit(VMFunctionBuilder *build)
{
for (unsigned i = 0; i < Expressions.Size(); ++i)
{
ExpEmit v = Expressions[i]->Emit(build, tailcall ? i == Expressions.Size()-1 : false);
ExpEmit v = Expressions[i]->Emit(build);
// Throw away any result. We don't care about it.
v.Free(build);
}
@ -3378,9 +3367,9 @@ VMFunction *FxSequence::GetDirectFunction()
//
//==========================================================================
FxIfStatement::FxIfStatement(FxExpression *cond, FxTailable *true_part,
FxTailable *false_part, const FScriptPosition &pos)
: FxTailable(pos)
FxIfStatement::FxIfStatement(FxExpression *cond, FxExpression *true_part,
FxExpression *false_part, const FScriptPosition &pos)
: FxExpression(pos)
{
Condition = cond;
WhenTrue = true_part;
@ -3407,12 +3396,12 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
ABORT(Condition);
if (WhenTrue != NULL)
{
WhenTrue = static_cast<FxTailable *>(WhenTrue->Resolve(ctx));
WhenTrue = WhenTrue->Resolve(ctx);
ABORT(WhenTrue);
}
if (WhenFalse != NULL)
{
WhenFalse = static_cast<FxTailable *>(WhenFalse->Resolve(ctx));
WhenFalse = WhenFalse->Resolve(ctx);
ABORT(WhenFalse);
}
ValueType = VAL_Unknown;
@ -3422,7 +3411,7 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
ExpVal condval = static_cast<FxConstant *>(Condition)->GetValue();
bool result = condval.GetBool();
FxTailable *e = result ? WhenTrue : WhenFalse;
FxExpression *e = result ? WhenTrue : WhenFalse;
delete (result ? WhenFalse : WhenTrue);
WhenTrue = WhenFalse = NULL;
delete this;
@ -3431,11 +3420,11 @@ FxExpression *FxIfStatement::Resolve(FCompileContext &ctx)
return this;
}
ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build, bool tailcall)
ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build)
{
ExpEmit v;
size_t jumpspot;
FxTailable *path1, *path2;
FxExpression *path1, *path2;
int condcheck;
// This is pretty much copied from FxConditional, except we don't
@ -3465,24 +3454,18 @@ ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build, bool tailcall)
cond.Free(build);
// Evaluate first path
v = path1->Emit(build, tailcall);
v = path1->Emit(build);
v.Free(build);
if (path2 != NULL)
{
size_t path1jump = build->Emit(OP_JMP, 0);
// Evaluate second path
build->BackpatchToHere(jumpspot);
v = path2->Emit(build, tailcall);
v = path2->Emit(build);
v.Free(build);
jumpspot = path1jump;
}
build->BackpatchToHere(jumpspot);
if (tailcall)
{
// When tailcall is true, execution is not expected to get past
// this if statement, so issue a RET.
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
}
return ExpEmit();
}
@ -3490,14 +3473,35 @@ ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build, bool tailcall)
//
//==========================================================================
FxReturnStatement::FxReturnStatement(const FScriptPosition &pos)
: FxTailable(pos)
FxReturnStatement::FxReturnStatement(FxVMFunctionCall *call, const FScriptPosition &pos)
: FxExpression(pos), Call(call)
{
}
ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build, bool tailcall)
FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
{
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
CHECKRESOLVED();
if (Call != NULL)
{
Call = static_cast<FxVMFunctionCall *>(Call->Resolve(ctx));
ABORT(Call);
}
return this;
}
ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
{
// If we return nothing, use a regular RET opcode. If we return
// something, use TAIL to call the function. Our return type
// should be compatible with the called function's return type.
if (Call == NULL)
{
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
}
else
{
Call->Emit(build, true);
}
return ExpEmit();
}

View File

@ -375,6 +375,42 @@ static void ParseArgListDef(FScanner &sc, PClassActor *cls,
sc.MustGetToken(';');
}
//==========================================================================
//
// SetImplicitArgs
//
// Adds the parameters implied by the function flags.
//
//==========================================================================
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClassActor *cls, DWORD funcflags)
{
// Must be called before adding any other arguments.
assert(args == NULL || args->Size() == 0);
assert(argflags == NULL || argflags->Size() == 0);
if (funcflags & VARF_Method)
{
// implied self pointer
if (args != NULL) args->Push(NewClassPointer(cls));
if (argflags != NULL) argflags->Push(0);
}
if (funcflags & VARF_Action)
{
// implied stateowner and callingstate pointers
if (args != NULL)
{
args->Push(NewClassPointer(RUNTIME_CLASS(AActor)));
args->Push(TypeState);
}
if (argflags != NULL)
{
argflags->Push(0);
argflags->Push(0);
}
}
}
//==========================================================================
//
// ParseFunctionDef
@ -401,15 +437,7 @@ void ParseFunctionDef(FScanner &sc, PClassActor *cls, FName funcname,
}
sc.MustGetToken('(');
if (funcflags & VARF_Method)
{
args.Push(NewClassPointer(cls)), argflags.Push(0); // implied self pointer
}
if (funcflags & VARF_Action)
{
args.Push(NewClassPointer(RUNTIME_CLASS(AActor))), argflags.Push(0); // implied stateowner pointer
args.Push(TypeState), argflags.Push(0); // implied callingstate pointer
}
SetImplicitArgs(&args, &argflags, cls, funcflags);
ParseArgListDef(sc, cls, args, argflags);
if (afd != NULL)
@ -1036,18 +1064,22 @@ static void ParseActionDef (FScanner &sc, PClassActor *cls)
sc.MustGetToken(TK_Native);
// check for a return value
if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Bool))
do
{
rets.Push(TypeSInt32);
}
else if (sc.CheckToken(TK_State))
{
rets.Push(TypeState);
}
else if (sc.CheckToken(TK_Float))
{
rets.Push(TypeFloat64);
if (sc.CheckToken(TK_Int) || sc.CheckToken(TK_Bool))
{
rets.Push(TypeSInt32);
}
else if (sc.CheckToken(TK_State))
{
rets.Push(TypeState);
}
else if (sc.CheckToken(TK_Float))
{
rets.Push(TypeFloat64);
}
}
while (sc.CheckToken(','));
sc.MustGetToken(TK_Identifier);
funcname = sc.String;
ParseFunctionDef(sc, cls, funcname, rets, VARF_Method | VARF_Action);

View File

@ -314,7 +314,12 @@ do_stop:
continue;
}
tcall->Code = ParseActions(sc, state, statestring, bag);
bool hasfinalret;
tcall->Code = ParseActions(sc, state, statestring, bag, tcall->Proto, hasfinalret);
if (!hasfinalret)
{
AddImplicitReturn(static_cast<FxSequence*>(tcall->Code), tcall->Proto, sc);
}
goto endofstate;
}
sc.UnGet();
@ -348,49 +353,162 @@ endofstate:
//==========================================================================
//
// ParseActions
// AddImplicitReturn
//
// Adds an implied return; statement to the end of a code sequence.
//
//==========================================================================
FxTailable *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag)
void AddImplicitReturn(FxSequence *code, const PPrototype *proto, FScanner &sc)
{
if (proto == NULL || proto->ReturnTypes.Size() == 0)
{ // Returns nothing. Good. We can safely add an implied return.
code->Add(new FxReturnStatement(NULL, sc));
}
else
{ // Something was returned earlier in the sequence. Make it an error
// instead of adding an implicit one.
sc.ScriptError("Action list must end with a return statement");
}
}
//==========================================================================
//
// ReturnCheck
//
// If proto1 is NULL, returns proto2. If proto2 is NULL, returns proto1.
// If neither is null, checks if both prototypes define the same return
// types. If not, an error is flagged.
//
//==========================================================================
static PPrototype *ReturnCheck(PPrototype *proto1, PPrototype *proto2, FScanner &sc)
{
if (proto1 == NULL)
{
return proto2;
}
if (proto2 == NULL)
{
return proto1;
}
// A prototype that defines fewer return types can be compatible with
// one that defines more if the shorter one matches the initial types
// for the longer one.
if (proto2->ReturnTypes.Size() < proto1->ReturnTypes.Size())
{ // Make proto1 the shorter one to avoid code duplication below.
swapvalues(proto1, proto2);
}
// If one prototype returns nothing, they both must.
if (proto1->ReturnTypes.Size() == 0)
{
if (proto2->ReturnTypes.Size() == 0)
{
return proto1;
}
proto1 = NULL;
}
else
{
for (unsigned i = 0; i < proto1->ReturnTypes.Size(); ++i)
{
if (proto1->ReturnTypes[i] != proto2->ReturnTypes[i])
{ // Incompatible
proto1 = NULL;
break;
}
}
}
if (proto1 == NULL)
{
sc.ScriptError("Return types are incompatible");
}
return proto1;
}
//==========================================================================
//
// ParseActions
//
// If this action block contains any return statements, the prototype for
// one of them will be returned. This is used for deducing the return type
// of anonymous functions. All called functions passed to return must have
// matching return types.
//
//==========================================================================
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag,
PPrototype *&retproto, bool &endswithret)
{
// If it's not a '{', then it should be a single action.
// Otherwise, it's a sequence of actions.
if (!sc.Compare("{"))
{
return ParseAction(sc, state, statestring, bag);
FxVMFunctionCall *call = ParseAction(sc, state, statestring, bag);
retproto = call->GetVMFunction()->Proto;
endswithret = true;
return new FxReturnStatement(call, sc);
}
const FScriptPosition pos(sc);
FxSequence *seq = NULL;
PPrototype *proto = NULL;
bool lastwasret = false;
sc.MustGetString();
while (!sc.Compare("}"))
{
FxTailable *add;
FxExpression *add;
lastwasret = false;
if (sc.Compare("if"))
{ // Hangle an if statement
FxExpression *cond;
FxTailable *true_part, *false_part = NULL;
FxExpression *true_part, *false_part = NULL;
PPrototype *true_proto, *false_proto = NULL;
bool true_ret, false_ret = false;
sc.MustGetStringName("(");
cond = ParseExpression(sc, bag.Info);
sc.MustGetStringName(")");
sc.MustGetStringName("{"); // braces are mandatory
true_part = ParseActions(sc, state, statestring, bag);
true_part = ParseActions(sc, state, statestring, bag, true_proto, true_ret);
sc.MustGetString();
if (sc.Compare("else"))
{
sc.MustGetStringName("{"); // braces are still mandatory
false_part = ParseActions(sc, state, statestring, bag);
false_part = ParseActions(sc, state, statestring, bag, false_proto, false_ret);
sc.MustGetString();
}
add = new FxIfStatement(cond, true_part, false_part, sc);
proto = ReturnCheck(proto, true_proto, sc);
proto = ReturnCheck(proto, false_proto, sc);
// If one side does not end with a return, we don't consider the if statement
// to end with a return.
if (true_ret && (false_proto == NULL || false_ret))
{
lastwasret = true;
}
}
else if (sc.Compare("return"))
{ // Handle a return statement
sc.MustGetStringName(";");
lastwasret = true;
FxVMFunctionCall *retexp = NULL;
PPrototype *retproto;
sc.MustGetString();
add = new FxReturnStatement(sc);
if (!sc.Compare(";"))
{
retexp = ParseAction(sc, state, statestring, bag);
sc.MustGetStringName(";");
retproto = retexp->GetVMFunction()->Proto;
}
else
{ // Returning nothing; we still need a prototype for that.
TArray<PType *> notypes(0);
retproto = NewPrototype(notypes, notypes);
}
proto = ReturnCheck(proto, retproto, sc);
sc.MustGetString();
add = new FxReturnStatement(retexp, sc);
}
else
{ // Handle a regular action function call
@ -408,6 +526,8 @@ FxTailable *ParseActions(FScanner &sc, FState state, FString statestring, Baggag
seq->Add(add);
}
}
endswithret = lastwasret;
retproto = proto;
return seq;
}
@ -460,7 +580,7 @@ FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, B
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
PFunction *afd, FString statestring, FStateDefinitions *statedef)
{
const TArray<PType *> &params = afd->Variants[0].Proto->ArgumentTypes;
const TArray<PType *> &params = afd->Variants[0].Implementation->Proto->ArgumentTypes;
const TArray<DWORD> &paramflags = afd->Variants[0].ArgFlags;
int numparams = (int)params.Size();
int pnum = 0;

View File

@ -162,8 +162,10 @@ public:
bool Native;
FName Name;
VMFunction() : Native(false), Name(NAME_None) {}
VMFunction(FName name) : Native(false), Name(name) {}
class PPrototype *Proto;
VMFunction() : Native(false), Name(NAME_None), Proto(NULL) {}
VMFunction(FName name) : Native(false), Name(name), Proto(NULL) {}
};
enum EVMOpMode

View File

@ -46,7 +46,7 @@ ACTOR Actor native //: Thinker
// Action functions
// Meh, MBF redundant functions. Only for DeHackEd support.
action native A_Turn(float angle = 0);
action native A_LineEffect(int boomspecial = 0, int tag = 0);
action native bool A_LineEffect(int boomspecial = 0, int tag = 0);
// End of MBF redundant functions.
action native A_MonsterRail();
@ -108,7 +108,7 @@ ACTOR Actor native //: Thinker
action native A_Die(name damagetype = "none");
action native A_Detonate();
action native A_Mushroom(class<Actor> spawntype = "FatShot", int numspawns = 0, int flags = 0, float vrange = 4.0, float hrange = 0.5);
action native A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0);
action native bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0);
action native A_SetFloorClip();
action native A_UnSetFloorClip();
@ -181,22 +181,22 @@ ACTOR Actor native //: Thinker
action native A_PlaySoundEx(sound whattoplay, coerce name slot, bool looping = false, int attenuation = 0);
action native A_StopSoundEx(coerce name slot);
action native A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10);
action native A_Jump(int chance = 256, state label, ...);
action native state A_Jump(int chance = 256, state label, ...);
action native A_CustomMissile(class<Actor> missiletype, float spawnheight = 32, int spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0, int ptr = AAPTR_TARGET);
action native A_CustomBulletAttack(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET);
action native A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, int aim = 0, float maxdiff = 0, class<Actor> pufftype = "BulletPuff", float/*angle*/ spread_xy = 0, float/*angle*/ spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class<Actor> spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270);
action native A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT);
action native A_JumpIfCloser(float distance, state label, bool noz = false);
action native A_JumpIfTracerCloser(float distance, state label, bool noz = false);
action native A_JumpIfMasterCloser(float distance, state label, bool noz = false);
action native A_JumpIfTargetOutsideMeleeRange(state label);
action native A_JumpIfTargetInsideMeleeRange(state label);
action native A_JumpIfInventory(class<Inventory> itemtype, int itemamount, state label, int owner = AAPTR_DEFAULT);
action native A_JumpIfArmorType(name Type, state label, int amount = 1);
action native A_GiveInventory(class<Inventory> itemtype, int amount = 0, int giveto = AAPTR_DEFAULT);
action native A_TakeInventory(class<Inventory> itemtype, int amount = 0, int flags = 0, int giveto = AAPTR_DEFAULT);
action native A_SpawnItem(class<Actor> itemtype = "Unknown", float distance = 0, float zheight = 0, bool useammo = true, bool transfer_translation = false);
action native A_SpawnItemEx(class<Actor> itemtype, float xofs = 0, float yofs = 0, float zofs = 0, float xvel = 0, float yvel = 0, float zvel = 0, float angle = 0, int flags = 0, int failchance = 0, int tid=0);
action native state A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT);
action native state A_JumpIfCloser(float distance, state label, bool noz = false);
action native state A_JumpIfTracerCloser(float distance, state label, bool noz = false);
action native state A_JumpIfMasterCloser(float distance, state label, bool noz = false);
action native state A_JumpIfTargetOutsideMeleeRange(state label);
action native state A_JumpIfTargetInsideMeleeRange(state label);
action native state A_JumpIfInventory(class<Inventory> itemtype, int itemamount, state label, int owner = AAPTR_DEFAULT);
action native state A_JumpIfArmorType(name Type, state label, int amount = 1);
action native bool A_GiveInventory(class<Inventory> itemtype, int amount = 0, int giveto = AAPTR_DEFAULT);
action native bool A_TakeInventory(class<Inventory> itemtype, int amount = 0, int flags = 0, int giveto = AAPTR_DEFAULT);
action native bool A_SpawnItem(class<Actor> itemtype = "Unknown", float distance = 0, float zheight = 0, bool useammo = true, bool transfer_translation = false);
action native bool A_SpawnItemEx(class<Actor> itemtype, float xofs = 0, float yofs = 0, float zofs = 0, float xvel = 0, float yvel = 0, float zvel = 0, float angle = 0, int flags = 0, int failchance = 0, int tid=0);
action native A_Print(string whattoprint, float time = 0, name fontname = "");
action native A_PrintBold(string whattoprint, float time = 0, name fontname = "");
action native A_Log(string whattoprint);
@ -209,31 +209,31 @@ ACTOR Actor native //: Thinker
action native A_SetMass(int mass);
action native A_SpawnDebris(class<Actor> spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1);
action native A_SpawnParticle(color color1, int flags = 0, int lifetime = 35, int size = 1, float angle = 0, float xoff = 0, float yoff = 0, float zoff = 0, float velx = 0, float vely = 0, float velz = 0, float accelx = 0, float accely = 0, float accelz = 0, float startalphaf = 1, float fadestepf = -1);
action native A_CheckSight(state label);
action native state A_CheckSight(state label);
action native A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false);
action native A_DropInventory(class<Inventory> itemtype);
action native A_SetBlend(color color1, float alpha, int tics, color color2 = "");
action native A_ChangeFlag(string flagname, bool value);
action native A_CheckFlag(string flagname, state label, int check_pointer = AAPTR_DEFAULT);
action native A_JumpIf(bool expression, state label);
action native state A_CheckFlag(string flagname, state label, int check_pointer = AAPTR_DEFAULT);
action native state A_JumpIf(bool expression, state label);
action native A_RaiseMaster(bool copy = 0);
action native A_RaiseChildren(bool copy = 0);
action native A_RaiseSiblings(bool copy = 0);
action native A_CheckFloor(state label);
action native A_CheckCeiling(state label);
action native A_PlayerSkinCheck(state label);
action native state A_CheckFloor(state label);
action native state A_CheckCeiling(state label);
action native state A_PlayerSkinCheck(state label);
action native A_BasicAttack(int meleedamage, sound meleesound, class<actor> missiletype, float missileheight);
action native A_Teleport(state teleportstate = "", class<SpecialSpot> targettype = "BossSpot", class<Actor> fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 0, int ptr = AAPTR_DEFAULT);
action native A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = "", float heightoffset = 0, float radiusoffset = 0, float pitch = 0);
action native A_ThrowGrenade(class<Actor> itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true);
action native state, bool A_Teleport(state teleportstate = "", class<SpecialSpot> targettype = "BossSpot", class<Actor> fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 0, int ptr = AAPTR_DEFAULT);
action native state, bool A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = "", float heightoffset = 0, float radiusoffset = 0, float pitch = 0);
action native bool A_ThrowGrenade(class<Actor> itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true);
action native A_Weave(int xspeed, int yspeed, float xdist, float ydist);
action native A_Recoil(float xyvel);
action native A_JumpIfInTargetInventory(class<Inventory> itemtype, int amount, state label, int forward_ptr = AAPTR_DEFAULT);
action native A_GiveToTarget(class<Inventory> itemtype, int amount = 0, int forward_ptr = AAPTR_DEFAULT);
action native A_TakeFromTarget(class<Inventory> itemtype, int amount = 0, int flags = 0, int forward_ptr = AAPTR_DEFAULT);
action native A_RadiusGive(class<Inventory> itemtype, float distance, int flags, int amount = 0, class<Actor> filter = "None", name species = "None", int mindist = 0);
action native A_CheckSpecies(state jump, name species = "", int ptr = AAPTR_DEFAULT);
action native state A_JumpIfInTargetInventory(class<Inventory> itemtype, int amount, state label, int forward_ptr = AAPTR_DEFAULT);
action native bool A_GiveToTarget(class<Inventory> itemtype, int amount = 0, int forward_ptr = AAPTR_DEFAULT);
action native bool A_TakeFromTarget(class<Inventory> itemtype, int amount = 0, int flags = 0, int forward_ptr = AAPTR_DEFAULT);
action native int A_RadiusGive(class<Inventory> itemtype, float distance, int flags, int amount = 0, class<Actor> filter = "None", name species = "None", int mindist = 0);
action native state A_CheckSpecies(state jump, name species = "", int ptr = AAPTR_DEFAULT);
action native A_CountdownArg(int argnum, state targstate = "");
action native A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true);
action native A_CustomComboAttack(class<Actor> missiletype, float spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true);
@ -249,10 +249,10 @@ ACTOR Actor native //: Thinker
action native A_LookEx(int flags = 0, float minseedist = 0, float maxseedist = 0, float maxheardist = 0, float fov = 0, state label = "");
action native A_ClearLastHeard();
action native A_ClearTarget();
action native A_CheckLOF(state jump, int flags = 0, float range = 0, float minrange = 0, float angle = 0, float pitch = 0, float offsetheight = 0, float offsetwidth = 0, int ptr_target = AAPTR_DEFAULT);
action native A_JumpIfTargetInLOS (state label, float/*angle*/ fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
action native A_JumpIfInTargetLOS (state label, float/*angle*/ fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
action native A_SelectWeapon(class<Weapon> whichweapon);
action native state A_CheckLOF(state jump, int flags = 0, float range = 0, float minrange = 0, float angle = 0, float pitch = 0, float offsetheight = 0, float offsetwidth = 0, int ptr_target = AAPTR_DEFAULT);
action native state A_JumpIfTargetInLOS (state label, float/*angle*/ fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
action native state A_JumpIfInTargetLOS (state label, float/*angle*/ fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
action native bool A_SelectWeapon(class<Weapon> whichweapon);
action native A_Punch();
action native A_Feathers();
action native A_ClassBossHealth();
@ -264,7 +264,7 @@ ACTOR Actor native //: Thinker
action native A_RemoveForcefield();
action native A_DropWeaponPieces(class<Actor> p1, class<Actor> p2, class<Actor> p3);
action native A_PigPain ();
action native A_MonsterRefire(int chance, state label);
action native state A_MonsterRefire(int chance, state label);
action native A_SetAngle(float angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
action native A_SetPitch(float pitch, int flags = 0, int ptr = AAPTR_DEFAULT);
action native A_SetRoll(float/*angle*/ roll, int flags = 0, int ptr = AAPTR_DEFAULT);
@ -299,37 +299,37 @@ ACTOR Actor native //: Thinker
action native A_RemoveChildren(bool removeall = false, int flags = 0, class<Actor> filter = "None", name species = "None");
action native A_RemoveSiblings(bool removeall = false, int flags = 0, class<Actor> filter = "None", name species = "None");
action native A_Remove(int removee, int flags = 0, class<Actor> filter = "None", name species = "None");
action native A_GiveToChildren(class<Inventory> itemtype, int amount = 0);
action native A_GiveToSiblings(class<Inventory> itemtype, int amount = 0);
action native A_TakeFromChildren(class<Inventory> itemtype, int amount = 0);
action native A_TakeFromSiblings(class<Inventory> itemtype, int amount = 0);
action native int A_GiveToChildren(class<Inventory> itemtype, int amount = 0);
action native int A_GiveToSiblings(class<Inventory> itemtype, int amount = 0);
action native int A_TakeFromChildren(class<Inventory> itemtype, int amount = 0);
action native int A_TakeFromSiblings(class<Inventory> itemtype, int amount = 0);
action native A_SetTeleFog(class<Actor> oldpos, class<Actor> newpos);
action native A_SwapTeleFog();
action native A_SetFloatBobPhase(int bob);
action native A_SetHealth(int health, int ptr = AAPTR_DEFAULT);
action native A_ResetHealth(int ptr = AAPTR_DEFAULT);
action native A_JumpIfHigherOrLower(state high, state low, float offsethigh = 0, float offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET);
action native state A_JumpIfHigherOrLower(state high, state low, float offsethigh = 0, float offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET);
action native A_SetSpecies(name species, int ptr = AAPTR_DEFAULT);
action native A_SetRipperLevel(int level);
action native A_SetRipMin(int min);
action native A_SetRipMax(int max);
action native A_SetChaseThreshold(int threshold, bool def = false, int ptr = AAPTR_DEFAULT);
action native A_CheckProximity(state jump, class<Actor> classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT);
action native A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT);
action native A_CheckSightOrRange(float distance, state label, bool two_dimension = false);
action native A_CheckRange(float distance, state label, bool two_dimension = false);
action native A_FaceMovementDirection(float offset = 0, float anglelimit = 0, float pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
action native state A_CheckProximity(state jump, class<Actor> classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT);
action native state A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT);
action native state A_CheckSightOrRange(float distance, state label, bool two_dimension = false);
action native state A_CheckRange(float distance, state label, bool two_dimension = false);
action native bool A_FaceMovementDirection(float offset = 0, float anglelimit = 0, float pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
action native A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0);
action native A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0);
action native A_CopyFriendliness(int ptr_source = AAPTR_MASTER);
action native ACS_NamedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0);
action native ACS_NamedSuspend(name script, int mapnum=0);
action native ACS_NamedTerminate(name script, int mapnum=0);
action native ACS_NamedLockedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int lock=0);
action native ACS_NamedLockedExecuteDoor(name script, int mapnum=0, int arg1=0, int arg2=0, int lock=0);
action native ACS_NamedExecuteWithResult(name script, int arg1=0, int arg2=0, int arg3=0, int arg4=0);
action native int ACS_NamedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0);
action native int ACS_NamedSuspend(name script, int mapnum=0);
action native int ACS_NamedTerminate(name script, int mapnum=0);
action native int ACS_NamedLockedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int lock=0);
action native int ACS_NamedLockedExecuteDoor(name script, int mapnum=0, int arg1=0, int arg2=0, int lock=0);
action native int ACS_NamedExecuteWithResult(name script, int arg1=0, int arg2=0, int arg3=0, int arg4=0);
action native ACS_NamedExecuteAlways(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0);
States

View File

@ -7,7 +7,7 @@ ACTOR Inventory native
Inventory.PickupSound "misc/i_pkup"
Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG"
action native A_JumpIfNoAmmo(state label);
action native state A_JumpIfNoAmmo(state label);
action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class<Actor> pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus", sound MeleeSound = "", sound MissSound = "");
action native A_FireBullets(float/*angle*/ spread_xy, float/*angle*/ spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", int flags = 1, float range = 0);
action native A_FireCustomMissile(class<Actor> missiletype, float angle = 0, bool useammo = true, int spawnofs_xy = 0, float spawnheight = 0, int flags = 0, float pitch = 0);
@ -42,7 +42,7 @@ ACTOR Inventory native
action native A_CheckReload();
action native A_GunFlash(state flash = "", int flags = 0);
action native A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff", int flags = 0, float range = 0, float/*angle*/ spread_xy = 2.8125, float/*angle*/ spread_z = 0, float lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus");
action native A_CheckForReload(int counter, state label, bool dontincrement = false);
action native state A_CheckForReload(int counter, state label, bool dontincrement = false);
action native A_ResetReloadCounter();
action native A_RestoreSpecialPosition();
action native A_RestoreSpecialDoomThing();