- scriptified the functions in a_possessed.cpp and added the needed exports and constants.

- fixed: Script functions did not receive the function name when being created.
- relaxed the asserts for PARAM_STATE, because the VM knows nothing about ATAG_STATE. Any state variable's content (e.g. Actor.SeeState) will receive ATAG_GENERIC, rather than ATAG_STATE.
- added a 'NeedResult' flag so that certain operations can create shorter code if the result of the expression is not needed. So far only used for postdecrement/increment statements on local variables (which is the most frequent case where this matters.)
- fixed postincrement and decrement for local variables. Due to the result preservation semantics it created faulty code.
This commit is contained in:
Christoph Oelckers 2016-10-26 11:30:30 +02:00
parent 33d00070b5
commit 823c52aeb2
16 changed files with 246 additions and 170 deletions

View file

@ -842,7 +842,6 @@ set( NOT_COMPILED_SOURCE_FILES
g_doom/a_keen.cpp
g_doom/a_lostsoul.cpp
g_doom/a_painelemental.cpp
g_doom/a_possessed.cpp
g_doom/a_revenant.cpp
g_doom/a_scriptedmarine.cpp
g_doom/a_spidermaster.cpp

View file

@ -29,7 +29,6 @@
#include "a_keen.cpp"
#include "a_lostsoul.cpp"
#include "a_painelemental.cpp"
#include "a_possessed.cpp"
#include "a_revenant.cpp"
#include "a_spidermaster.cpp"
#include "a_scriptedmarine.cpp"

View file

@ -1,134 +0,0 @@
/*
#include "actor.h"
#include "info.h"
#include "m_random.h"
#include "s_sound.h"
#include "p_local.h"
#include "p_enemy.h"
#include "gstrings.h"
#include "a_action.h"
#include "vm.h"
*/
static FRandom pr_posattack ("PosAttack");
static FRandom pr_sposattack ("SPosAttack");
static FRandom pr_cposattack ("CPosAttack");
static FRandom pr_cposrefire ("CPosRefire");
//
// A_PosAttack
//
DEFINE_ACTION_FUNCTION(AActor, A_PosAttack)
{
PARAM_SELF_PROLOGUE(AActor);
int damage;
DAngle angle;
DAngle slope;
if (!self->target)
return 0;
A_FaceTarget (self);
angle = self->Angles.Yaw;
slope = P_AimLineAttack (self, angle, MISSILERANGE);
S_Sound (self, CHAN_WEAPON, "grunt/attack", 1, ATTN_NORM);
angle += pr_posattack.Random2() * (22.5 / 256);
damage = ((pr_posattack()%5)+1)*3;
P_LineAttack (self, angle, MISSILERANGE, slope, damage, NAME_Hitscan, NAME_BulletPuff);
return 0;
}
static void A_SPosAttack2 (AActor *self)
{
int i;
DAngle bangle;
DAngle slope;
A_FaceTarget (self);
bangle = self->Angles.Yaw;
slope = P_AimLineAttack (self, bangle, MISSILERANGE);
for (i=0 ; i<3 ; i++)
{
DAngle angle = bangle + pr_sposattack.Random2() * (22.5 / 256);
int damage = ((pr_sposattack()%5)+1)*3;
P_LineAttack(self, angle, MISSILERANGE, slope, damage, NAME_Hitscan, NAME_BulletPuff);
}
}
DEFINE_ACTION_FUNCTION(AActor, A_SPosAttackUseAtkSound)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
return 0;
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
A_SPosAttack2 (self);
return 0;
}
// This version of the function, which uses a hard-coded sound, is
// meant for Dehacked only.
DEFINE_ACTION_FUNCTION(AActor, A_SPosAttack)
{
PARAM_SELF_PROLOGUE(AActor);
if (!self->target)
return 0;
S_Sound (self, CHAN_WEAPON, "shotguy/attack", 1, ATTN_NORM);
A_SPosAttack2 (self);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_CPosAttack)
{
PARAM_SELF_PROLOGUE(AActor);
DAngle angle;
DAngle bangle;
int damage;
DAngle slope;
if (!self->target)
return 0;
// [RH] Andy Baker's stealth monsters
if (self->flags & MF_STEALTH)
{
self->visdir = 1;
}
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
A_FaceTarget (self);
bangle = self->Angles.Yaw;
slope = P_AimLineAttack (self, bangle, MISSILERANGE);
angle = bangle + pr_cposattack.Random2() * (22.5 / 256);
damage = ((pr_cposattack()%5)+1)*3;
P_LineAttack (self, angle, MISSILERANGE, slope, damage, NAME_Hitscan, NAME_BulletPuff);
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_CPosRefire)
{
PARAM_SELF_PROLOGUE(AActor);
// keep firing unless target got out of sight
A_FaceTarget (self);
if (pr_cposrefire() < 40)
return 0;
if (!self->target
|| P_HitFriend (self)
|| self->target->health <= 0
|| !P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES))
{
self->SetState (self->SeeState);
}
return 0;
}

View file

@ -77,7 +77,7 @@ bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info,
{
ActionCycles.Clock();
static VMFrameStack stack;
VMFrameStack stack;
VMValue params[3] = { self, stateowner, VMValue(info, ATAG_STATEINFO) };
// If the function returns a state, store it at *stateret.
// If it doesn't return a state but stateret is non-NULL, we need

View file

@ -461,6 +461,12 @@ bool P_HitFriend(AActor * self)
return false;
}
DEFINE_ACTION_FUNCTION(AActor, HitFriend)
{
PARAM_SELF_PROLOGUE(AActor);
ACTION_RETURN_BOOL(P_HitFriend(self));
}
//
// P_Move
// Move in the current direction,

View file

@ -4043,6 +4043,18 @@ DAngle P_AimLineAttack(AActor *t1, DAngle angle, double distance, FTranslatedLin
return result->linetarget ? result->pitch : t1->Angles.Pitch;
}
DEFINE_ACTION_FUNCTION(AActor, AimLineAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_ANGLE(angle);
PARAM_FLOAT(distance);
PARAM_POINTER_OPT(pLineTarget, FTranslatedLineTarget) { pLineTarget = nullptr; }
PARAM_ANGLE_OPT(vrange) { vrange = 0.; }
PARAM_INT_OPT(flags) { flags = 0; }
PARAM_OBJECT_OPT(target, AActor) { target = nullptr; }
PARAM_OBJECT_OPT(friender, AActor) { friender = nullptr; }
ACTION_RETURN_FLOAT(P_AimLineAttack(self, angle, distance, pLineTarget, vrange, flags, target, friender).Degrees);
}
//==========================================================================
//
@ -4410,6 +4422,25 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance,
}
}
DEFINE_ACTION_FUNCTION(AActor, LineAttack)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_ANGLE(angle);
PARAM_FLOAT(distance);
PARAM_ANGLE(pitch);
PARAM_INT(damage);
PARAM_NAME(damageType);
PARAM_CLASS(puffType, AActor);
PARAM_INT_OPT(flags) { flags = 0; }
PARAM_POINTER_OPT(victim, FTranslatedLineTarget) { victim = nullptr; }
int acdmg;
auto puff = P_LineAttack(self, angle, distance, pitch, damage, damageType, puffType, flags, victim, &acdmg);
if (numret > 0) ret[0].SetPointer(puff, ATAG_OBJECT);
if (numret > 1) ret[1].SetInt(acdmg), numret = 2;
return numret;
}
//==========================================================================
//
// P_LinePickActor

View file

@ -510,6 +510,14 @@ bool AActor::SetState (FState *newstate, bool nofunction)
return true;
}
DEFINE_ACTION_FUNCTION(AActor, SetState)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_STATE(state);
PARAM_BOOL_OPT(nofunction) { nofunction = false; }
ACTION_RETURN_BOOL(self->SetState(state, nofunction));
};
//============================================================================
//
// AActor :: AddInventory

View file

@ -905,6 +905,14 @@ done:
return res;
}
DEFINE_ACTION_FUNCTION(AActor, CheckSight)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_OBJECT(target, AActor);
PARAM_INT_OPT(flags) { flags = 0; }
ACTION_RETURN_BOOL(P_CheckSight(self, target, flags));
}
ADD_STAT (sight)
{
FString out;

View file

@ -1519,9 +1519,8 @@ ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build)
assert(ValueType == TypeBool);
ExpEmit from = Operand->Emit(build);
assert(!from.Konst);
// ~x & 1
build->Emit(OP_NOT, from.RegNum, from.RegNum, 0);
build->Emit(OP_AND_RK, from.RegNum, from.RegNum, build->GetConstantInt(1));
// boolean not is the same as XOR-ing the lowest bit
build->Emit(OP_XOR_RK, from.RegNum, from.RegNum, build->GetConstantInt(1));
return from;
}
@ -1734,32 +1733,54 @@ ExpEmit FxPostIncrDecr::Emit(VMFunctionBuilder *build)
int zero = build->GetConstantInt(0);
int regtype = ValueType->GetRegType();
ExpEmit pointer = Base->Emit(build);
ExpEmit out = pointer;
if (!pointer.Target)
{
out = ExpEmit(build, regtype);
ExpEmit out(build, regtype);
build->Emit(ValueType->GetLoadOp(), out.RegNum, pointer.RegNum, zero);
ExpEmit assign(build, regtype);
if (regtype == REGT_INT)
{
build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, assign.RegNum, out.RegNum, build->GetConstantInt(1));
}
else
{
build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, assign.RegNum, out.RegNum, build->GetConstantFloat(1.));
}
build->Emit(ValueType->GetStoreOp(), pointer.RegNum, out.RegNum, zero);
pointer.Free(build);
assign.Free(build);
return out;
}
ExpEmit assign(build, regtype);
if (regtype == REGT_INT)
else if (NeedResult)
{
build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, assign.RegNum, out.RegNum, build->GetConstantInt(1));
ExpEmit out(build, regtype);
if (regtype == REGT_INT)
{
build->Emit(OP_MOVE, out.RegNum, pointer.RegNum);
build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, pointer.RegNum, pointer.RegNum, build->GetConstantInt(1));
}
else
{
build->Emit(OP_MOVEF, out.RegNum, pointer.RegNum);
build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, pointer.RegNum, pointer.RegNum, build->GetConstantFloat(1.));
}
pointer.Free(build);
return out;
}
else
{
build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, assign.RegNum, out.RegNum, build->GetConstantFloat(1.));
if (regtype == REGT_INT)
{
build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, pointer.RegNum, pointer.RegNum, build->GetConstantInt(1));
}
else
{
build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, pointer.RegNum, pointer.RegNum, build->GetConstantFloat(1.));
}
pointer.Free(build);
return ExpEmit();
}
if (!pointer.Target)
{
build->Emit(ValueType->GetStoreOp(), pointer.RegNum, assign.RegNum, zero);
}
pointer.Free(build);
assign.Free(build);
return out;
}
//==========================================================================
@ -5795,7 +5816,8 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx)
auto x = Condition;
Condition = nullptr;
delete this;
return Condition;
x->NeedResult = false;
return x;
}
}
@ -5805,6 +5827,7 @@ FxExpression *FxSwitchStatement::Resolve(FCompileContext &ctx)
if (line->ExprType != EFX_JumpStatement || static_cast<FxJumpStatement *>(line)->Token != TK_Break)
{
SAFE_RESOLVE(line, ctx);
line->NeedResult = false;
}
}
@ -5999,6 +6022,8 @@ FxIfStatement::FxIfStatement(FxExpression *cond, FxExpression *true_part,
Condition = cond;
WhenTrue = true_part;
WhenFalse = false_part;
if (WhenTrue != nullptr) WhenTrue->NeedResult = false;
if (WhenFalse != nullptr) WhenFalse->NeedResult = false;
assert(cond != NULL);
}
@ -6338,6 +6363,8 @@ FxForLoop::FxForLoop(FxExpression *init, FxExpression *condition, FxExpression *
: FxLoopStatement(EFX_ForLoop, pos), Init(init), Condition(condition), Iteration(iteration), Code(code)
{
ValueType = TypeVoid;
if (Iteration != nullptr) Iteration->NeedResult = false;
if (Code != nullptr) Code->NeedResult = false;
}
FxForLoop::~FxForLoop()
@ -7139,19 +7166,19 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
{
default:
case REGT_INT:
build->Emit(OP_LK, build->GetConstantInt(constval->GetValue().GetInt()), RegNum);
build->Emit(OP_LK, RegNum, build->GetConstantInt(constval->GetValue().GetInt()));
break;
case REGT_FLOAT:
build->Emit(OP_LKF, build->GetConstantFloat(constval->GetValue().GetFloat()), RegNum);
build->Emit(OP_LKF, RegNum, build->GetConstantFloat(constval->GetValue().GetFloat()));
break;
case REGT_POINTER:
build->Emit(OP_LKP, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC), RegNum);
build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC));
break;
case REGT_STRING:
build->Emit(OP_LKS, build->GetConstantString(constval->GetValue().GetString()), RegNum);
build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString()));
}
emitval.Free(build);
}

View file

@ -300,6 +300,7 @@ public:
PType *ValueType = nullptr;
bool isresolved = false;
bool NeedResult = true; // should be set to false if not needed and properly handled by all nodes for their subnodes to eliminate redundant code
EFxType ExprType;
};
@ -1246,7 +1247,7 @@ public:
FxSequence(const FScriptPosition &pos) : FxExpression(EFX_Sequence, pos) {}
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
void Add(FxExpression *expr) { if (expr != NULL) Expressions.Push(expr); }
void Add(FxExpression *expr) { if (expr != NULL) Expressions.Push(expr); expr->NeedResult = false; }
VMFunction *GetDirectFunction();
};

View file

@ -680,6 +680,7 @@ void InitThingdef()
symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, threshold)));
symt.AddSymbol(new PField(NAME_DefThreshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DefThreshold)));
symt.AddSymbol(new PField(NAME_Damage, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DamageVal)));
symt.AddSymbol(new PField("visdir", TypeSInt32, VARF_Native, myoffsetof(AActor, visdir)));
symt.AddSymbol(new PField(NAME_VisibleStartAngle, TypeFloat64, VARF_Native, myoffsetof(AActor, VisibleStartAngle)));
symt.AddSymbol(new PField(NAME_VisibleStartPitch, TypeFloat64, VARF_Native, myoffsetof(AActor, VisibleStartPitch)));
symt.AddSymbol(new PField(NAME_VisibleEndAngle, TypeFloat64, VARF_Native, myoffsetof(AActor, VisibleEndAngle)));
@ -688,6 +689,7 @@ void InitThingdef()
symt.AddSymbol(new PField("Pos", TypeVector3, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos)));
symt.AddSymbol(new PField("Vel", TypeVector3, VARF_Native, myoffsetof(AActor, Vel)));
symt.AddSymbol(new PField("Scale", TypeVector2, VARF_Native, myoffsetof(AActor, Scale)));
symt.AddSymbol(new PField("SeeState", TypeState, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, SeeState)));
symt.AddSymbol(new PField(NAME_Target, TypeActor, VARF_Native, myoffsetof(AActor, target)));
symt.AddSymbol(new PField(NAME_Master, TypeActor, VARF_Native, myoffsetof(AActor, master)));
symt.AddSymbol(new PField(NAME_Tracer, TypeActor, VARF_Native, myoffsetof(AActor, tracer)));

View file

@ -915,7 +915,7 @@ 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].a == NULL)); FState *x = (FState *)param[p].a;
#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_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)));
@ -932,8 +932,8 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_FLOAT_OPT_AT(p,x) double x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_FLOAT); x = param[p].f; } else
#define PARAM_ANGLE_OPT_AT(p,x) DAngle x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_FLOAT); x = param[p].f; } else
#define PARAM_STRING_OPT_AT(p,x) FString x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_STRING); x = param[p].s(); } else
#define PARAM_STATE_OPT_AT(p,x) FState *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATE || param[p].a == NULL)); x = (FState *)param[p].a; } else
#define PARAM_STATEINFO_OPT_AT(p,x) FStateParamInfo *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATEINFO || param[p].a == NULL)); x = (FStateParamInfo *)param[p].a; } else
#define PARAM_STATE_OPT_AT(p,x) FState *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATE || param[p].atag == ATAG_GENERIC || param[p].a == NULL)); x = (FState *)param[p].a; } else
#define PARAM_STATEINFO_OPT_AT(p,x) FStateParamInfo *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATEINFO || param[p].atag == ATAG_GENERIC || param[p].a == NULL)); x = (FStateParamInfo *)param[p].a; } else
#define PARAM_POINTER_OPT_AT(p,x,type) type *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER); x = (type *)param[p].a; } else
#define PARAM_OBJECT_OPT_AT(p,x,type) type *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); } else
#define PARAM_CLASS_OPT_AT(p,x,base) base::MetaClass *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); } else

View file

@ -665,6 +665,7 @@ VMFunction *FFunctionBuildList::AddFunction(PFunction *functype, FxExpression *c
it.Code = code;
it.PrintableName = name;
it.Function = new VMScriptFunction;
it.Function->Name = functype->SymbolName;
it.Function->ImplicitArgs = functype->GetImplicitArgs();
it.Proto = nullptr;
it.FromDecorate = fromdecorate;

View file

@ -51,6 +51,12 @@ class Actor : Thinker native
native void TraceBleed(int damage, Actor missile);
native bool CheckMeleeRange();
native int DamageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags = 0, double angle = 0);
native double AimLineAttack(double angle, double distance, out FTranslatedLineTarget pLineTarget = null, double vrange = 0., int flags = 0, Actor target = null, Actor friender = null);
native Actor, int LineAttack(double angle, double distance, double pitch, int damage, Name damageType, class<Actor> pufftype, int flags = 0, out FTranslatedLineTarget victim = null);
native bool CheckSight(Actor target, int flags = 0);
native bool HitFriend();
native bool SetState(state st, bool nofunction = false);
// DECORATE compatible functions
native bool CheckClass(class<Actor> checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false);
@ -88,10 +94,7 @@ class Actor : Thinker native
native void A_FaceTarget(float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0);
native void A_FaceTracer(float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0);
native void A_FaceMaster(float max_turn = 0, float max_pitch = 270, float ang_offset = 0, float pitch_offset = 0, int flags = 0, float z_ofs = 0);
native void A_PosAttack();
native void A_Scream();
native void A_SPosAttack();
native void A_SPosAttackUseAtkSound();
native void A_VileChase();
native void A_VileStart();
native void A_VileTarget(class<Actor> fire = "ArchvileFire");
@ -108,8 +111,6 @@ class Actor : Thinker native
native void A_FatAttack2(class<Actor> spawntype = "FatShot");
native void A_FatAttack3(class<Actor> spawntype = "FatShot");
native void A_BossDeath();
native void A_CPosAttack();
native void A_CPosRefire();
native void A_SkullAttack(float speed = 20);
native void A_BetaSkullAttack();
native void A_Metal();

View file

@ -824,4 +824,41 @@ struct FStateParamInfo
int mPSPIndex;
}
// returned by AimLineAttack.
struct FTranslatedLineTarget
{
Actor linetarget;
double angleFromSource;
bool unlinked; // found by a trace that went through an unlinked portal.
}
enum EAimFlags
{
ALF_FORCENOSMART = 1,
ALF_CHECK3D = 2,
ALF_CHECKNONSHOOTABLE = 4,
ALF_CHECKCONVERSATION = 8,
ALF_NOFRIENDS = 16,
ALF_PORTALRESTRICT = 32, // only work through portals with a global offset (to be used for stuff that cannot remember the calculated FTranslatedLineTarget info)
}
enum ELineAttackFlags
{
LAF_ISMELEEATTACK = 1,
LAF_NORANDOMPUFFZ = 2,
LAF_NOIMPACTDECAL = 4,
LAF_NOINTERACT = 8,
}
const MELEERANGE = 64;
const SAWRANGE = (64.+(1./65536.)); // use meleerange + 1 so the puff doesn't skip the flash (i.e. plays all states)
const MISSILERANGE = (32*64);
const PLAYERMISSILERANGE = 8192; // [RH] New MISSILERANGE for players
enum ESightFlags
{
SF_IGNOREVISIBILITY=1,
SF_SEEPASTSHOOTABLELINES=2,
SF_SEEPASTBLOCKEVERYTHING=4,
SF_IGNOREWATERBOUNDARY=8
}

View file

@ -251,3 +251,93 @@ class WolfensteinSS : Actor
Goto See ;
}
}
//===========================================================================
//
// Code (must be attached to Actor)
//
//===========================================================================
extend class Actor
{
void A_PosAttack()
{
if (target)
{
A_FaceTarget();
double ang = angle;
double slope = AimLineAttack(ang, MISSILERANGE);
A_PlaySound("grunt/attack", CHAN_WEAPON);
ang += Random2[PosAttack]() * (22.5/256);
int damage = Random[PosAttack](1, 5) * 3;
LineAttack(ang, MISSILERANGE, slope, damage, "Hitscan", "Bulletpuff");
}
}
private void A_SPosAttackInternal()
{
if (target)
{
A_FaceTarget();
double bangle = angle;
double slope = AimLineAttack(bangle, MISSILERANGE);
for (int i=0 ; i<3 ; i++)
{
double ang = bangle + Random2[SPosAttack]() * (22.5/256);
int damage = Random[SPosAttack](1, 5) * 3;
LineAttack(ang, MISSILERANGE, slope, damage, "Hitscan", "Bulletpuff");
}
}
}
void A_SPosAttackUseAtkSound()
{
if (target)
{
A_PlaySound(AttackSound, CHAN_WEAPON);
A_SPosAttackInternal();
}
}
// This version of the function, which uses a hard-coded sound, is meant for Dehacked only.
void A_SPosAttack()
{
if (target)
{
A_PlaySound("shotguy/attack", CHAN_WEAPON);
A_SPosAttackInternal();
}
}
void A_CPosAttack()
{
if (target)
{
if (bStealth) visdir = 1;
A_PlaySound(AttackSound, CHAN_WEAPON);
A_FaceTarget();
double slope = AimLineAttack(angle, MISSILERANGE);
double ang = angle + Random2[SPosAttack]() * (22.5/256);
int damage = Random[CPosAttack](1, 5) * 3;
LineAttack(ang, MISSILERANGE, slope, damage, "Hitscan", "Bulletpuff");
}
}
void A_CPosRefire()
{
// keep firing unless target got out of sight
A_FaceTarget();
if (Random[CPosRefire](0, 255) >= 40)
{
if (!target
|| HitFriend()
|| target.health <= 0
|| !CheckSight(target, SF_SEEPASTBLOCKEVERYTHING|SF_SEEPASTSHOOTABLELINES))
{
SetState(SeeState);
}
}
}
}