- fixed: State labels were resolved in the calling function's context instead of the called function one's.

This could cause problems with functions that take states as parameters but use them to set them internally instead of passing them through the A_Jump interface back to the caller, like A_Chase or A_LookEx.
This required some quite significant refactoring because the entire state resolution logic had been baked into the compiler which turned out to be a major maintenance problem.
Fixed this by adding a new builtin type 'statelabel'. This is an opaque identifier representing a state, with the actual data either directly encoded into the number for single label state or an index into a state information table.
The state resolution is now the task of the called function as it should always have remained. Note, that this required giving back the 'action' qualifier to most state jumping functions.

- refactored most A_Jump checkers to a two stage setup with a pure checker that returns a boolean and a scripted A_Jump wrapper, for some simpler checks the checker function was entirely omitted and calculated inline in the A_Jump function. It is strongly recommended to use the boolean checkers unless using an inline function invocation in a state as they lead to vastly clearer code and offer more flexibility.

- let Min() and Max() use the OP_MIN and OP_MAX opcodes. Although these were present, these function were implemented using some grossly inefficient branching tests.
- the DECORATE 'state' cast kludge will now actually call ResolveState because a state label is not a state and needs conversion.
This commit is contained in:
Christoph Oelckers 2016-11-14 14:12:27 +01:00
parent 1e6a632774
commit ac86a535e7
25 changed files with 641 additions and 646 deletions

View file

@ -1494,7 +1494,7 @@ static int PatchFrame (int frameNum)
DEFINE_ACTION_FUNCTION(AActor, isDEHState)
{
PARAM_PROLOGUE;
PARAM_STATE(state);
PARAM_POINTER(state, FState);
ACTION_RETURN_BOOL(state != nullptr && (state->DefineFlags & SDF_DEHACKED));
}

View file

@ -82,6 +82,7 @@ PName *TypeName;
PSound *TypeSound;
PColor *TypeColor;
PStatePointer *TypeState;
PStateLabel *TypeStateLabel;
PStruct *TypeVector2;
PStruct *TypeVector3;
PPointer *TypeNullPtr;
@ -546,7 +547,8 @@ void PType::StaticInit()
RUNTIME_CLASS(PPrototype)->TypeTableType = RUNTIME_CLASS(PPrototype);
RUNTIME_CLASS(PClass)->TypeTableType = RUNTIME_CLASS(PClass);
RUNTIME_CLASS(PStatePointer)->TypeTableType = RUNTIME_CLASS(PStatePointer);
RUNTIME_CLASS(PStateLabel)->TypeTableType = RUNTIME_CLASS(PStateLabel);
// Create types and add them type the type table.
TypeTable.AddType(TypeError = new PErrorType);
TypeTable.AddType(TypeVoid = new PVoidType);
@ -564,6 +566,7 @@ void PType::StaticInit()
TypeTable.AddType(TypeSound = new PSound);
TypeTable.AddType(TypeColor = new PColor);
TypeTable.AddType(TypeState = new PStatePointer);
TypeTable.AddType(TypeStateLabel = new PStateLabel);
TypeTable.AddType(TypeNullPtr = new PPointer);
TypeVector2 = new PStruct(NAME_Vector2, nullptr);
@ -701,7 +704,7 @@ IMPLEMENT_CLASS(PInt, false, false, false, false)
//==========================================================================
PInt::PInt()
: PBasicType(4, 4), Unsigned(false)
: PBasicType(4, 4), Unsigned(false), IntCompatible(true)
{
mDescriptiveName = "SInt32";
Symbols.AddSymbol(new PSymbolConstNumeric(NAME_Min, this, -0x7FFFFFFF - 1));
@ -715,8 +718,8 @@ PInt::PInt()
//
//==========================================================================
PInt::PInt(unsigned int size, bool unsign)
: PBasicType(size, size), Unsigned(unsign)
PInt::PInt(unsigned int size, bool unsign, bool compatible)
: PBasicType(size, size), Unsigned(unsign), IntCompatible(compatible)
{
mDescriptiveName.Format("%cInt%d", unsign? 'U':'S', size);
@ -1308,7 +1311,7 @@ IMPLEMENT_CLASS(PName, false, false, false, false)
//==========================================================================
PName::PName()
: PInt(sizeof(FName), true)
: PInt(sizeof(FName), true, false)
{
mDescriptiveName = "Name";
assert(sizeof(FName) == __alignof(FName));
@ -1414,6 +1417,22 @@ PColor::PColor()
assert(sizeof(PalEntry) == __alignof(PalEntry));
}
/* PStateLabel *****************************************************************/
IMPLEMENT_CLASS(PStateLabel, false, false, false, false)
//==========================================================================
//
// PStateLabel Default Constructor
//
//==========================================================================
PStateLabel::PStateLabel()
: PInt(sizeof(int), false, false)
{
mDescriptiveName = "StateLabel";
}
/* PStatePointer **********************************************************/
IMPLEMENT_CLASS(PStatePointer, false, false, false, false)

View file

@ -227,6 +227,7 @@ public:
PType(unsigned int size = 1, unsigned int align = 1);
virtual ~PType();
virtual bool isNumeric() { return false; }
bool AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &));
@ -417,7 +418,7 @@ class PInt : public PBasicType
{
DECLARE_CLASS(PInt, PBasicType);
public:
PInt(unsigned int size, bool unsign);
PInt(unsigned int size, bool unsign, bool compatible = true);
void WriteValue(FSerializer &ar, const char *key,const void *addr) const override;
bool ReadValue(FSerializer &ar, const char *key,void *addr) const override;
@ -426,8 +427,10 @@ public:
virtual void SetValue(void *addr, double val);
virtual int GetValueInt(void *addr) const;
virtual double GetValueFloat(void *addr) const;
virtual bool isNumeric() override { return IntCompatible; }
bool Unsigned;
bool IntCompatible;
protected:
PInt();
void SetOps();
@ -453,6 +456,7 @@ public:
virtual void SetValue(void *addr, double val);
virtual int GetValueInt(void *addr) const;
virtual double GetValueFloat(void *addr) const;
virtual bool isNumeric() override { return true; }
protected:
PFloat();
void SetOps();
@ -516,6 +520,13 @@ public:
PColor();
};
class PStateLabel : public PInt
{
DECLARE_CLASS(PStateLabel, PInt);
public:
PStateLabel();
};
// Pointers -----------------------------------------------------------------
class PStatePointer : public PBasicType
@ -903,6 +914,7 @@ extern PColor *TypeColor;
extern PStruct *TypeVector2;
extern PStruct *TypeVector3;
extern PStatePointer *TypeState;
extern PStateLabel *TypeStateLabel;
extern PPointer *TypeNullPtr;
// A constant value ---------------------------------------------------------

View file

@ -313,6 +313,44 @@ struct FDoomEdEntry
int Args[5];
};
struct FStateLabelStorage
{
TArray<uint8_t> Storage;
int AddPointer(FState *ptr)
{
if (ptr != nullptr)
{
int pos = Storage.Reserve(sizeof(ptr) + sizeof(int));
memset(&Storage[pos], 0, sizeof(int));
memcpy(&Storage[pos + sizeof(int)], &ptr, sizeof(ptr));
return pos / 4 + 1;
}
else return 0;
}
int AddNames(TArray<FName> &names)
{
int siz = names.Size();
if (siz > 1)
{
int pos = Storage.Reserve(sizeof(int) + sizeof(FName) * names.Size());
memcpy(&Storage[pos], &siz, sizeof(int));
memcpy(&Storage[pos + sizeof(int)], &names[0], sizeof(FName) * names.Size());
return pos / 4 + 1;
}
else
{
// don't store single name states in the array.
return names[0].GetIndex() + 0x10000000;
}
}
FState *GetState(int pos, PClassActor *cls);
};
extern FStateLabelStorage StateLabels;
enum ESpecialMapthings
{
SMT_Player1Start = 1,

View file

@ -730,10 +730,10 @@ xx(stateinfo)
xx(__decorate_internal_int__)
xx(__decorate_internal_bool__)
xx(__decorate_internal_state__)
xx(__decorate_internal_float__)
xx(ResolveState)
xx(DamageFunction)
xx(Length)
xx(Unit)
xx(A_Jump)
xx(StateLabel)

View file

@ -715,7 +715,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CountProximity)
//===========================================================================
//
// __decorate_internal_state__
// __decorate_internal_int__
// __decorate_internal_bool__
// __decorate_internal_float__
@ -725,13 +724,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, CountProximity)
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_state__)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(returnme);
ACTION_RETURN_STATE(returnme);
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, __decorate_internal_int__)
{
PARAM_SELF_PROLOGUE(AActor);
@ -1158,7 +1150,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BulletAttack)
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_ACTION_PROLOGUE(AActor);
PARAM_INT(maxchance);
paramnum++; // Increment paramnum to point at the first jump target
@ -1166,7 +1158,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump)
if (count > 0 && (maxchance >= 256 || pr_cajump() < maxchance))
{
int jumpnum = (count == 1 ? 0 : (pr_cajump() % count));
PARAM_STATE_AT(paramnum + jumpnum, jumpto);
PARAM_STATE_ACTION_AT(paramnum + jumpnum, jumpto);
ACTION_RETURN_STATE(jumpto);
}
ACTION_RETURN_STATE(NULL);
@ -1177,137 +1169,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Jump)
// State jump function
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_INT (health);
PARAM_STATE (jump);
PARAM_INT_DEF (ptr_selector);
AActor *measured;
measured = COPY_AAPTR(self, ptr_selector);
if (measured != NULL && measured->health < health)
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
}
//==========================================================================
//
// State jump function
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetOutsideMeleeRange)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
if (!self->CheckMeleeRange())
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
}
//==========================================================================
//
// State jump function
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
if (self->CheckMeleeRange())
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
}
//==========================================================================
//
// State jump function
//
//==========================================================================
static int DoJumpIfCloser(AActor *target, VM_ARGS)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_FLOAT (dist);
PARAM_STATE (jump);
PARAM_BOOL_DEF(noz);
if (!target)
{ // No target - no jump
ACTION_RETURN_STATE(NULL);
}
if (self->Distance2D(target) < dist &&
(noz ||
((self->Z() > target->Z() && self->Z() - target->Top() < dist) ||
(self->Z() <= target->Z() && target->Z() - self->Top() < dist))))
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *target;
if (self->player == NULL)
{
target = self->target;
}
else
{
// Does the player aim at something that can be shot?
FTranslatedLineTarget t;
P_BulletSlope(self, &t, ALF_PORTALRESTRICT);
target = t.linetarget;
}
return DoJumpIfCloser(target, VM_ARGS_NAMES);
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser)
{
PARAM_SELF_PROLOGUE(AActor);
return DoJumpIfCloser(self->tracer, VM_ARGS_NAMES);
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser)
{
PARAM_SELF_PROLOGUE(AActor);
return DoJumpIfCloser(self->master, VM_ARGS_NAMES);
}
//==========================================================================
//
// State jump function
//
//==========================================================================
int DoJumpIfInventory(AActor *owner, VM_ARGS)
DEFINE_ACTION_FUNCTION(AActor, CheckInventory)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS (itemtype, AInventory);
PARAM_INT (itemamount);
PARAM_STATE (label);
PARAM_INT_DEF (setowner) { setowner = AAPTR_DEFAULT; }
PARAM_INT_DEF (setowner);
if (itemtype == NULL)
if (itemtype == nullptr)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
owner = COPY_AAPTR(owner, setowner);
if (owner == NULL)
AActor *owner = COPY_AAPTR(self, setowner);
if (owner == nullptr)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
AInventory *item = owner->FindInventory(itemtype);
@ -1318,48 +1195,32 @@ int DoJumpIfInventory(AActor *owner, VM_ARGS)
{
if (item->Amount >= itemamount)
{
ACTION_RETURN_STATE(label);
ACTION_RETURN_BOOL(true);
}
}
else if (item->Amount >= item->MaxAmount)
{
ACTION_RETURN_STATE(label);
ACTION_RETURN_BOOL(true);
}
}
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInventory)
{
PARAM_SELF_PROLOGUE(AActor);
return DoJumpIfInventory(self, VM_ARGS_NAMES);
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetInventory)
{
PARAM_SELF_PROLOGUE(AActor);
return DoJumpIfInventory(self->target, VM_ARGS_NAMES);
}
//==========================================================================
//
// State jump function
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfArmorType)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckArmorType)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_NAME (type);
PARAM_STATE (label);
PARAM_INT_DEF(amount);
ABasicArmor *armor = (ABasicArmor *)self->FindInventory(NAME_BasicArmor);
if (armor && armor->ArmorType == type && armor->Amount >= amount)
{
ACTION_RETURN_STATE(label);
}
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(armor && armor->ArmorType == type && armor->Amount >= amount);
}
//==========================================================================
@ -1910,7 +1771,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomComboAttack)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_STATE(jump);
PARAM_STATE_ACTION(jump);
if (!ACTION_CALL_FROM_PSPRITE() || self->player->ReadyWeapon == nullptr)
{
@ -3637,10 +3498,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnParticle)
// jumps if no player can see this actor
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfSeen)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
for (int i = 0; i < MAXPLAYERS; i++)
{
@ -3649,17 +3509,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSight)
// Always check sight from each player.
if (P_CheckSight(players[i].mo, self, SF_IGNOREVISIBILITY))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
// If a player is viewing from a non-player, then check that too.
if (players[i].camera != NULL && players[i].camera->player == NULL &&
P_CheckSight(players[i].camera, self, SF_IGNOREVISIBILITY))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
}
}
ACTION_RETURN_STATE(jump);
ACTION_RETURN_BOOL(true);
}
//===========================================================================
@ -3706,11 +3566,10 @@ static bool DoCheckSightOrRange(AActor *self, AActor *camera, double range, bool
return false;
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckSightOrRange)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_FLOAT(range);
PARAM_STATE(jump);
PARAM_BOOL_DEF(twodi);
range *= range;
@ -3721,25 +3580,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSightOrRange)
// Always check from each player.
if (DoCheckSightOrRange(self, players[i].mo, range, twodi, true))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
// If a player is viewing from a non-player, check that too.
if (players[i].camera != NULL && players[i].camera->player == NULL &&
DoCheckSightOrRange(self, players[i].camera, range, twodi, true))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
}
}
ACTION_RETURN_STATE(jump);
ACTION_RETURN_BOOL(true);
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckRange)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_FLOAT(range);
PARAM_STATE(jump);
PARAM_BOOL_DEF(twodi);
range *= range;
@ -3750,17 +3608,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckRange)
// Always check from each player.
if (DoCheckSightOrRange(self, players[i].mo, range, twodi, false))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
// If a player is viewing from a non-player, check that too.
if (players[i].camera != NULL && players[i].camera->player == NULL &&
DoCheckSightOrRange(self, players[i].camera, range, twodi, false))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
}
}
ACTION_RETURN_STATE(jump);
ACTION_RETURN_BOOL(false);
}
@ -3813,20 +3671,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetBlend)
}
//===========================================================================
//
// A_JumpIf
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIf)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_BOOL (condition);
PARAM_STATE (jump);
ACTION_RETURN_STATE(condition ? jump : NULL);
}
//===========================================================================
//
// A_CountdownArg
@ -3917,43 +3761,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst)
return 0;
}
//===========================================================================
//
// A_CheckFloor
// [GRB] Jumps if actor is standing on floor
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFloor)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
if (self->Z() <= self->floorz)
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
}
//===========================================================================
//
// A_CheckCeiling
// [GZ] Totally copied from A_CheckFloor, jumps if actor touches ceiling
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckCeiling)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
if (self->Top() >= self->ceilingz) // Height needs to be counted
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
}
//===========================================================================
//
// A_Stop
@ -4077,17 +3884,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn)
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PlayerSkinCheck)
DEFINE_ACTION_FUNCTION(AActor, PlayerSkinCheck)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
if (self->player != NULL &&
skins[self->player->userinfo.GetSkin()].othergame)
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(self->player != NULL &&
skins[self->player->userinfo.GetSkin()].othergame);
}
// [KS] *** Start of my modifications ***
@ -4220,7 +4022,7 @@ ETraceStatus CheckLOFTraceFunc(FTraceResults &trace, void *userdata)
return TRACE_Abort;
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckLOF)
{
// Check line of fire
@ -4233,7 +4035,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF)
DVector3 vel;
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE (jump);
PARAM_INT_DEF (flags)
PARAM_FLOAT_DEF (range)
PARAM_FLOAT_DEF (minrange)
@ -4290,7 +4091,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF)
double distance = self->Distance3D(target);
if (distance > range)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
}
@ -4339,7 +4140,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF)
}
else
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
double cp = pitch.Cos();
@ -4377,7 +4178,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF)
{
if (minrange > 0 && trace.Distance < minrange)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if ((trace.HitType == TRACE_HitActor) && (trace.Actor != NULL) && !(lof_data.BadActor))
{
@ -4385,9 +4186,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF)
if (flags & (CLOFF_SETMASTER)) self->master = trace.Actor;
if (flags & (CLOFF_SETTRACER)) self->tracer = trace.Actor;
}
ACTION_RETURN_STATE(jump);
ACTION_RETURN_BOOL(true);
}
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
//==========================================================================
@ -4422,10 +4223,9 @@ enum JLOS_flags
JLOSF_CHECKTRACER = 1 << 12,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfTargetInLOS)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE (jump);
PARAM_ANGLE_DEF (fov)
PARAM_INT_DEF (flags)
PARAM_FLOAT_DEF (dist_max)
@ -4456,11 +4256,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
if (target == NULL)
{ // [KS] Let's not call P_CheckSight unnecessarily in this case.
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
doCheckSight = !(flags & JLOSF_NOSIGHT);
@ -4472,7 +4272,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
if (!t.linetarget)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
target = t.linetarget;
@ -4498,24 +4298,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
// [FDARI] If target is not a combatant, don't jump
if ( (flags & JLOSF_COMBATANTONLY) && (!target->player) && !(target->flags3 & MF3_ISMONSTER))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
// [FDARI] If actors share team, don't jump
if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
double distance = self->Distance3D(target);
if (dist_max && (distance > dist_max))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if (dist_close && (distance < dist_close))
{
if (flags & JLOSF_CLOSENOJUMP)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if (flags & JLOSF_CLOSENOFOV)
fov = 0.;
@ -4529,7 +4329,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
if (doCheckSight && !P_CheckSight (viewport, target, SF_IGNOREVISIBILITY))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if (flags & JLOSF_FLIPFOV)
@ -4546,10 +4346,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
if (an > (fov / 2))
{
ACTION_RETURN_STATE(NULL); // [KS] Outside of FOV - return
ACTION_RETURN_BOOL(false); // [KS] Outside of FOV - return
}
}
ACTION_RETURN_STATE(jump);
ACTION_RETURN_BOOL(true);
}
@ -4560,10 +4360,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckIfInTargetLOS)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE (jump);
PARAM_ANGLE_DEF (fov)
PARAM_INT_DEF (flags)
PARAM_FLOAT_DEF (dist_max)
@ -4589,19 +4388,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS)
if (target == NULL)
{ // [KS] Let's not call P_CheckSight unnecessarily in this case.
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
double distance = self->Distance3D(target);
if (dist_max && (distance > dist_max))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
bool doCheckSight = !(flags & JLOSF_NOSIGHT);
@ -4610,7 +4409,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS)
{
if (flags & JLOSF_CLOSENOJUMP)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if (flags & JLOSF_CLOSENOFOV)
fov = 0.;
@ -4625,14 +4424,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS)
if (an > (fov / 2))
{
ACTION_RETURN_STATE(NULL); // [KS] Outside of FOV - return
ACTION_RETURN_BOOL(false); // [KS] Outside of FOV - return
}
}
if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY))
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
ACTION_RETURN_STATE(jump);
ACTION_RETURN_BOOL(true);
}
//===========================================================================
@ -4650,7 +4449,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckForReload)
ACTION_RETURN_STATE(NULL);
}
PARAM_INT (count);
PARAM_STATE (jump);
PARAM_STATE_ACTION (jump);
PARAM_BOOL_DEF (dontincrement);
if (numret > 0)
@ -4727,24 +4526,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag)
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckFlag)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STRING (flagname);
PARAM_STATE (jumpto);
PARAM_INT_DEF (checkpointer);
AActor *owner = COPY_AAPTR(self, checkpointer);
if (owner == NULL)
{
ACTION_RETURN_STATE(NULL);
}
if (CheckActorFlag(owner, flagname))
{
ACTION_RETURN_STATE(jumpto);
}
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(owner != nullptr && CheckActorFlag(owner, flagname));
}
@ -5175,7 +4964,7 @@ enum T_Flags
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_ACTION_PROLOGUE(AActor);
PARAM_STATE_DEF (teleport_state)
PARAM_CLASS_DEF (target_type, ASpecialSpot)
PARAM_CLASS_DEF (fog_type, AActor)
@ -5605,7 +5394,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_ACTION_PROLOGUE(AActor);
PARAM_INT(destination_selector);
PARAM_FLOAT_DEF(xofs)
PARAM_FLOAT_DEF(yofs)
@ -5902,27 +5691,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive)
ACTION_RETURN_INT(given);
}
//===========================================================================
//
// A_CheckSpecies
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckSpecies)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
PARAM_NAME_DEF(species);
PARAM_INT_DEF(ptr);
AActor *mobj = COPY_AAPTR(self, ptr);
if (mobj != NULL && jump && mobj->GetSpecies() == species)
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
}
//==========================================================================
//
// A_SetTics
@ -6666,44 +6434,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ResetHealth)
return 0;
}
//===========================================================================
// A_JumpIfHigherOrLower
//
// Jumps if a target, master, or tracer is higher or lower than the calling
// actor. Can also specify how much higher/lower the actor needs to be than
// itself. Can also take into account the height of the actor in question,
// depending on which it's checking. This means adding height of the
// calling actor's self if the pointer is higher, or height of the pointer
// if its lower.
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHigherOrLower)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(high);
PARAM_STATE(low);
PARAM_FLOAT_DEF(offsethigh);
PARAM_FLOAT_DEF(offsetlow);
PARAM_BOOL_DEF(includeHeight);
PARAM_INT_DEF(ptr);
AActor *mobj = COPY_AAPTR(self, ptr);
if (mobj != NULL && mobj != self) //AAPTR_DEFAULT is completely useless in this regard.
{
if ((high) && (mobj->Z() > ((includeHeight ? self->Height : 0) + self->Z() + offsethigh)))
{
ACTION_RETURN_STATE(high);
}
else if ((low) && (mobj->Z() + (includeHeight ? mobj->Height : 0)) < (self->Z() + offsetlow))
{
ACTION_RETURN_STATE(low);
}
}
ACTION_RETURN_STATE(NULL);
}
//===========================================================================
// A_SetSpecies(str species, ptr)
//
@ -6760,29 +6490,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetChaseThreshold)
// Checks to see if a certain actor class is close to the
// actor/pointer within distance, in numbers.
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckProximity)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckProximity)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(jump);
PARAM_CLASS(classname, AActor);
PARAM_FLOAT(distance);
PARAM_INT_DEF(count);
PARAM_INT_DEF(flags);
PARAM_INT_DEF(ptr);
if (!jump)
{
if (!(flags & (CPXF_SETTARGET | CPXF_SETMASTER | CPXF_SETTRACER)))
{
ACTION_RETURN_STATE(NULL);
}
}
if (P_Thing_CheckProximity(self, classname, distance, count, flags, ptr) && jump)
{
ACTION_RETURN_STATE(jump);
}
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(!!P_Thing_CheckProximity(self, classname, distance, count, flags, ptr));
}
/*===========================================================================
@ -6806,10 +6523,9 @@ enum CBF
CBF_ABSOLUTEANGLE = 1 << 8, //Absolute angle for offsets.
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock)
DEFINE_ACTION_FUNCTION_PARAMS(AActor, CheckBlock)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(block)
PARAM_INT_DEF(flags)
PARAM_INT_DEF(ptr)
PARAM_FLOAT_DEF(xofs)
@ -6822,7 +6538,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock)
//Needs at least one state jump to work.
if (!mobj)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if (!(flags & CBF_ABSOLUTEANGLE))
@ -6872,7 +6588,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock)
if (checker)
{
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL(false);
}
if (mobj->BlockingMobj)
@ -6886,23 +6602,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock)
}
}
//[MC] If modders don't want jumping, but just getting the pointer, only abort at
//this point. I.e. A_CheckBlock("",CBF_SETTRACER) is like having CBF_NOLINES.
//It gets the mobj blocking, if any, and doesn't jump at all.
if (!block)
{
ACTION_RETURN_STATE(NULL);
}
//[MC] I don't know why I let myself be persuaded not to include a flag.
//If an actor is loaded with pointers, they don't really have any options to spare.
//Also, fail if a dropoff or a step is too great to pass over when checking for dropoffs.
if ((!(flags & CBF_NOACTORS) && (mobj->BlockingMobj)) || (!(flags & CBF_NOLINES) && mobj->BlockingLine != NULL) ||
((flags & CBF_DROPOFF) && !checker))
{
ACTION_RETURN_STATE(block);
}
ACTION_RETURN_STATE(NULL);
ACTION_RETURN_BOOL((!(flags & CBF_NOACTORS) && (mobj->BlockingMobj)) || (!(flags & CBF_NOLINES) && mobj->BlockingLine != NULL) ||
((flags & CBF_DROPOFF) && !checker));
}
//===========================================================================

View file

@ -159,6 +159,7 @@ extern FFlagDef ActorFlagDefs[];
void AActor::InitNativeFields()
{
PType *TypePlayer = NewPointer(TypeVoid); // placeholder
PType *TypeActor = NewPointer(RUNTIME_CLASS(AActor));
PType *TypeActorClass = NewClassPointer(RUNTIME_CLASS(AActor));
PType *TypeInventory = NewPointer(RUNTIME_CLASS(AInventory));
@ -168,6 +169,7 @@ void AActor::InitNativeFields()
auto meta = RUNTIME_CLASS(AActor);
meta->AddNativeField("Player", TypePlayer, myoffsetof(AActor, player));
meta->AddNativeField("Pos", TypeVector3, myoffsetof(AActor, __Pos), VARF_ReadOnly);
meta->AddNativeField(NAME_X, TypeFloat64, myoffsetof(AActor, __Pos.X), VARF_ReadOnly | VARF_Deprecated); // must remain read-only!
meta->AddNativeField(NAME_Y, TypeFloat64, myoffsetof(AActor, __Pos.Y), VARF_ReadOnly | VARF_Deprecated); // must remain read-only!
@ -694,7 +696,7 @@ bool AActor::SetState (FState *newstate, bool nofunction)
DEFINE_ACTION_FUNCTION(AActor, SetState)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(state);
PARAM_POINTER(state, FState);
PARAM_BOOL_DEF(nofunction);
ACTION_RETURN_BOOL(self->SetState(state, nofunction));
};

View file

@ -886,7 +886,7 @@ static void P_CheckWeaponButtons (player_t *player)
DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_ReFire)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_STATE_DEF(state);
PARAM_STATE_ACTION_DEF(state);
A_ReFire(self, state);
return 0;
}
@ -1224,7 +1224,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Overlay)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_INT (layer);
PARAM_STATE_DEF(state);
PARAM_STATE_ACTION_DEF(state);
PARAM_BOOL_DEF(dontoverride);
player_t *player = self->player;
@ -1294,7 +1294,7 @@ enum GF_Flags
DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_GunFlash)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_STATE_DEF(flash);
PARAM_STATE_ACTION_DEF(flash);
PARAM_INT_DEF(flags);
player_t *player = self->player;
@ -1361,6 +1361,19 @@ DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget, int aimfla
return pitch;
}
AActor *P_AimTarget(AActor *mo)
{
FTranslatedLineTarget t;
P_BulletSlope(mo, &t, ALF_PORTALRESTRICT);
return t.linetarget;
}
DEFINE_ACTION_FUNCTION(AActor, AimTarget)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_OBJECT(P_AimTarget(self));
}
//
// P_GunShot

View file

@ -110,6 +110,7 @@ void P_FireWeapon (player_t *player);
void P_DropWeapon (player_t *player);
void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac);
DAngle P_BulletSlope (AActor *mo, FTranslatedLineTarget *pLineTarget = NULL, int aimflags = 0);
AActor *P_AimTarget(AActor *mo);
void P_GunShot (AActor *mo, bool accurate, PClassActor *pufftype, DAngle pitch);

View file

@ -42,6 +42,9 @@
#include "vm.h"
#include "thingdef.h"
// stores indices for symbolic state labels for some old-style DECORATE functions.
FStateLabelStorage StateLabels;
// Each state is owned by an actor. Actors can own any number of
// states, but a single state cannot be owned by more than one
// actor. States are archived by recording the actor they belong
@ -258,7 +261,94 @@ FState *PClassActor::FindStateByString(const char *name, bool exact)
}
//==========================================================================
//
// validate a runtime state index.
//
//==========================================================================
static bool VerifyJumpTarget(PClassActor *cls, FState *CallingState, int index)
{
while (cls != RUNTIME_CLASS(AActor))
{
// both calling and target state need to belong to the same class.
if (cls->OwnsState(CallingState))
{
return cls->OwnsState(CallingState + index);
}
// We can safely assume the ParentClass is of type PClassActor
// since we stop when we see the Actor base class.
cls = static_cast<PClassActor *>(cls->ParentClass);
}
return false;
}
//==========================================================================
//
// Get a statw pointer from a symbolic label
//
//==========================================================================
FState *FStateLabelStorage::GetState(int pos, PClassActor *cls)
{
if (pos > 0x10000000)
{
return cls? cls->FindState(ENamedName(pos - 0x10000000)) : nullptr;
}
else if (pos < 0)
{
// decode the combined value produced by the script.
int index = (pos >> 16) & 32767;
pos = ((pos & 65535) - 1) * 4;
FState *state;
memcpy(&state, &Storage[pos + sizeof(int)], sizeof(state));
if (VerifyJumpTarget(cls, state, index))
return state + index;
else
return nullptr;
}
else if (pos > 0)
{
int val;
pos = (pos - 1) * 4;
memcpy(&val, &Storage[pos], sizeof(int));
if (val == 0)
{
FState *state;
memcpy(&state, &Storage[pos + sizeof(int)], sizeof(state));
return state;
}
else if (cls != nullptr)
{
FName *labels = (FName*)&Storage[pos + sizeof(int)];
return cls->FindState(val, labels, false);
}
}
return nullptr;
}
//==========================================================================
//
// State label conversion function for scripts
//
//==========================================================================
DEFINE_ACTION_FUNCTION(AActor, FindState)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(newstate);
ACTION_RETURN_STATE(newstate);
}
// same as above but context aware.
DEFINE_ACTION_FUNCTION_PARAMS(AActor, ResolveState)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_STATE_ACTION(newstate);
ACTION_RETURN_STATE(newstate);
}
//==========================================================================
//

View file

@ -812,7 +812,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx)
if (basex->ValueType->GetRegType() == REGT_INT)
{
if (basex->ValueType != TypeName || Explicit) // names can be converted to int, but only with an explicit type cast.
if (basex->ValueType->isNumeric() || Explicit) // names can be converted to int, but only with an explicit type cast.
{
FxExpression *x = basex;
x->ValueType = ValueType;
@ -822,7 +822,7 @@ FxExpression *FxIntCast::Resolve(FCompileContext &ctx)
}
else
{
// Ugh. This should abort, but too many mods fell into this logic hole somewhere, so this seroious error needs to be reduced to a warning. :(
// Ugh. This should abort, but too many mods fell into this logic hole somewhere, so this serious error needs to be reduced to a warning. :(
// At least in ZScript, MSG_OPTERROR always means to report an error, not a warning so the problem only exists in DECORATE.
if (!basex->isConstant())
ScriptPosition.Message(MSG_OPTERROR, "Numeric type expected, got a name");
@ -919,7 +919,7 @@ FxExpression *FxFloatCast::Resolve(FCompileContext &ctx)
}
else if (basex->ValueType->GetRegType() == REGT_INT)
{
if (basex->ValueType != TypeName)
if (basex->ValueType->isNumeric())
{
if (basex->isConstant())
{
@ -1428,8 +1428,15 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
delete this;
return x;
}
else if (ValueType == TypeState)
else if (ValueType == TypeStateLabel)
{
if (basex->ValueType == TypeNullPtr)
{
auto x = new FxConstant(0, ScriptPosition);
x->ValueType = TypeStateLabel;
delete this;
return x;
}
// Right now this only supports string constants. There should be an option to pass a string variable, too.
if (basex->isConstant() && (basex->ValueType == TypeString || basex->ValueType == TypeName))
{
@ -4022,6 +4029,10 @@ FxExpression *FxConditional::Resolve(FCompileContext& ctx)
ValueType = TypeSInt32;
else if (truex->IsNumeric() && falsex->IsNumeric())
ValueType = TypeFloat64;
else if (truex->IsPointer() && falsex->ValueType == TypeNullPtr)
ValueType = truex->ValueType;
else if (falsex->IsPointer() && truex->ValueType == TypeNullPtr)
ValueType = falsex->ValueType;
else
ValueType = TypeVoid;
//else if (truex->ValueType != falsex->ValueType)
@ -4519,23 +4530,21 @@ static void EmitLoad(VMFunctionBuilder *build, const ExpEmit resultreg, const Ex
ExpEmit FxMinMax::Emit(VMFunctionBuilder *build)
{
unsigned i;
int opcode, opA;
int opcode;
assert(choices.Size() > 0);
assert(OP_LTF_RK == OP_LTF_RR+1);
assert(OP_LT_RK == OP_LT_RR+1);
assert(OP_LEF_RK == OP_LEF_RR+1);
assert(OP_LE_RK == OP_LE_RR+1);
assert(OP_MAXF_RK == OP_MAXF_RR+1);
assert(OP_MAX_RK == OP_MAX_RR+1);
assert(OP_MIN_RK == OP_MIN_RR+1);
assert(OP_MIN_RK == OP_MIN_RR+1);
if (Type == NAME_Min)
{
opcode = ValueType->GetRegType() == REGT_FLOAT ? OP_LEF_RR : OP_LE_RR;
opA = 1;
opcode = ValueType->GetRegType() == REGT_FLOAT ? OP_MINF_RR : OP_MIN_RR;
}
else
{
opcode = ValueType->GetRegType() == REGT_FLOAT ? OP_LTF_RR : OP_LT_RR;
opA = 0;
opcode = ValueType->GetRegType() == REGT_FLOAT ? OP_MAXF_RR : OP_MAX_RR;
}
ExpEmit bestreg;
@ -4556,17 +4565,8 @@ ExpEmit FxMinMax::Emit(VMFunctionBuilder *build)
{
ExpEmit checkreg = choices[i]->Emit(build);
assert(checkreg.RegType == bestreg.RegType);
build->Emit(opcode + checkreg.Konst, opA, bestreg.RegNum, checkreg.RegNum);
build->Emit(OP_JMP, 1);
if (checkreg.Konst)
{
build->Emit(bestreg.RegType == REGT_FLOAT ? OP_LKF : OP_LK, bestreg.RegNum, checkreg.RegNum);
}
else
{
build->Emit(bestreg.RegType == REGT_FLOAT ? OP_MOVEF : OP_MOVE, bestreg.RegNum, checkreg.RegNum, 0);
checkreg.Free(build);
}
build->Emit(opcode + checkreg.Konst, bestreg.RegNum, bestreg.RegNum, checkreg.RegNum);
checkreg.Free(build);
}
return bestreg;
}
@ -6121,7 +6121,6 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
case NAME_Name:
case NAME_Color:
case NAME_Sound:
case NAME_State:
if (CheckArgSize(MethodName, ArgList, 1, 1, ScriptPosition))
{
PType *type =
@ -6131,8 +6130,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx)
MethodName == NAME_Float ? TypeFloat64 :
MethodName == NAME_Double ? TypeFloat64 :
MethodName == NAME_Name ? TypeName :
MethodName == NAME_Color ? TypeColor :
MethodName == NAME_State? TypeState :(PType*)TypeSound;
MethodName == NAME_Color ? TypeColor : (PType*)TypeSound;
func = new FxTypeCast(ArgList[0], type, true, true);
ArgList[0] = nullptr;
@ -6819,7 +6817,6 @@ bool FxVMFunctionCall::CheckEmitCast(VMFunctionBuilder *build, bool returnit, Ex
FName funcname = Function->SymbolName;
if (funcname == NAME___decorate_internal_int__ ||
funcname == NAME___decorate_internal_bool__ ||
funcname == NAME___decorate_internal_state__ ||
funcname == NAME___decorate_internal_float__)
{
FxExpression *arg = ArgList[0];
@ -7926,6 +7923,13 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
}
else
{
// If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.)
if (ctx.ReturnProto != nullptr && ctx.Function->SymbolName != NAME_None)
{
Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[0], false, false);
Value = Value->Resolve(ctx);
ABORT(Value);
}
retproto = Value->ReturnProto();
}
@ -8135,7 +8139,9 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build)
//==========================================================================
//
//
// Symbolic state labels.
// Conversion will not happen inside the compiler anymore because it causes
// just too many problems.
//
//==========================================================================
@ -8155,7 +8161,9 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx)
delete this;
return nullptr;
}
FxExpression *x = new FxConstant(aclass->OwnedStates + index, ScriptPosition);
int symlabel = StateLabels.AddPointer(aclass->OwnedStates + index);
FxExpression *x = new FxConstant(symlabel, ScriptPosition);
x->ValueType = TypeStateLabel;
delete this;
return x;
}
@ -8169,8 +8177,7 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx)
FxRuntimeStateIndex::FxRuntimeStateIndex(FxExpression *index)
: FxExpression(EFX_RuntimeStateIndex, index->ScriptPosition), Index(index)
{
EmitTail = false;
ValueType = TypeState;
ValueType = TypeStateLabel;
}
FxRuntimeStateIndex::~FxRuntimeStateIndex()
@ -8178,12 +8185,6 @@ FxRuntimeStateIndex::~FxRuntimeStateIndex()
SAFE_DELETE(Index);
}
PPrototype *FxRuntimeStateIndex::ReturnProto()
{
EmitTail = true;
return FxExpression::ReturnProto();
}
FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
@ -8206,13 +8207,15 @@ FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx)
}
else if (index == 0)
{
auto x = new FxConstant((FState*)nullptr, ScriptPosition);
int symlabel = StateLabels.AddPointer(nullptr);
auto x = new FxConstant(symlabel, ScriptPosition);
delete this;
return x->Resolve(ctx);
x->ValueType = TypeStateLabel;
return x;
}
else
{
auto x = new FxStateByIndex(index, ScriptPosition);
auto x = new FxStateByIndex(ctx.StateIndex + index, ScriptPosition);
delete this;
return x->Resolve(ctx);
}
@ -8222,84 +8225,21 @@ FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx)
Index = new FxIntCast(Index, ctx.FromDecorate);
SAFE_RESOLVE(Index, ctx);
}
auto aclass = dyn_cast<PClassActor>(ctx.Class);
assert(aclass != nullptr && aclass->NumOwnedStates > 0);
symlabel = StateLabels.AddPointer(aclass->OwnedStates + ctx.StateIndex);
ValueType = TypeStateLabel;
return this;
}
static bool VerifyJumpTarget(AActor *stateowner, FStateParamInfo *stateinfo, int index)
{
PClassActor *cls = stateowner->GetClass();
if (stateinfo->mCallingState != nullptr)
{
while (cls != RUNTIME_CLASS(AActor))
{
// both calling and target state need to belong to the same class.
if (cls->OwnsState(stateinfo->mCallingState))
{
return cls->OwnsState(stateinfo->mCallingState + index);
}
// We can safely assume the ParentClass is of type PClassActor
// since we stop when we see the Actor base class.
cls = static_cast<PClassActor *>(cls->ParentClass);
}
}
return false;
}
static int BuiltinHandleRuntimeState(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
PARAM_PROLOGUE;
PARAM_OBJECT(stateowner, AActor);
PARAM_POINTER(stateinfo, FStateParamInfo);
PARAM_INT(index);
if (index == 0 || !VerifyJumpTarget(stateowner, stateinfo, index))
{
// Null is returned if the location was invalid which means that no jump will be performed
// if used as return value
// 0 always meant the same thing so we handle it here for compatibility
ACTION_RETURN_STATE(nullptr);
}
else
{
ACTION_RETURN_STATE(stateinfo->mCallingState + index);
}
}
ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build)
{
// This can only be called from inline state functions which must be VARF_Action.
assert(build->NumImplicits >= NAP && build->Registers[REGT_POINTER].GetMostUsed() >= build->NumImplicits &&
"FxRuntimeStateIndex is only valid inside action functions");
ExpEmit out(build, REGT_POINTER);
build->Emit(OP_PARAM, 0, REGT_POINTER, 1); // stateowner
build->Emit(OP_PARAM, 0, REGT_POINTER, 2); // stateinfo
ExpEmit id = Index->Emit(build);
build->Emit(OP_PARAM, 0, REGT_INT | (id.Konst ? REGT_KONST : 0), id.RegNum); // index
VMFunction *callfunc;
PSymbol *sym;
sym = FindBuiltinFunction(NAME_BuiltinHandleRuntimeState, BuiltinHandleRuntimeState);
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
if (EmitTail)
{
build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1);
out.Final = true;
}
else
{
build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1);
build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum);
}
ExpEmit out = Index->Emit(build);
// out = (clamp(Index, 0, 32767) << 16) | symlabel | 0x80000000; 0x80000000 is here to make it negative.
build->Emit(OP_MAX_RK, out.RegNum, out.RegNum, build->GetConstantInt(0));
build->Emit(OP_MIN_RK, out.RegNum, out.RegNum, build->GetConstantInt(32767));
build->Emit(OP_SLL_RI, out.RegNum, out.RegNum, 16);
build->Emit(OP_OR_RK, out.RegNum, out.RegNum, build->GetConstantInt(symlabel|0x80000000));
return out;
}
@ -8340,6 +8280,7 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
ABORT(ctx.Class);
int symlabel;
if (names[0] == NAME_None)
{
@ -8379,107 +8320,17 @@ FxExpression *FxMultiNameState::Resolve(FCompileContext &ctx)
return this;
}
}
FxExpression *x = new FxConstant(destination, ScriptPosition);
delete this;
return x;
}
names.Delete(0);
names.ShrinkToFit();
ValueType = TypeState;
return this;
}
//==========================================================================
//
//
//
//==========================================================================
static int DoFindState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, FName *names, int numnames)
{
PARAM_OBJECT_AT(0, self, AActor);
FState *state = self->GetClass()->FindState(numparam - 1, names);
if (state == nullptr)
{
const char *dot = "";
Printf("Jump target '");
for (int i = 0; i < numparam - 1; i++)
{
Printf("%s%s", dot, names[i].GetChars());
dot = ".";
}
Printf("' not found in %s\n", self->GetClass()->TypeName.GetChars());
}
ret->SetPointer(state, ATAG_STATE);
return 1;
}
// Find a state with any number of dots in its name.
int BuiltinFindMultiNameState(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam > 1);
assert(numret == 1);
assert(ret->RegType == REGT_POINTER);
FName *names = (FName *)alloca((numparam - 1) * sizeof(FName));
for (int i = 1; i < numparam; ++i)
{
PARAM_NAME_AT(i, zaname);
names[i - 1] = zaname;
}
return DoFindState(stack, param, numparam, ret, names, numparam - 1);
}
// Find a state without any dots in its name.
int BuiltinFindSingleNameState(VMFrameStack *stack, VMValue *param, TArray<VMValue> &defaultparam, int numparam, VMReturn *ret, int numret)
{
assert(numparam == 2);
assert(numret == 1);
assert(ret->RegType == REGT_POINTER);
PARAM_NAME_AT(1, zaname);
return DoFindState(stack, param, numparam, ret, &zaname, 1);
}
ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build)
{
ExpEmit dest(build, REGT_POINTER);
if (build->NumImplicits == NAP)
{
build->Emit(OP_PARAM, 0, REGT_POINTER, 1); // pass stateowner
symlabel = StateLabels.AddPointer(destination);
}
else
{
build->Emit(OP_PARAM, 0, REGT_POINTER, 0); // pass self
names.Delete(0);
symlabel = StateLabels.AddNames(names);
}
for (unsigned i = 0; i < names.Size(); ++i)
{
build->EmitParamInt(names[i]);
}
// For one name, use the BuiltinFindSingleNameState function. For more than
// one name, use the BuiltinFindMultiNameState function.
VMFunction *callfunc;
PSymbol *sym;
if (names.Size() == 1)
{
sym = FindBuiltinFunction(NAME_BuiltinFindSingleNameState, BuiltinFindSingleNameState);
}
else
{
sym = FindBuiltinFunction(NAME_BuiltinFindMultiNameState, BuiltinFindMultiNameState);
}
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function;
build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), names.Size() + 1, 1);
build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum);
names.Clear();
names.ShrinkToFit();
return dest;
FxExpression *x = new FxConstant(symlabel, ScriptPosition);
x->ValueType = TypeStateLabel;
delete this;
return x;
}
//==========================================================================

View file

@ -310,9 +310,9 @@ public:
virtual PPrototype *ReturnProto();
virtual VMFunction *GetDirectFunction();
virtual bool CheckReturn() { return false; }
bool IsNumeric() const { return ValueType != TypeName && ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT); }
bool IsNumeric() const { return ValueType->isNumeric(); }
bool IsFloat() const { return ValueType->GetRegType() == REGT_FLOAT && ValueType->GetRegCount() == 1; }
bool IsInteger() const { return ValueType != TypeName && (ValueType->GetRegType() == REGT_INT); }
bool IsInteger() const { return ValueType->isNumeric() && (ValueType->GetRegType() == REGT_INT); }
bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; }
bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3; };
bool IsBoolCompat() const { return ValueType->GetRegCount() == 1 && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT || ValueType->GetRegType() == REGT_POINTER); }
@ -1684,14 +1684,13 @@ public:
class FxRuntimeStateIndex : public FxExpression
{
bool EmitTail;
FxExpression *Index;
int symlabel;
public:
FxRuntimeStateIndex(FxExpression *index);
~FxRuntimeStateIndex();
FxExpression *Resolve(FCompileContext&);
PPrototype *ReturnProto();
ExpEmit Emit(VMFunctionBuilder *build);
};
@ -1709,7 +1708,6 @@ public:
FxMultiNameState(const char *statestring, const FScriptPosition &pos);
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
};
//==========================================================================

View file

@ -174,14 +174,15 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c
val.Int = v;
x = new FxConstant(val, sc);
}
else if (type == TypeState)
else if (type == TypeStateLabel)
{
// This forces quotation marks around the state name.
if (sc.CheckToken(TK_StringConst))
{
if (sc.String[0] == 0 || sc.Compare("None"))
{
x = new FxConstant((FState*)nullptr, sc);
x = new FxConstant(0, sc);
x->ValueType = TypeStateLabel;
}
else if (sc.Compare("*"))
{

View file

@ -626,7 +626,7 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
while (numparams > 0)
{
FxExpression *x;
if (statedef != NULL && params[pnum] == TypeState && sc.CheckNumber())
if (statedef != NULL && params[pnum] == TypeStateLabel && sc.CheckNumber())
{
// Special case: State label as an offset
if (sc.Number > 0 && statestring.Len() > 1)
@ -646,7 +646,8 @@ void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression
}
else
{
x = new FxConstant((FState*)NULL, sc);
x = new FxConstant(0, sc);
x->ValueType = TypeStateLabel;
}
}
else
@ -703,7 +704,7 @@ FName CheckCastKludges(FName in)
case NAME_Bool:
return NAME___decorate_internal_bool__;
case NAME_State:
return NAME___decorate_internal_state__;
return NAME_ResolveState;
case NAME_Float:
return NAME___decorate_internal_float__;
default:

View file

@ -940,15 +940,12 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_FLOAT_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_FLOAT); double x = param[p].f;
#define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_FLOAT); DAngle x = param[p].f;
#define PARAM_STRING_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_STRING); FString x = param[p].s();
#define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATE || param[p].atag == ATAG_GENERIC || param[p].a == NULL)); FState *x = (FState *)param[p].a;
#define PARAM_STATE_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, self->GetClass());
#define PARAM_STATE_ACTION_AT(p,x) assert((p) < numparam); assert(param[p].Type == REGT_INT); FState *x = (FState *)StateLabels.GetState(param[p].i, stateowner->GetClass());
#define PARAM_POINTER_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER); type *x = (type *)param[p].a;
#define PARAM_OBJECT_AT(p,x,type) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); type *x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type)));
#define PARAM_CLASS_AT(p,x,base) assert((p) < numparam); assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); base::MetaClass *x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base)));
// For optional paramaters. These have dangling elses for you to fill in the default assignment. e.g.:
// PARAM_INT_OPT(0,myint) { myint = 55; }
// Just make sure to fill it in when using these macros, because the compiler isn't likely
// to give useful error messages if you don't.
#define PARAM_EXISTS(p) ((p) < numparam && param[p].Type != REGT_NIL)
#define ASSERTINT(p) assert((p).Type == REGT_INT)
#define ASSERTFLOAT(p) assert((p).Type == REGT_FLOAT)
@ -965,7 +962,8 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_FLOAT_DEF_AT(p,x) double x; if (PARAM_EXISTS(p)) { ASSERTFLOAT(param[p]); x = param[p].f; } else { ASSERTFLOAT(defaultparam[p]); x = defaultparam[p].f; }
#define PARAM_ANGLE_DEF_AT(p,x) DAngle x; if (PARAM_EXISTS(p)) { ASSERTFLOAT(param[p]); x = param[p].f; } else { ASSERTFLOAT(defaultparam[p]); x = defaultparam[p].f; }
#define PARAM_STRING_DEF_AT(p,x) FString x; if (PARAM_EXISTS(p)) { ASSERTSTRING(param[p]); x = param[p].s; } else { ASSERTSTRING(defaultparam[p]); x = defaultparam[p].s; }
#define PARAM_STATE_DEF_AT(p,x) FState *x; if (PARAM_EXISTS(p)) { ASSERTSTATE(param[p]); x = (FState*)param[p].a; } else { ASSERTSTATE(defaultparam[p]); x = (FState*)defaultparam[p].a; }
#define PARAM_STATE_DEF_AT(p,x) FState *x; if (PARAM_EXISTS(p)) { ASSERTINT(param[p]); x = (FState*)StateLabels.GetState(param[p].i, self->GetClass()); } else { ASSERTINT(defaultparam[p]); x = (FState*)StateLabels.GetState(defaultparam[p].i, self->GetClass()); }
#define PARAM_STATE_ACTION_DEF_AT(p,x) FState *x; if (PARAM_EXISTS(p)) { ASSERTINT(param[p]); x = (FState*)StateLabels.GetState(param[p].i, stateowner->GetClass()); } else { ASSERTINT(defaultparam[p]); x = (FState*)StateLabels.GetState(defaultparam[p].i, stateowner->GetClass()); }
#define PARAM_POINTER_DEF_AT(p,x,t) t *x; if (PARAM_EXISTS(p)) { ASSERTPOINTER(param[p]); x = (t*)param[p].a; } else { ASSERTPOINTER(defaultparam[p]); x = (t*)defaultparam[p].a; }
#define PARAM_OBJECT_DEF_AT(p,x,t) t *x; if (PARAM_EXISTS(p)) { ASSERTOBJECT(param[p]); x = (t*)param[p].a; } else { ASSERTOBJECT(defaultparam[p]); x = (t*)defaultparam[p].a; }
#define PARAM_CLASS_DEF_AT(p,x,t) t::MetaClass *x; if (PARAM_EXISTS(p)) { ASSERTOBJECT(param[p]); x = (t::MetaClass*)param[p].a; } else { ASSERTOBJECT(defaultparam[p]); x = (t::MetaClass*)defaultparam[p].a; }
@ -982,6 +980,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_ANGLE(x) ++paramnum; PARAM_ANGLE_AT(paramnum,x)
#define PARAM_STRING(x) ++paramnum; PARAM_STRING_AT(paramnum,x)
#define PARAM_STATE(x) ++paramnum; PARAM_STATE_AT(paramnum,x)
#define PARAM_STATE_ACTION(x) ++paramnum; PARAM_STATE_ACTION_AT(paramnum,x)
#define PARAM_POINTER(x,type) ++paramnum; PARAM_POINTER_AT(paramnum,x,type)
#define PARAM_OBJECT(x,type) ++paramnum; PARAM_OBJECT_AT(paramnum,x,type)
#define PARAM_CLASS(x,base) ++paramnum; PARAM_CLASS_AT(paramnum,x,base)
@ -995,6 +994,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_ANGLE_DEF(x) ++paramnum; PARAM_ANGLE_DEF_AT(paramnum,x)
#define PARAM_STRING_DEF(x) ++paramnum; PARAM_STRING_DEF_AT(paramnum,x)
#define PARAM_STATE_DEF(x) ++paramnum; PARAM_STATE_DEF_AT(paramnum,x)
#define PARAM_STATE_ACTION_DEF(x) ++paramnum; PARAM_STATE_ACTION_DEF_AT(paramnum,x)
#define PARAM_POINTER_DEF(x,type) ++paramnum; PARAM_POINTER_DEF_AT(paramnum,x,type)
#define PARAM_OBJECT_DEF(x,type) ++paramnum; PARAM_OBJECT_DEF_AT(paramnum,x,type)
#define PARAM_CLASS_DEF(x,base) ++paramnum; PARAM_CLASS_DEF_AT(paramnum,x,base)

View file

@ -1429,7 +1429,15 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
break;
case ZCC_UserType:
retval = ResolveUserType(btype, &outertype->Symbols);
// statelabel is not a token - there really is no need to, it works just as well as an identifier. Maybe the same should be done for some other types, too?
if (btype->UserType->Id == NAME_StateLabel)
{
retval = TypeStateLabel;
}
else
{
retval = ResolveUserType(btype, &outertype->Symbols);
}
break;
}
break;

View file

@ -1,6 +1,7 @@
zscript/base.txt
zscript/constants.txt
zscript/actor.txt
zscript/actor_checks.txt
zscript/shared/inventory.txt
zscript/shared/player.txt

View file

@ -71,6 +71,7 @@ class Actor : Thinker native
native void SetXYZ(vector3 newpos);
native Actor GetPointer(int aaptr);
native void FaceMovementDirection();
native Actor AimTarget();
native static Actor Spawn(class<Actor> type, vector3 pos = (0,0,0), int replace = NO_REPLACE);
native Actor SpawnMissile(Actor dest, class<Actor> type, Actor owner = null);
native Actor SpawnMissileZ (double z, Actor dest, class<Actor> type);
@ -90,6 +91,9 @@ class Actor : Thinker native
native void NewChaseDir();
native bool CheckMissileRange();
native bool SetState(state st, bool nofunction = false);
native state FindState(statelabel st); // do we need exact later?
bool SetStateLabel(statelabel st, bool nofunction = false) { return SetState(FindState(st), nofunction); }
native action state ResolveState(statelabel st); // this one, unlike FindState, is context aware.
native void LinkToWorld();
native void UnlinkFromWorld();
native bool CanSeek(Actor target);
@ -282,7 +286,7 @@ class Actor : Thinker native
void A_Fall() { A_NoBlocking(); }
native void A_XScream();
native void A_Look();
native void A_Chase(state melee = null, state missile = null, int flags = 0);
native void A_Chase(statelabel melee = null, statelabel missile = null, int flags = 0);
native void A_Scream();
native void A_VileChase();
native void A_BossDeath();
@ -332,18 +336,10 @@ class Actor : Thinker native
deprecated native void A_PlaySoundEx(sound whattoplay, name slot, bool looping = false, int attenuation = 0);
deprecated native void A_StopSoundEx(name slot);
native void A_SeekerMissile(int threshold, int turnmax, int flags = 0, int chance = 50, int distance = 10);
native state A_Jump(int chance, state label, ...);
native action state A_Jump(int chance, statelabel label, ...);
native void A_CustomMissile(class<Actor> missiletype, float spawnheight = 32, float spawnofs_xy = 0, float angle = 0, int flags = 0, float pitch = 0, int ptr = AAPTR_TARGET);
native void A_CustomBulletAttack(float spread_xy, float spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET, class<Actor> missile = null, float Spawnheight = 32, float Spawnofs_xy = 0);
native void A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = 0, color color2 = 0, int flags = 0, int aim = 0, float maxdiff = 0, class<Actor> pufftype = "BulletPuff", float spread_xy = 0, float spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class<Actor> spawnclass = null, float spawnofs_z = 0, int spiraloffset = 270, int limit = 0);
native state A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT);
native state A_JumpIfCloser(float distance, state label, bool noz = false);
native state A_JumpIfTracerCloser(float distance, state label, bool noz = false);
native state A_JumpIfMasterCloser(float distance, state label, bool noz = false);
native state A_JumpIfTargetOutsideMeleeRange(state label);
native state A_JumpIfTargetInsideMeleeRange(state label);
native state A_JumpIfInventory(class<Inventory> itemtype, int itemamount, state label, int owner = AAPTR_DEFAULT);
native state A_JumpIfArmorType(name Type, state label, int amount = 1);
native bool A_SetInventory(class<Inventory> itemtype, int amount, int ptr = AAPTR_DEFAULT, bool beyondMax = false);
native bool A_GiveInventory(class<Inventory> itemtype, int amount = 0, int giveto = AAPTR_DEFAULT);
native bool A_TakeInventory(class<Inventory> itemtype, int amount = 0, int flags = 0, int giveto = AAPTR_DEFAULT);
@ -361,33 +357,29 @@ class Actor : Thinker native
native void A_FadeTo(float target, float amount = 0.1, int flags = 0);
native void A_SpawnDebris(class<Actor> spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1);
native void A_SpawnParticle(color color1, int flags = 0, int lifetime = 35, float 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, float sizestep = 0);
native state A_CheckSight(state label);
native void A_ExtChase(bool usemelee, bool usemissile, bool playactive = true, bool nightmarefast = false);
native void A_DropInventory(class<Inventory> itemtype);
native void A_SetBlend(color color1, float alpha, int tics, color color2 = 0);
deprecated native void A_ChangeFlag(string flagname, bool value);
deprecated native state A_CheckFlag(string flagname, state label, int check_pointer = AAPTR_DEFAULT);
native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE);
native state A_JumpIf(bool expression, state label);
native void A_RaiseMaster(bool copy = 0);
native void A_RaiseChildren(bool copy = 0);
native void A_RaiseSiblings(bool copy = 0);
native state A_CheckFloor(state label);
native state A_CheckCeiling(state label);
native state A_PlayerSkinCheck(state label);
deprecated native void A_BasicAttack(int meleedamage, sound meleesound, class<actor> missiletype, float missileheight);
native state, bool A_Teleport(state teleportstate = null, class<SpecialSpot> targettype = "BossSpot", class<Actor> fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 128, int ptr = AAPTR_DEFAULT);
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 = null, 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);
native void A_Weave(int xspeed, int yspeed, float xdist, float ydist);
native state, bool A_Teleport(statelabel teleportstate = null, class<SpecialSpot> targettype = "BossSpot", class<Actor> fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 128, int ptr = AAPTR_DEFAULT);
native state, bool A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, statelabel success_state = null, float heightoffset = 0, float radiusoffset = 0, float pitch = 0);
native void A_CountdownArg(int argnum, statelabel targstate = null);
native state A_MonsterRefire(int chance, statelabel label);
native void A_LookEx(int flags = 0, float minseedist = 0, float maxseedist = 0, float maxheardist = 0, float fov = 0, statelabel label = null);
native void A_Recoil(float xyvel);
native state A_JumpIfInTargetInventory(class<Inventory> itemtype, int amount, state label, int forward_ptr = AAPTR_DEFAULT);
native bool A_GiveToTarget(class<Inventory> itemtype, int amount = 0, int forward_ptr = AAPTR_DEFAULT);
native bool A_TakeFromTarget(class<Inventory> itemtype, int amount = 0, int flags = 0, int forward_ptr = AAPTR_DEFAULT);
native int A_RadiusGive(class<Inventory> itemtype, float distance, int flags, int amount = 0, class<Actor> filter = null, name species = "None", float mindist = 0, int limit = 0);
native state A_CheckSpecies(state jump, name species = 'none', int ptr = AAPTR_DEFAULT);
native void A_CountdownArg(int argnum, state targstate = null);
native void A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true);
native void A_CustomComboAttack(class<Actor> missiletype, float spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true);
native void A_Burst(class<Actor> chunktype);
@ -399,11 +391,7 @@ class Actor : Thinker native
native void A_Respawn(int flags = 1);
native void A_QueueCorpse();
native void A_DeQueueCorpse();
native void A_LookEx(int flags = 0, float minseedist = 0, float maxseedist = 0, float maxheardist = 0, float fov = 0, state label = null);
native void A_ClearLastHeard();
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, float offsetforward = 0);
native state A_JumpIfTargetInLOS (state label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
native state A_JumpIfInTargetLOS (state label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
native bool A_SelectWeapon(class<Weapon> whichweapon, int flags = 0);
action native void A_Punch();
native void A_Feathers();
@ -416,7 +404,6 @@ class Actor : Thinker native
native void A_RemoveForcefield();
native void A_DropWeaponPieces(class<Actor> p1, class<Actor> p2, class<Actor> p3);
native void A_PigPain ();
native state A_MonsterRefire(int chance, state label);
native void A_SetAngle(float angle = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
native void A_SetPitch(float pitch, int flags = 0, int ptr = AAPTR_DEFAULT);
native void A_SetRoll(float roll, int flags = 0, int ptr = AAPTR_DEFAULT);
@ -455,13 +442,8 @@ class Actor : Thinker native
native void A_SwapTeleFog();
native void A_SetHealth(int health, int ptr = AAPTR_DEFAULT);
native void A_ResetHealth(int ptr = AAPTR_DEFAULT);
native state A_JumpIfHigherOrLower(state high, state low, float offsethigh = 0, float offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET);
native void A_SetSpecies(name species, int ptr = AAPTR_DEFAULT);
native void A_SetChaseThreshold(int threshold, bool def = false, int ptr = AAPTR_DEFAULT);
native state A_CheckProximity(state jump, class<Actor> classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT);
native state A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0);
native state A_CheckSightOrRange(float distance, state label, bool two_dimension = false);
native state A_CheckRange(float distance, state label, bool two_dimension = false);
native bool A_FaceMovementDirection(float offset = 0, float anglelimit = 0, float pitchlimit = 0, int flags = 0, int ptr = AAPTR_DEFAULT);
native int A_ClearOverlays(int sstart = 0, int sstop = 0, bool safety = true);
native bool A_CopySpriteFrame(int from, int to, int flags = 0);
@ -472,7 +454,7 @@ class Actor : Thinker native
native void A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0);
native void A_CopyFriendliness(int ptr_source = AAPTR_MASTER);
action native bool A_Overlay(int layer, state start = null, bool nooverride = false);
action native bool A_Overlay(int layer, statelabel start = null, bool nooverride = false);
native void A_WeaponOffset(float wx = 0, float wy = 32, int flags = 0);
action native void A_OverlayOffset(int layer = PSP_WEAPON, float wx = 0, float wy = 32, int flags = 0);
action native void A_OverlayFlags(int layer, int flags, bool set);
@ -525,7 +507,6 @@ class Actor : Thinker native
}
// Internal functions
deprecated private native state __decorate_internal_state__(state s);
deprecated private native int __decorate_internal_int__(int i);
deprecated private native bool __decorate_internal_bool__(bool b);
deprecated private native float __decorate_internal_float__(float f);

View file

@ -0,0 +1,274 @@
extend class Actor
{
//==========================================================================
//
// This file contains all the A_Jump* checker functions, all split up
// into an actual checker and a simple wrapper around ResolveState.
//
//==========================================================================
action state A_JumpIf(bool expression, statelabel label)
{
return expression? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
action state A_JumpIfHealthLower(int health, statelabel label, int ptr_selector = AAPTR_DEFAULT)
{
Actor aptr = GetPointer(ptr_selector);
return aptr && aptr.health < health? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
bool CheckIfCloser(Actor targ, float dist, bool noz = false)
{
if (!targ) return false;
return
(Distance2D(target) < dist && (noz ||
((pos.z > targ.pos.z && pos.z - targ.pos.z - targ.height < dist) ||
(pos.z <= targ.pos.z && targ.pos.z - pos.z - height < dist)
)
));
}
action state A_JumpIfCloser(float distance, statelabel label, bool noz = false)
{
Actor targ;
if (player == NULL)
{
targ = target;
}
else
{
// Does the player aim at something that can be shot?
targ = AimTarget();
}
return CheckIfCloser(targ, distance, noz)? ResolveState(label) : null;
}
action state A_JumpIfTracerCloser(float distance, statelabel label, bool noz = false)
{
return CheckIfCloser(tracer, distance, noz)? ResolveState(label) : null;
}
action state A_JumpIfMasterCloser(float distance, statelabel label, bool noz = false)
{
return CheckIfCloser(master, distance, noz)? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
action state A_JumpIfTargetOutsideMeleeRange(statelabel label)
{
return CheckMeleeRange()? null : ResolveState(label);
}
action state A_JumpIfTargetInsideMeleeRange(statelabel label)
{
return CheckMeleeRange()? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
native bool CheckInventory(class<Inventory> itemtype, int itemamount, int owner = AAPTR_DEFAULT);
action state A_JumpIfInventory(class<Inventory> itemtype, int itemamount, statelabel label, int owner = AAPTR_DEFAULT)
{
return CheckInventory(itemtype, itemamount, owner)? ResolveState(label) : null;
}
action state A_JumpIfInTargetInventory(class<Inventory> itemtype, int amount, statelabel label, int forward_ptr = AAPTR_DEFAULT)
{
if (target == null) return null;
return target.CheckInventory(itemtype, amount, forward_ptr)? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
native bool CheckArmorType(name Type, int amount = 1);
action state A_JumpIfArmorType(name Type, statelabel label, int amount = 1)
{
return CheckArmorType(Type, amount)? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
native bool CheckIfSeen();
native bool CheckSightOrRange(float distance, bool two_dimension = false);
native bool CheckRange(float distance, bool two_dimension = false);
action state A_CheckSight(statelabel label)
{
return CheckIfSeen()? ResolveState(label) : null;
}
action state A_CheckSightOrRange(float distance, statelabel label, bool two_dimension = false)
{
return CheckSightOrRange(distance, two_dimension)? ResolveState(label) : null;
}
action state A_CheckRange(float distance, statelabel label, bool two_dimension = false)
{
return CheckRange(distance, two_dimension)? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
action state A_CheckFloor(statelabel label)
{
return pos.z <= floorz? ResolveState(label) : null;
}
action state A_CheckCeiling(statelabel label)
{
return pos.z + height >= ceilingz? ResolveState(label) : null;
}
//==========================================================================
//
// since this is deprecated the checker is private.
//
//==========================================================================
private native bool CheckFlag(string flagname, int check_pointer = AAPTR_DEFAULT);
deprecated action state A_CheckFlag(string flagname, statelabel label, int check_pointer = AAPTR_DEFAULT)
{
return CheckFlag(flagname, check_pointer)? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
native bool PlayerSkinCheck();
action state A_PlayerSkinCheck(statelabel label)
{
return PlayerSkinCheck()? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
action state A_CheckSpecies(statelabel label, name species = 'none', int ptr = AAPTR_DEFAULT)
{
Actor aptr = GetPointer(ptr);
return aptr && aptr.GetSpecies() == species? ResolveState(label) : null;
}
//==========================================================================
//
//
//
//==========================================================================
native bool CheckLOF(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, float offsetforward = 0);
native bool CheckIfTargetInLOS (float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
native bool CheckIfInTargetLOS (float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0);
native bool CheckProximity(class<Actor> classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT);
native bool CheckBlock(int flags = 0, int ptr = AAPTR_DEFAULT, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0);
action state A_CheckLOF(statelabel label, 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, float offsetforward = 0)
{
return CheckLOF(flags, range, minrange, angle, pitch, offsetheight, offsetwidth, ptr_target, offsetforward)? ResolveState(label) : null;
}
action state A_JumpIfTargetInLOS (statelabel label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0)
{
return CheckIfTargetInLOS(fov, flags, dist_max, dist_close)? ResolveState(label) : null;
}
action state A_JumpIfInTargetLOS (statelabel label, float fov = 0, int flags = 0, float dist_max = 0, float dist_close = 0)
{
return CheckIfInTargetLOS(fov, flags, dist_max, dist_close)? ResolveState(label) : null;
}
action state A_CheckProximity(statelabel label, class<Actor> classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT)
{
// This one was doing some weird stuff that needs to be preserved.
state jumpto = ResolveState(label);
if (!jumpto)
{
if (!(flags & (CPXF_SETTARGET | CPXF_SETMASTER | CPXF_SETTRACER)))
{
return null;
}
}
return CheckProximity(classname, distance, count, flags, ptr)? jumpto : null;
}
action state A_CheckBlock(statelabel label, int flags = 0, int ptr = AAPTR_DEFAULT, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0)
{
return CheckBlock(flags, ptr, xofs, yofs, zofs, angle)? ResolveState(label) : null;
}
//===========================================================================
// A_JumpIfHigherOrLower
//
// Jumps if a target, master, or tracer is higher or lower than the calling
// actor. Can also specify how much higher/lower the actor needs to be than
// itself. Can also take into account the height of the actor in question,
// depending on which it's checking. This means adding height of the
// calling actor's self if the pointer is higher, or height of the pointer
// if its lower.
//===========================================================================
action state A_JumpIfHigherOrLower(statelabel high, statelabel low, float offsethigh = 0, float offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET)
{
Actor mobj = GetPointer(ptr);
if (mobj != null && mobj != self) //AAPTR_DEFAULT is completely useless in this regard.
{
if ((high) && (mobj.pos.z > ((includeHeight ? Height : 0) + pos.z + offsethigh)))
{
return ResolveState(high);
}
else if ((low) && (mobj.pos.z + (includeHeight ? mobj.Height : 0)) < (pos.z + offsetlow))
{
return ResolveState(low);
}
}
return null;
}
}

View file

@ -162,7 +162,7 @@ extend class Actor
boom.DeathSound = "misc/brainexplode";
boom.Vel.z = random[BrainScream](0, 255)/128.;
boom.SetState ("BossBrain::Brainexplode");
boom.SetStateLabel ("BossBrain::Brainexplode");
boom.bRocketTrail = false;
boom.SetDamage(0); // disables collision detection which is not wanted here
boom.tics -= random[BrainScream](0, 7);

View file

@ -113,7 +113,7 @@ class HereticImp : Actor
if (extremecrash)
{
SetState ("XCrash");
SetStateLabel ("XCrash");
}
}

View file

@ -92,7 +92,7 @@ class Bat : Actor
{
if (special2 < 0)
{
SetState ("Death");
SetStateLabel ("Death");
}
special2 -= 2; // Called every 2 tics

View file

@ -131,7 +131,7 @@ class Bishop : Actor
{
if (random[BishopDecide]() >= 220)
{
SetState ("Blur");
SetStateLabel ("Blur");
}
}
@ -210,7 +210,7 @@ class Bishop : Actor
{
if (random[BishopPainBlur]() < 64)
{
SetState ("Blur");
SetStateLabel ("Blur");
return;
}
double xo = random2[BishopPainBlur]() / 16.;

View file

@ -41,7 +41,7 @@ class Inventory : Actor native
class StateProvider : Inventory native
{
action native state A_JumpIfNoAmmo(state label);
action native state A_JumpIfNoAmmo(statelabel label);
action native void 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 = 0, sound MissSound = "");
action native void A_FireBullets(float spread_xy, float spread_z, int numbullets, int damageperbullet, class<Actor> pufftype = "BulletPuff", int flags = 1, float range = 0, class<Actor> missile = null, float Spawnheight = 32, float Spawnofs_xy = 0);
action native void A_FireCustomMissile(class<Actor> missiletype, float angle = 0, bool useammo = true, float spawnofs_xy = 0, float spawnheight = 0, int flags = 0, float pitch = 0);
@ -73,13 +73,13 @@ class StateProvider : Inventory native
action void A_BFGsound() { A_PlaySound("weapons/bfgf", CHAN_WEAPON); }
action native void A_FireBFG();
action native void A_FireOldBFG();
action native void A_ReFire(state flash = null);
action native void A_ReFire(statelabel flash = null);
action native void A_ClearReFire();
action native void A_CheckReload();
action native void A_GunFlash(state flash = null, int flags = 0);
action native void A_GunFlash(statelabel flash = null, int flags = 0);
action native void A_FireAssaultGun();
action native void A_Saw(sound fullsound = "weapons/sawfull", sound hitsound = "weapons/sawhit", int damage = 2, class<Actor> pufftype = "BulletPuff", int flags = 0, float range = 0, float spread_xy = 2.8125, float spread_z = 0, float lifesteal = 0, int lifestealmax = 0, class<BasicArmorBonus> armorbonustype = "ArmorBonus");
action native state A_CheckForReload(int counter, state label, bool dontincrement = false);
action native state A_CheckForReload(int counter, statelabel label, bool dontincrement = false);
action native void A_ResetReloadCounter();
}

View file

@ -816,7 +816,7 @@ class StrifeGrenadeLauncher : StrifeWeapon
Inventory.PickupMessage "$TXT_GLAUNCHER";
}
action native void A_FireGrenade (class<Actor> grenadetype, float angleofs, state flash);
action native void A_FireGrenade (class<Actor> grenadetype, float angleofs, statelabel flash);
States
{