- scriptified A_SpawnItem(Ex) and A_ThrowGrenade.

These were the last native functions referencing AWeapon::DepleteAmmo, so that function is now exclusively on the scripting side.
This commit is contained in:
Christoph Oelckers 2018-11-24 17:01:12 +01:00
commit b4c272ddff
5 changed files with 361 additions and 474 deletions

View file

@ -1960,452 +1960,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_TakeFromSiblings)
ACTION_RETURN_INT(count);
}
//===========================================================================
//
// Common code for A_SpawnItem and A_SpawnItemEx
//
//===========================================================================
enum SIX_Flags
{
SIXF_TRANSFERTRANSLATION = 0x00000001,
SIXF_ABSOLUTEPOSITION = 0x00000002,
SIXF_ABSOLUTEANGLE = 0x00000004,
SIXF_ABSOLUTEVELOCITY = 0x00000008,
SIXF_SETMASTER = 0x00000010,
SIXF_NOCHECKPOSITION = 0x00000020,
SIXF_TELEFRAG = 0x00000040,
SIXF_CLIENTSIDE = 0x00000080, // only used by Skulldronum
SIXF_TRANSFERAMBUSHFLAG = 0x00000100,
SIXF_TRANSFERPITCH = 0x00000200,
SIXF_TRANSFERPOINTERS = 0x00000400,
SIXF_USEBLOODCOLOR = 0x00000800,
SIXF_CLEARCALLERTID = 0x00001000,
SIXF_MULTIPLYSPEED = 0x00002000,
SIXF_TRANSFERSCALE = 0x00004000,
SIXF_TRANSFERSPECIAL = 0x00008000,
SIXF_CLEARCALLERSPECIAL = 0x00010000,
SIXF_TRANSFERSTENCILCOL = 0x00020000,
SIXF_TRANSFERALPHA = 0x00040000,
SIXF_TRANSFERRENDERSTYLE = 0x00080000,
SIXF_SETTARGET = 0x00100000,
SIXF_SETTRACER = 0x00200000,
SIXF_NOPOINTERS = 0x00400000,
SIXF_ORIGINATOR = 0x00800000,
SIXF_TRANSFERSPRITEFRAME = 0x01000000,
SIXF_TRANSFERROLL = 0x02000000,
SIXF_ISTARGET = 0x04000000,
SIXF_ISMASTER = 0x08000000,
SIXF_ISTRACER = 0x10000000,
};
static bool InitSpawnedItem(AActor *self, AActor *mo, int flags)
{
if (mo == NULL)
{
return false;
}
AActor *originator = self;
if (!(mo->flags2 & MF2_DONTTRANSLATE))
{
if (flags & SIXF_TRANSFERTRANSLATION)
{
mo->Translation = self->Translation;
}
else if (flags & SIXF_USEBLOODCOLOR)
{
// [XA] Use the spawning actor's BloodColor to translate the newly-spawned object.
mo->Translation = self->BloodTranslation;
}
}
if (flags & SIXF_TRANSFERPOINTERS)
{
mo->target = self->target;
mo->master = self->master; // This will be overridden later if SIXF_SETMASTER is set
mo->tracer = self->tracer;
}
mo->Angles.Yaw = self->Angles.Yaw;
if (flags & SIXF_TRANSFERPITCH)
{
mo->Angles.Pitch = self->Angles.Pitch;
}
if (!(flags & SIXF_ORIGINATOR))
{
while (originator && originator->isMissile())
{
originator = originator->target;
}
}
if (flags & SIXF_TELEFRAG)
{
P_TeleportMove(mo, mo->Pos(), true);
// This is needed to ensure consistent behavior.
// Otherwise it will only spawn if nothing gets telefragged
flags |= SIXF_NOCHECKPOSITION;
}
if (mo->flags3 & MF3_ISMONSTER)
{
if (!(flags & SIXF_NOCHECKPOSITION) && !P_TestMobjLocation(mo))
{
// The monster is blocked so don't spawn it at all!
mo->ClearCounters();
mo->Destroy();
return false;
}
else if (originator && !(flags & SIXF_NOPOINTERS))
{
if (originator->flags3 & MF3_ISMONSTER)
{
// If this is a monster transfer all friendliness information
mo->CopyFriendliness(originator, true);
}
else if (originator->player)
{
// A player always spawns a monster friendly to him
mo->flags |= MF_FRIENDLY;
mo->SetFriendPlayer(originator->player);
AActor * attacker=originator->player->attacker;
if (attacker)
{
if (!(attacker->flags&MF_FRIENDLY) ||
(deathmatch && attacker->FriendPlayer!=0 && attacker->FriendPlayer!=mo->FriendPlayer))
{
// Target the monster which last attacked the player
mo->LastHeard = mo->target = attacker;
}
}
}
}
}
else if (!(flags & SIXF_TRANSFERPOINTERS))
{
// If this is a missile or something else set the target to the originator
mo->target = originator ? originator : self;
}
if (flags & SIXF_NOPOINTERS)
{
//[MC]Intentionally eliminate pointers. Overrides TRANSFERPOINTERS, but is overridden by SETMASTER/TARGET/TRACER.
mo->LastHeard = NULL; //Sanity check.
mo->target = NULL;
mo->master = NULL;
mo->tracer = NULL;
}
if (flags & SIXF_SETMASTER)
{ // don't let it attack you (optional)!
mo->master = originator;
}
if (flags & SIXF_SETTARGET)
{
mo->target = originator;
}
if (flags & SIXF_SETTRACER)
{
mo->tracer = originator;
}
if (flags & SIXF_TRANSFERSCALE)
{
mo->Scale = self->Scale;
}
if (flags & SIXF_TRANSFERAMBUSHFLAG)
{
mo->flags = (mo->flags & ~MF_AMBUSH) | (self->flags & MF_AMBUSH);
}
if (flags & SIXF_CLEARCALLERTID)
{
self->RemoveFromHash();
self->tid = 0;
}
if (flags & SIXF_TRANSFERSPECIAL)
{
mo->special = self->special;
memcpy(mo->args, self->args, sizeof(self->args));
}
if (flags & SIXF_CLEARCALLERSPECIAL)
{
self->special = 0;
memset(self->args, 0, sizeof(self->args));
}
if (flags & SIXF_TRANSFERSTENCILCOL)
{
mo->fillcolor = self->fillcolor;
}
if (flags & SIXF_TRANSFERALPHA)
{
mo->Alpha = self->Alpha;
}
if (flags & SIXF_TRANSFERRENDERSTYLE)
{
mo->RenderStyle = self->RenderStyle;
}
if (flags & SIXF_TRANSFERSPRITEFRAME)
{
mo->sprite = self->sprite;
mo->frame = self->frame;
}
if (flags & SIXF_TRANSFERROLL)
{
mo->Angles.Roll = self->Angles.Roll;
}
if (flags & SIXF_ISTARGET)
{
self->target = mo;
}
if (flags & SIXF_ISMASTER)
{
self->master = mo;
}
if (flags & SIXF_ISTRACER)
{
self->tracer = mo;
}
return true;
}
//===========================================================================
//
// A_SpawnItem
//
// Spawns an item in front of the caller like Heretic's time bomb
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SpawnItem)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_CLASS (missile, AActor)
PARAM_FLOAT (distance)
PARAM_FLOAT (zheight)
PARAM_BOOL (useammo)
PARAM_BOOL (transfer_translation);
if (numret > 1) ret[1].SetObject(nullptr);
if (missile == NULL)
{
if (numret > 0) ret[0].SetInt(false);
return MIN(numret, 2);
}
// Don't spawn monsters if this actor has been massacred
if (self->DamageType == NAME_Massacre && (GetDefaultByType(missile)->flags3 & MF3_ISMONSTER))
{
if (numret > 0) ret[0].SetInt(true);
return MIN(numret, 2);
}
if (ACTION_CALL_FROM_PSPRITE())
{
// Used from a weapon, so use some ammo
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon == NULL)
{
if (numret > 0) ret[0].SetInt(true);
return MIN(numret, 2);
}
if (useammo && !weapon->DepleteAmmo(weapon->bAltFire))
{
if (numret > 0) ret[0].SetInt(true);
return MIN(numret, 2);
}
}
AActor *mo = Spawn( missile, self->Vec3Angle(distance, self->Angles.Yaw, -self->Floorclip + self->GetBobOffset() + zheight), ALLOW_REPLACE);
int flags = (transfer_translation ? SIXF_TRANSFERTRANSLATION : 0) + (useammo ? SIXF_SETMASTER : 0);
bool res = InitSpawnedItem(self, mo, flags); // for an inventory item's use state
if (numret > 0) ret[0].SetInt(res);
if (numret > 1) ret[1].SetObject(mo);
return MIN(numret, 2);
}
//===========================================================================
//
// A_SpawnItemEx
//
// Enhanced spawning function
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SpawnItemEx)
{
PARAM_SELF_PROLOGUE(AActor);
PARAM_CLASS (missile, AActor);
PARAM_FLOAT (xofs)
PARAM_FLOAT (yofs)
PARAM_FLOAT (zofs)
PARAM_FLOAT (xvel)
PARAM_FLOAT (yvel)
PARAM_FLOAT (zvel)
PARAM_ANGLE (angle)
PARAM_INT (flags)
PARAM_INT (chance)
PARAM_INT (tid)
if (numret > 1) ret[1].SetObject(nullptr);
if (missile == NULL)
{
if (numret > 0) ret[0].SetInt(false);
return MIN(numret, 2);
}
if (chance > 0 && pr_spawnitemex() < chance)
{
if (numret > 0) ret[0].SetInt(true);
return MIN(numret, 2);
}
// Don't spawn monsters if this actor has been massacred
if (self->DamageType == NAME_Massacre && (GetDefaultByType(missile)->flags3 & MF3_ISMONSTER))
{
if (numret > 0) ret[0].SetInt(true);
return MIN(numret, 2);
}
DVector2 pos;
if (!(flags & SIXF_ABSOLUTEANGLE))
{
angle += self->Angles.Yaw;
}
double s = angle.Sin();
double c = angle.Cos();
if (flags & SIXF_ABSOLUTEPOSITION)
{
pos = self->Vec2Offset(xofs, yofs);
}
else
{
// in relative mode negative y values mean 'left' and positive ones mean 'right'
// This is the inverse orientation of the absolute mode!
pos = self->Vec2Offset(xofs * c + yofs * s, xofs * s - yofs*c);
}
if (!(flags & SIXF_ABSOLUTEVELOCITY))
{
// Same orientation issue here!
double newxvel = xvel * c + yvel * s;
yvel = xvel * s - yvel * c;
xvel = newxvel;
}
AActor *mo = Spawn(missile, DVector3(pos, self->Z() - self->Floorclip + self->GetBobOffset() + zofs), ALLOW_REPLACE);
bool res = InitSpawnedItem(self, mo, flags);
if (res)
{
if (tid != 0)
{
assert(mo->tid == 0);
mo->tid = tid;
mo->AddToHash();
}
mo->Vel = {xvel, yvel, zvel};
if (flags & SIXF_MULTIPLYSPEED)
{
mo->Vel *= mo->Speed;
}
mo->Angles.Yaw = angle;
}
if (numret > 0) ret[0].SetInt(res);
if (numret > 1) ret[1].SetObject(mo);
return MIN(numret, 2);
}
//===========================================================================
//
// A_ThrowGrenade
//
// Throws a grenade (like Hexen's fighter flechette)
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_ThrowGrenade)
{
PARAM_ACTION_PROLOGUE(AActor);
PARAM_CLASS (missile, AActor);
PARAM_FLOAT (zheight)
PARAM_FLOAT (xyvel)
PARAM_FLOAT (zvel)
PARAM_BOOL (useammo)
if (numret > 1) ret[1].SetObject(nullptr);
if (missile == NULL)
{
if (numret > 0) ret[0].SetInt(false);
return MIN(numret, 2);
}
if (ACTION_CALL_FROM_PSPRITE())
{
// Used from a weapon, so use some ammo
AWeapon *weapon = self->player->ReadyWeapon;
if (weapon == NULL)
{
if (numret > 0) ret[0].SetInt(true);
return MIN(numret, 2);
}
if (useammo && !weapon->DepleteAmmo(weapon->bAltFire))
{
if (numret > 0) ret[0].SetInt(true);
return MIN(numret, 2);
}
}
AActor *bo;
bo = Spawn(missile,
self->PosPlusZ(-self->Floorclip + self->GetBobOffset() + zheight + 35 + (self->player? self->player->crouchoffset : 0.)),
ALLOW_REPLACE);
if (bo)
{
P_PlaySpawnSound(bo, self);
if (xyvel != 0)
bo->Speed = xyvel;
bo->Angles.Yaw = self->Angles.Yaw + (((pr_grenade()&7) - 4) * (360./256.));
DAngle pitch = -self->Angles.Pitch;
DAngle angle = bo->Angles.Yaw;
// There are two vectors we are concerned about here: xy and z. We rotate
// them separately according to the shooter's pitch and then sum them to
// get the final velocity vector to shoot with.
double xy_xyscale = bo->Speed * pitch.Cos();
double xy_velz = bo->Speed * pitch.Sin();
double xy_velx = xy_xyscale * angle.Cos();
double xy_vely = xy_xyscale * angle.Sin();
pitch = self->Angles.Pitch;
double z_xyscale = zvel * pitch.Sin();
double z_velz = zvel * pitch.Cos();
double z_velx = z_xyscale * angle.Cos();
double z_vely = z_xyscale * angle.Sin();
bo->Vel.X = xy_velx + z_velx + self->Vel.X / 2;
bo->Vel.Y = xy_vely + z_vely + self->Vel.Y / 2;
bo->Vel.Z = xy_velz + z_velz;
bo->target = self;
if (!P_CheckMissileSpawn(bo, self->radius)) bo = nullptr;
if (numret > 0) ret[0].SetInt(true);
if (numret > 1) ret[1].SetObject(bo);
return MIN(numret, 2);
}
else
{
if (numret > 0) ret[0].SetInt(false);
return MIN(numret, 2);
}
}
//===========================================================================
//
// A_Recoil