- Sync scripting branch with what was in trunk on Sunday. I believe that would be revision 2739.

SVN r2790 (scripting)
This commit is contained in:
Randy Heit 2010-09-16 03:14:32 +00:00
commit 99670b708c
317 changed files with 17299 additions and 7273 deletions

View file

@ -66,6 +66,7 @@
#include "v_font.h"
#include "doomstat.h"
#include "v_palette.h"
#include "g_shared/a_specialspot.h"
static FRandom pr_camissile ("CustomActorfire");
@ -81,6 +82,7 @@ static FRandom pr_spawndebris ("SpawnDebris");
static FRandom pr_spawnitemex ("SpawnItemEx");
static FRandom pr_burst ("Burst");
static FRandom pr_monsterrefire ("MonsterRefire");
static FRandom pr_teleport("A_Teleport");
//==========================================================================
@ -368,6 +370,7 @@ static FRandom pr_seekermissile ("SeekerMissile");
enum
{
SMF_LOOK = 1,
SMF_PRECISE = 2,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile)
{
@ -382,7 +385,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SeekerMissile)
{
self->tracer = P_RoughMonsterSearch (self, distance);
}
P_SeekerMissile(self, clamp<int>(ang1, 0, 90) * ANGLE_1, clamp<int>(ang2, 0, 90) * ANGLE_1);
P_SeekerMissile(self, clamp<int>(ang1, 0, 90) * ANGLE_1, clamp<int>(ang2, 0, 90) * ANGLE_1, !!(flags & SMF_PRECISE));
return 0;
}
@ -500,12 +503,29 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHealthLower)
// State jump function
//
//==========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser)
void DoJumpIfCloser(AActor *target, VM_ARGS)
{
PARAM_ACTION_PROLOGUE;
PARAM_FIXED (dist);
PARAM_STATE (jump);
ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains!
// No target - no jump
if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist &&
( (self->z > target->z && self->z - (target->z + target->height) < dist) ||
(self->z <=target->z && target->z - (self->z + self->height) < dist)
)
)
{
ACTION_JUMP(jump);
}
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser)
{
PARAM_ACTION_PROLOGUE;
AActor *target;
if (self->player == NULL)
@ -517,24 +537,29 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfCloser)
// Does the player aim at something that can be shot?
P_BulletSlope(self, &target);
}
DoJumpIfCloser(target, VM_ARGS_NAMES);
return 0;
}
ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains!
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTracerCloser)
{
PARAM_ACTION_PROLOGUE;
// No target - no jump
if (target == NULL)
return 0;
if (P_AproxDistance(self->x-target->x, self->y-target->y) < dist &&
( (self->z > target->z && self->z - (target->z + target->height) < dist) ||
(self->z <=target->z && target->z - (self->z + self->height) < dist)
)
)
// Is there really any reason to limit this to seeker missiles?
if (self->flags2 & MF2_SEEKERMISSILE)
{
ACTION_JUMP(jump);
DoJumpIfCloser(self->tracer, VM_ARGS_NAMES);
}
return 0;
}
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfMasterCloser)
{
PARAM_ACTION_PROLOGUE;
DoJumpIfCloser(self->master, VM_ARGS_NAMES);
return 0;
}
//==========================================================================
//
// State jump function
@ -851,6 +876,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile)
// An even more customizable hitscan attack
//
//==========================================================================
enum CBA_Flags
{
CBAF_AIMFACING = 1,
CBAF_NORANDOM = 2,
CBAF_EXPLICITANGLE = 4,
CBAF_NOPITCH = 8,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack)
{
PARAM_ACTION_PROLOGUE;
@ -860,7 +893,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack)
PARAM_INT (damageperbullet);
PARAM_CLASS_OPT (pufftype, AActor) { pufftype = PClass::FindActor(NAME_BulletPuff); }
PARAM_FIXED_OPT (range) { range = MISSILERANGE; }
PARAM_BOOL_OPT (aimfacing) { aimfacing = false; }
PARAM_INT_OPT (flags) { flags = 0; }
if (range == 0)
range = MISSILERANGE;
@ -869,19 +902,35 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomBulletAttack)
int bangle;
int bslope;
if (self->target || aimfacing)
if (self->target || (flags & CBAF_AIMFACING))
{
if (!aimfacing) A_FaceTarget (self);
if (!(flags & CBAF_AIMFACING)) A_FaceTarget (self);
bangle = self->angle;
bslope = P_AimLineAttack (self, bangle, MISSILERANGE);
if (!(flags & CBAF_NOPITCH)) bslope = P_AimLineAttack (self, bangle, MISSILERANGE);
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
for (i = 0; i < numbullets; i++)
{
int angle = bangle + pr_cabullet.Random2() * (spread_xy / 255);
int slope = bslope + pr_cabullet.Random2() * (spread_z / 255);
int damage = ((pr_cabullet()%3)+1) * damageperbullet;
int angle = bangle;
int slope = bslope;
if (flags & CBAF_EXPLICITANGLE)
{
angle += spread_xy;
slope += spread_z;
}
else
{
angle += pr_cwbullet.Random2() * (spread_xy / 255);
slope += pr_cwbullet.Random2() * (spread_z / 255);
}
int damage = damageperbullet;
if (!(flags & CBAF_NORANDOM))
damage *= ((pr_cabullet()%3)+1);
P_LineAttack(self, angle, range, slope, damage, NAME_None, pufftype);
}
}
@ -1006,6 +1055,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfNoAmmo)
// An even more customizable hitscan attack
//
//==========================================================================
enum FB_Flags
{
FBF_USEAMMO = 1,
FBF_NORANDOM = 2,
FBF_EXPLICITANGLE = 4,
FBF_NOPITCH = 8,
FBF_NOFLASH = 16,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
{
PARAM_ACTION_PROLOGUE;
@ -1014,7 +1072,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
PARAM_INT (numbullets);
PARAM_INT (damageperbullet);
PARAM_CLASS_OPT (pufftype, AActor) { pufftype = NULL; }
PARAM_BOOL_OPT (useammo) { useammo = true; }
PARAM_INT_OPT (flags) { flags = FBF_USEAMMO; }
PARAM_FIXED_OPT (range) { range = 0; }
if (!self->player) return 0;
@ -1026,7 +1084,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
int bangle;
int bslope;
if (useammo && weapon)
if ((flags & FBF_USEAMMO) && weapon)
{
if (!weapon->DepleteAmmo(weapon->bAltFire, true))
return 0; // out of ammo
@ -1035,9 +1093,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
if (range == 0)
range = PLAYERMISSILERANGE;
static_cast<APlayerPawn *>(self)->PlayAttacking2();
if (!(flags & FBF_NOFLASH)) static_cast<APlayerPawn *>(self)->PlayAttacking2 ();
bslope = P_BulletSlope(self);
if (!(flags & FBF_NOPITCH)) bslope = P_BulletSlope(self);
bangle = self->angle;
if (pufftype == NULL)
@ -1047,7 +1105,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
if ((numbullets == 1 && !player->refire) || numbullets == 0)
{
int damage = ((pr_cwbullet()%3)+1) * damageperbullet;
int damage = damageperbullet;
if (!(flags & FBF_NORANDOM))
damage *= ((pr_cwbullet()%3)+1);
P_LineAttack(self, bangle, range, bslope, damage, NAME_None, pufftype);
}
else
@ -1056,9 +1118,25 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireBullets)
numbullets = 1;
for (i = 0; i < numbullets; i++)
{
int angle = bangle + pr_cwbullet.Random2() * (spread_xy / 255);
int slope = bslope + pr_cwbullet.Random2() * (spread_z / 255);
int damage = ((pr_cwbullet()%3)+1) * damageperbullet;
int angle = bangle;
int slope = bslope;
if (flags & FBF_EXPLICITANGLE)
{
angle += spread_xy;
slope += spread_z;
}
else
{
angle += pr_cwbullet.Random2() * (spread_xy / 255);
slope += pr_cwbullet.Random2() * (spread_z / 255);
}
int damage = damageperbullet;
if (!(flags & FBF_NORANDOM))
damage *= ((pr_cwbullet()%3)+1);
P_LineAttack(self, angle, range, slope, damage, NAME_None, pufftype);
}
}
@ -1139,14 +1217,23 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireCustomMissile)
// Berserk is not handled here. That can be done with A_CheckIfInventory
//
//==========================================================================
enum
{
CPF_USEAMMO = 1,
CPF_DAGGER = 2,
CPF_PULLIN = 4,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch)
{
PARAM_ACTION_PROLOGUE;
PARAM_INT (damage);
PARAM_BOOL_OPT (norandom) { norandom = false; }
PARAM_BOOL_OPT (useammo) { useammo = true; }
PARAM_INT_OPT (flags) { flags = CPF_USEAMMO; }
PARAM_CLASS_OPT (pufftype, AActor) { pufftype = NULL; }
PARAM_FIXED_OPT (range) { range = 0; }
PARAM_FIXED_OPT (lifesteal) { lifesteal = 0; }
if (!self->player)
return 0;
@ -1168,7 +1255,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch)
pitch = P_AimLineAttack (self, angle, range, &linetarget);
// only use ammo when actually hitting something!
if (useammo && linetarget && weapon)
if ((flags & CPF_USEAMMO) && linetarget && weapon)
{
if (!weapon->DepleteAmmo(weapon->bAltFire, true))
return 0; // out of ammo
@ -1177,14 +1264,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch)
if (pufftype == NULL)
pufftype = PClass::FindActor(NAME_BulletPuff);
P_LineAttack (self, angle, range, pitch, damage, NAME_None, pufftype, true);
P_LineAttack (self, angle, range, pitch, damage, NAME_None, pufftype, true, &linetarget);
// turn to face target
if (linetarget)
{
if (lifesteal)
P_GiveBody (self, (damage * lifesteal) >> FRACBITS);
S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM);
self->angle = R_PointToAngle2 (self->x, self->y, linetarget->x, linetarget->y);
if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED;
if (flags & CPF_DAGGER) P_DaggerAlert (self, linetarget);
}
return 0;
}
@ -1193,7 +1287,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch)
enum
{
RAF_SILENT = 1,
RAF_NOPIERCE = 2
RAF_NOPIERCE = 2,
RAF_EXPLICITANGLE = 4,
};
//==========================================================================
@ -1212,6 +1307,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack)
PARAM_INT_OPT (flags) { flags = 0; }
PARAM_FLOAT_OPT (maxdiff) { maxdiff = 0; }
PARAM_CLASS_OPT (pufftype, AActor) { pufftype = PClass::FindActor(NAME_BulletPuff); }
PARAM_ANGLE_OPT (spread_xy) { spread_xy = 0; }
PARAM_ANGLE_OPT (spread_z) { spread_z = 0; }
if (!self->player)
return 0;
@ -1225,7 +1322,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack)
return 0; // out of ammo
}
P_RailAttack (self, damage, spawnofs_xy, color1, color2, float(maxdiff), (flags & RAF_SILENT), pufftype, (!(flags & RAF_NOPIERCE)));
angle_t angle;
angle_t slope;
if (flags & RAF_EXPLICITANGLE)
{
angle = spread_xy;
slope = spread_z;
}
else
{
angle = pr_crailgun.Random2() * (spread_xy / 255);
slope = pr_crailgun.Random2() * (spread_z / 255);
}
P_RailAttack (self, damage, spawnofs_xy, color1, color2, maxdiff, (flags & RAF_SILENT), pufftype, (!(flags & RAF_NOPIERCE)), angle, slope);
return 0;
}
@ -1238,7 +1349,8 @@ enum
{
CRF_DONTAIM = 0,
CRF_AIMPARALLEL = 1,
CRF_AIMDIRECT = 2
CRF_AIMDIRECT = 2,
CRF_EXPLICITANGLE = 4,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun)
@ -1252,6 +1364,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun)
PARAM_INT_OPT (aim) { aim = CRF_DONTAIM; }
PARAM_FLOAT_OPT (maxdiff) { maxdiff = 0; }
PARAM_CLASS_OPT (pufftype, AActor) { pufftype = PClass::FindActor(NAME_BulletPuff); }
PARAM_ANGLE_OPT (spread_xy) { spread_xy = 0; }
PARAM_ANGLE_OPT (spread_z) { spread_z = 0; }
AActor *linetarget;
@ -1318,7 +1432,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun)
angle_t angle = (self->angle - ANG90) >> ANGLETOFINESHIFT;
P_RailAttack (self, damage, spawnofs_xy, color1, color2, float(maxdiff), flags & RAF_SILENT, pufftype, !(flags & RAF_NOPIERCE));
angle_t angleoffset;
angle_t slopeoffset;
if (flags & CRF_EXPLICITANGLE)
{
angleoffset = spread_xy;
slopeoffset = spread_z;
}
else
{
angleoffset = pr_crailgun.Random2() * (spread_xy / 255);
slopeoffset = pr_crailgun.Random2() * (spread_z / 255);
}
P_RailAttack (self, damage, spawnofs_xy, color1, color2, maxdiff, (flags & RAF_SILENT), pufftype, (!(flags & RAF_NOPIERCE)), angleoffset, slopeoffset);
self->x = saved_x;
self->y = saved_y;
@ -1400,11 +1528,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget)
//
//===========================================================================
enum
{
TIF_NOTAKEINFINITE = 1,
};
void DoTakeInventory(AActor *receiver, StateCallData *statecall, VM_ARGS)
{
int paramnum = NAP-1;
PARAM_CLASS (itemtype, AInventory);
PARAM_INT_OPT (amount) { amount = 0; }
PARAM_INT_OPT (flags) { flags = 0; }
if (itemtype == NULL || receiver == NULL) return;
@ -1418,12 +1552,18 @@ void DoTakeInventory(AActor *receiver, StateCallData *statecall, VM_ARGS)
{
res = true;
}
if (amount == 0 || amount >= inv->Amount)
// Do not take ammo if the "no take infinite/take as ammo depletion" flag is set
// and infinite ammo is on
if (flags & TIF_NOTAKEINFINITE &&
((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) &&
inv->IsKindOf(RUNTIME_CLASS(AAmmo)))
{
if (inv->ItemFlags & IF_KEEPDEPLETED)
inv->Amount = 0;
else
inv->Destroy();
// Nothing to do here, except maybe res = false;? Would it make sense?
}
else if (!amount || amount>=inv->Amount)
{
if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0;
else inv->Destroy();
}
else
{
@ -1833,6 +1973,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Print)
C_MidPrint(font != NULL ? font : SmallFont, formatted.GetChars());
con_midtime = saved;
}
ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains!
return 0;
}
@ -1863,6 +2004,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_PrintBold)
FString formatted = strbin1(text);
C_MidPrintBold(font != NULL ? font : SmallFont, formatted.GetChars());
con_midtime = saved;
ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains!
return 0;
}
@ -1877,6 +2019,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Log)
PARAM_ACTION_PROLOGUE;
PARAM_STRING(text);
Printf("%s\n", text);
ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains!
return 0;
}
@ -1891,6 +2034,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LogInt)
PARAM_ACTION_PROLOGUE;
PARAM_INT(num);
Printf("%d\n", num);
ACTION_SET_RESULT(false); // Prints should never set the result for inventory state chains!
return 0;
}
@ -1926,10 +2070,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn)
PARAM_FIXED_OPT(reduce) { reduce = FRACUNIT/10; }
if (reduce == 0)
{
reduce = FRACUNIT/10;
}
self->RenderStyle.Flags &= ~STYLEF_Alpha1;
self->alpha += reduce;
// Should this clamp alpha to 1.0?
return 0;
}
@ -1947,12 +2093,57 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut)
PARAM_BOOL_OPT(remove) { remove = true; }
if (reduce == 0)
{
reduce = FRACUNIT/10;
}
self->RenderStyle.Flags &= ~STYLEF_Alpha1;
self->alpha -= reduce;
if (self->alpha<=0 && remove)
if (self->alpha <= 0 && remove)
{
self->Destroy();
}
return 0;
}
//===========================================================================
//
// A_FadeTo
//
// fades the actor to a specified transparency by a specified amount and
// destroys it if so desired
//
//===========================================================================
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo)
{
PARAM_ACTION_PROLOGUE;
PARAM_FIXED (target);
PARAM_FIXED_OPT (amount) { amount = fixed_t(0.1*FRACUNIT); }
PARAM_BOOL_OPT (remove) { remove = false; }
self->RenderStyle.Flags &= ~STYLEF_Alpha1;
if (self->alpha > target)
{
self->alpha -= amount;
if (self->alpha < target)
{
self->alpha = target;
}
}
else if (self->alpha < target)
{
self->alpha += amount;
if (self->alpha > target)
{
self->alpha = target;
}
}
if (self->alpha == target && remove)
{
self->Destroy();
}
return 0;
}
@ -2256,49 +2447,51 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Burst)
PARAM_ACTION_PROLOGUE;
PARAM_CLASS(chunk, AActor);
int i, numChunks;
AActor *mo;
int i, numChunks;
AActor * mo;
if (chunk == NULL)
return 0;
if (chunk == NULL)
{
return 0;
}
self->velx = self->vely = self->velz = 0;
self->height = self->GetDefault()->height;
self->velx = self->vely = self->velz = 0;
self->height = self->GetDefault()->height;
// [RH] In Hexen, this creates a random number of shards (range [24,56])
// with no relation to the size of the self shattering. I think it should
// base the number of shards on the size of the dead thing, so bigger
// things break up into more shards than smaller things.
// An self with radius 20 and height 64 creates ~40 chunks.
numChunks = MAX<int> (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32);
i = (pr_burst.Random2()) % (numChunks/4);
for (i = MAX (24, numChunks + i); i >= 0; i--)
{
mo = Spawn(chunk,
self->x + (((pr_burst()-128)*self->radius)>>7),
self->y + (((pr_burst()-128)*self->radius)>>7),
self->z + (pr_burst()*self->height/255), ALLOW_REPLACE);
// [RH] In Hexen, this creates a random number of shards (range [24,56])
// with no relation to the size of the self shattering. I think it should
// base the number of shards on the size of the dead thing, so bigger
// things break up into more shards than smaller things.
// An self with radius 20 and height 64 creates ~40 chunks.
numChunks = MAX<int> (4, (self->radius>>FRACBITS)*(self->height>>FRACBITS)/32);
i = (pr_burst.Random2()) % (numChunks/4);
for (i = MAX (24, numChunks + i); i >= 0; i--)
{
mo = Spawn(chunk,
self->x + (((pr_burst()-128)*self->radius)>>7),
self->y + (((pr_burst()-128)*self->radius)>>7),
self->z + (pr_burst()*self->height/255), ALLOW_REPLACE);
if (mo)
{
mo->velz = FixedDiv(mo->z - self->z, self->height)<<2;
mo->velx = pr_burst.Random2 () << (FRACBITS-7);
mo->vely = pr_burst.Random2 () << (FRACBITS-7);
mo->RenderStyle = self->RenderStyle;
mo->alpha = self->alpha;
mo->CopyFriendliness(self, true);
}
}
if (mo)
{
mo->velz = FixedDiv(mo->z - self->z, self->height)<<2;
mo->velx = pr_burst.Random2 () << (FRACBITS-7);
mo->vely = pr_burst.Random2 () << (FRACBITS-7);
mo->RenderStyle = self->RenderStyle;
mo->alpha = self->alpha;
mo->CopyFriendliness(self, true);
}
}
// [RH] Do some stuff to make this more useful outside Hexen
if (self->flags4 & MF4_BOSSDEATH)
{
// [RH] Do some stuff to make this more useful outside Hexen
if (self->flags4 & MF4_BOSSDEATH)
{
CALL_ACTION(A_BossDeath, self);
}
CALL_ACTION(A_NoBlocking, self);
}
A_Unblock(self, true);
self->Destroy();
return 0;
self->Destroy ();
return 0;
}
//===========================================================================
@ -2508,23 +2701,38 @@ DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget)
//==========================================================================
//
// A_JumpIfTargetInLOS (state label, optional fixed fov, optional bool
// projectiletarget)
// A_JumpIfTargetInLOS (state label, optional fixed fov, optional int flags,
// optional fixed dist_max, optional fixed dist_close)
//
// Jumps if the actor can see its target, or if the player has a linetarget.
// ProjectileTarget affects how projectiles are treated. If set, it will use
// the target of the projectile for seekers, and ignore the target for
// normal projectiles. If not set, it will use the missile's owner instead
// (the default).
// (the default). ProjectileTarget is now flag JLOSF_PROJECTILE. dist_max
// sets the maximum distance that actor can see, 0 means forever. dist_close
// uses special behavior if certain flags are set, 0 means no checks.
//
//==========================================================================
enum JLOS_flags
{
JLOSF_PROJECTILE=1,
JLOSF_NOSIGHT=2,
JLOSF_CLOSENOFOV=4,
JLOSF_CLOSENOSIGHT=8,
JLOSF_CLOSENOJUMP=16,
JLOSF_DEADNOJUMP=32,
JLOSF_CHECKMASTER=64,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
{
PARAM_ACTION_PROLOGUE;
PARAM_STATE (jump);
PARAM_ANGLE_OPT (fov) { fov = 0; }
PARAM_BOOL_OPT (projtarg) { projtarg = false; }
PARAM_ANGLE_OPT (fov) { fov = 0; }
PARAM_INT_OPT (flags) { flags = 0; }
PARAM_FIXED_OPT (dist_max) { dist_max = 0; }
PARAM_FIXED_OPT (dist_close) { dist_close = 0; }
angle_t an;
AActor *target;
@ -2533,7 +2741,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
if (!self->player)
{
if (self->flags & MF_MISSILE && projtarg)
if (flags & JLOSF_CHECKMASTER)
{
target = self->master;
}
else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE))
{
if (self->flags2 & MF2_SEEKERMISSILE)
target = self->tracer;
@ -2548,7 +2760,34 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
if (target == NULL)
return 0; // [KS] Let's not call P_CheckSight unnecessarily in this case.
if (!P_CheckSight (self, target, SF_IGNOREVISIBILITY))
if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0))
{
return 0;
}
fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y);
distance = P_AproxDistance(distance, target->z - self->z);
if (dist_max && (distance > dist_max))
{
return 0;
}
bool doCheckSight = !(flags & JLOSF_NOSIGHT);
if (dist_close && (distance < dist_close))
{
if (flags & JLOSF_CLOSENOJUMP)
return 0;
if (flags & JLOSF_CLOSENOFOV)
fov = 0;
if (flags & JLOSF_CLOSENOSIGHT)
doCheckSight = false;
}
if (doCheckSight && !P_CheckSight (self, target, SF_IGNOREVISIBILITY))
return 0;
if (fov && (fov < ANGLE_MAX))
@ -2566,10 +2805,25 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
{
// Does the player aim at something that can be shot?
P_BulletSlope(self, &target);
}
if (target == NULL)
return 0;
if (target == NULL)
{
return 0;
}
fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y);
distance = P_AproxDistance(distance, target->z - self->z);
if (dist_max && (distance > dist_max))
{
return 0;
}
if (dist_close && (distance < dist_close))
{
if (flags & JLOSF_CLOSENOJUMP)
return 0;
}
}
ACTION_JUMP(jump);
return 0;
@ -2578,8 +2832,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS)
//==========================================================================
//
// A_JumpIfInTargetLOS (state label, optional fixed fov, optional bool
// projectiletarget)
// A_JumpIfInTargetLOS (state label, optional fixed fov, optional int flags
// optional fixed dist_max, optional fixed dist_close)
//
//==========================================================================
@ -2587,15 +2841,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS)
{
PARAM_ACTION_PROLOGUE;
PARAM_STATE (jump);
PARAM_ANGLE_OPT (fov) { fov = 0; }
PARAM_BOOL_OPT (projtarg) { projtarg = false; }
PARAM_ANGLE_OPT (fov) { fov = 0; }
PARAM_INT_OPT (flags) { flags = 0; }
PARAM_FIXED_OPT (dist_max) { dist_max = 0; }
PARAM_FIXED_OPT (dist_close) { dist_close = 0; }
angle_t an;
AActor *target;
ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains!
if (self->flags & MF_MISSILE && projtarg)
if (flags & JLOSF_CHECKMASTER)
{
target = self->master;
}
else if (self->flags & MF_MISSILE && (flags & JLOSF_PROJECTILE))
{
if (self->flags2 & MF2_SEEKERMISSILE)
target = self->tracer;
@ -2608,9 +2868,38 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS)
}
if (target == NULL)
return 0; // [KS] Let's not call P_CheckSight unnecessarily in this case.
{ // [KS] Let's not call P_CheckSight unnecessarily in this case.
return 0;
}
if (!P_CheckSight (target, self, SF_IGNOREVISIBILITY))
if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0))
{
return 0;
}
fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y);
distance = P_AproxDistance(distance, target->z - self->z);
if (dist_max && (distance > dist_max))
{
return 0;
}
bool doCheckSight = !(flags & JLOSF_NOSIGHT);
if (dist_close && (distance < dist_close))
{
if (flags & JLOSF_CLOSENOJUMP)
return 0;
if (flags & JLOSF_CLOSENOFOV)
fov = 0;
if (flags & JLOSF_CLOSENOSIGHT)
doCheckSight = false;
}
if (doCheckSight && !P_CheckSight (target, self, SF_IGNOREVISIBILITY))
return 0;
if (fov && (fov < ANGLE_MAX))
@ -3187,16 +3476,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserVar)
PARAM_NAME (varname);
PARAM_INT (value);
PSymbolVariable *var = dyn_cast<PSymbolVariable>(stateowner->GetClass()->Symbols.FindSymbol(varname, true));
PSymbolVariable *var = dyn_cast<PSymbolVariable>(self->GetClass()->Symbols.FindSymbol(varname, true));
if (var == NULL || !var->bUserVar || var->ValueType.Type != VAL_Int)
{
Printf("%s is not a user variable in class %s\n", varname.GetChars(),
stateowner->GetClass()->TypeName.GetChars());
self->GetClass()->TypeName.GetChars());
return 0;
}
// Set the value of the specified user variable.
*(int *)(reinterpret_cast<BYTE *>(stateowner) + var->offset) = value;
*(int *)(reinterpret_cast<BYTE *>(self) + var->offset) = value;
return 0;
}
@ -3213,22 +3502,119 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetUserArray)
PARAM_INT (pos);
PARAM_INT (value);
PSymbolVariable *var = dyn_cast<PSymbolVariable>(stateowner->GetClass()->Symbols.FindSymbol(varname, true));
PSymbolVariable *var = dyn_cast<PSymbolVariable>(self->GetClass()->Symbols.FindSymbol(varname, true));
if (var == NULL || !var->bUserVar || var->ValueType.Type != VAL_Array || var->ValueType.BaseType != VAL_Int)
{
Printf("%s is not a user array in class %s\n", varname.GetChars(),
stateowner->GetClass()->TypeName.GetChars());
self->GetClass()->TypeName.GetChars());
return 0;
}
if (pos < 0 || pos >= var->ValueType.size)
{
Printf("%d is out of bounds in array %s in class %s\n", pos, varname.GetChars(),
stateowner->GetClass()->TypeName.GetChars());
self->GetClass()->TypeName.GetChars());
return 0;
}
// Set the value of the specified user array at index pos.
((int *)(reinterpret_cast<BYTE *>(stateowner) + var->offset))[pos] = value;
((int *)(reinterpret_cast<BYTE *>(self) + var->offset))[pos] = value;
return 0;
}
//===========================================================================
//
// A_Teleport(optional state teleportstate, optional class targettype,
// optional class fogtype, optional int flags, optional fixed mindist,
// optional fixed maxdist)
//
// Attempts to teleport to a targettype at least mindist away and at most
// maxdist away (0 means unlimited). If successful, spawn a fogtype at old
// location and place calling actor in teleportstate.
//
//===========================================================================
enum T_Flags
{
TF_TELEFRAG = 1, // Allow telefrag in order to teleport.
TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide)
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport)
{
PARAM_ACTION_PROLOGUE;
PARAM_STATE_OPT (teleport_state) { teleport_state = NULL; }
PARAM_CLASS_OPT (target_type, ASpecialSpot) { target_type = PClass::FindActor("BossSpot"); }
PARAM_CLASS_OPT (fog_type, AActor) { fog_type = PClass::FindActor("TeleportFog"); }
PARAM_INT_OPT (flags) { flags = 0; }
PARAM_FIXED_OPT (mindist) { mindist = 128 << FRACBITS; }
PARAM_FIXED_OPT (maxdist) { maxdist = 128 << FRACBITS; }
// Randomly choose not to teleport like A_Srcr2Decide.
if (flags & TF_RANDOMDECIDE)
{
static const int chance[] =
{
192, 120, 120, 120, 64, 64, 32, 16, 0
};
unsigned int chanceindex = self->health / ((self->SpawnHealth()/8 == 0) ? 1 : self->SpawnHealth()/8);
if (chanceindex >= countof(chance))
{
chanceindex = countof(chance) - 1;
}
if (pr_teleport() >= chance[chanceindex])
{
return 0;
}
}
if (teleport_state == NULL)
{
// Default to Teleport.
teleport_state = self->FindState("Teleport");
// If still nothing, then return.
if (teleport_state == NULL)
{
return 0;
}
}
DSpotState *state = DSpotState::GetSpotState();
if (state == NULL)
{
return 0;
}
if (target_type == NULL)
{
target_type = PClass::FindActor("BossSpot");
}
AActor *spot = state->GetSpotWithMinMaxDistance(target_type, self->x, self->y, mindist, maxdist);
if (spot == NULL)
{
return 0;
}
fixed_t prevX = self->x;
fixed_t prevY = self->y;
fixed_t prevZ = self->z;
if (P_TeleportMove (self, spot->x, spot->y, spot->z, flags & TF_TELEFRAG))
{
ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains!
if (fog_type != NULL)
{
Spawn(fog_type, prevX, prevY, prevZ, ALLOW_REPLACE);
}
ACTION_JUMP(teleport_state);
self->z = self->floorz;
self->angle = spot->angle;
self->velx = self->vely = self->velz = 0;
}
return 0;
}