mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 13:01:47 +00:00
- 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:
parent
6be7fc33f3
commit
b4c272ddff
5 changed files with 361 additions and 474 deletions
|
@ -227,30 +227,6 @@ bool AWeapon::CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo, int amm
|
|||
return false;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// AWeapon :: DepleteAmmo
|
||||
//
|
||||
// Use up some of the weapon's ammo. Returns true if the ammo was successfully
|
||||
// depleted. If checkEnough is false, then the ammo will always be depleted,
|
||||
// even if it drops below zero.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool AWeapon::DepleteAmmo(bool altFire, bool checkEnough, int ammouse)
|
||||
{
|
||||
IFVIRTUAL(AWeapon, DepleteAmmo)
|
||||
{
|
||||
VMValue params[] = { (DObject*)this, altFire, checkEnough, ammouse };
|
||||
VMReturn ret;
|
||||
int retval;
|
||||
ret.IntAt(&retval);
|
||||
VMCall(func, params, 4, &ret, 1);
|
||||
return !!retval;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// AWeapon :: GetUpState
|
||||
|
|
|
@ -140,7 +140,6 @@ public:
|
|||
EitherFire
|
||||
};
|
||||
bool CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo=false, int ammocount = -1);
|
||||
bool DepleteAmmo (bool altFire, bool checkEnough=true, int ammouse = -1);
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1071,8 +1071,6 @@ class Actor : Thinker native
|
|||
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);
|
||||
action native bool, Actor A_SpawnItem(class<Actor> itemtype = "Unknown", double distance = 0, double zheight = 0, bool useammo = true, bool transfer_translation = false);
|
||||
native bool, Actor A_SpawnItemEx(class<Actor> itemtype, double xofs = 0, double yofs = 0, double zofs = 0, double xvel = 0, double yvel = 0, double zvel = 0, double angle = 0, int flags = 0, int failchance = 0, int tid=0);
|
||||
native void A_Print(string whattoprint, double time = 0, name fontname = "none");
|
||||
native void A_PrintBold(string whattoprint, double time = 0, name fontname = "none");
|
||||
native void A_Log(string whattoprint, bool local = false);
|
||||
|
@ -1097,7 +1095,6 @@ class Actor : Thinker native
|
|||
native bool RaiseActor(Actor other, int flags = 0);
|
||||
native bool CanRaise();
|
||||
native void Revive();
|
||||
action native bool, Actor A_ThrowGrenade(class<Actor> itemtype, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true);
|
||||
native void A_Weave(int xspeed, int yspeed, double xdist, double ydist);
|
||||
|
||||
action native state, bool A_Teleport(statelabel teleportstate = null, class<SpecialSpot> targettype = "BossSpot", class<Actor> fogtype = "TeleportFog", int flags = 0, double mindist = 128, double maxdist = 0, int ptr = AAPTR_DEFAULT);
|
||||
|
|
|
@ -170,5 +170,366 @@ extend class Actor
|
|||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Common code for A_SpawnItem and A_SpawnItemEx
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool InitSpawnedItem(Actor mo, int flags)
|
||||
{
|
||||
if (mo == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Actor originator = self;
|
||||
|
||||
if (!(mo.bDontTranslate))
|
||||
{
|
||||
if (flags & SXF_TRANSFERTRANSLATION)
|
||||
{
|
||||
mo.Translation = Translation;
|
||||
}
|
||||
else if (flags & SXF_USEBLOODCOLOR)
|
||||
{
|
||||
// [XA] Use the spawning actor's BloodColor to translate the newly-spawned object.
|
||||
mo.Translation = BloodTranslation;
|
||||
}
|
||||
}
|
||||
if (flags & SXF_TRANSFERPOINTERS)
|
||||
{
|
||||
mo.target = self.target;
|
||||
mo.master = self.master; // This will be overridden later if SXF_SETMASTER is set
|
||||
mo.tracer = self.tracer;
|
||||
}
|
||||
|
||||
mo.Angle = self.Angle;
|
||||
if (flags & SXF_TRANSFERPITCH)
|
||||
{
|
||||
mo.Pitch = self.Pitch;
|
||||
}
|
||||
if (!(flags & SXF_ORIGINATOR))
|
||||
{
|
||||
while (originator && (originator.bMissile || originator.default.bMissile))
|
||||
{
|
||||
originator = originator.target;
|
||||
}
|
||||
}
|
||||
if (flags & SXF_TELEFRAG)
|
||||
{
|
||||
mo.TeleportMove(mo.Pos, true);
|
||||
// This is needed to ensure consistent behavior.
|
||||
// Otherwise it will only spawn if nothing gets telefragged
|
||||
flags |= SXF_NOCHECKPOSITION;
|
||||
}
|
||||
if (mo.bIsMonster)
|
||||
{
|
||||
if (!(flags & SXF_NOCHECKPOSITION) && !mo.TestMobjLocation())
|
||||
{
|
||||
// The monster is blocked so don't spawn it at all!
|
||||
mo.ClearCounters();
|
||||
mo.Destroy();
|
||||
return false;
|
||||
}
|
||||
else if (originator && !(flags & SXF_NOPOINTERS))
|
||||
{
|
||||
if (originator.bIsMonster)
|
||||
{
|
||||
// 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.bFriendly = true;
|
||||
mo.SetFriendPlayer(originator.player);
|
||||
|
||||
Actor attacker=originator.player.attacker;
|
||||
if (attacker)
|
||||
{
|
||||
if (!(attacker.bFriendly) ||
|
||||
(deathmatch && attacker.FriendPlayer != 0 && attacker.FriendPlayer != mo.FriendPlayer))
|
||||
{
|
||||
// Target the monster which last attacked the player
|
||||
mo.LastHeard = mo.target = attacker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!(flags & SXF_TRANSFERPOINTERS))
|
||||
{
|
||||
// If this is a missile or something else set the target to the originator
|
||||
mo.target = originator ? originator : self;
|
||||
}
|
||||
if (flags & SXF_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 & SXF_SETMASTER)
|
||||
{ // don't let it attack you (optional)!
|
||||
mo.master = originator;
|
||||
}
|
||||
if (flags & SXF_SETTARGET)
|
||||
{
|
||||
mo.target = originator;
|
||||
}
|
||||
if (flags & SXF_SETTRACER)
|
||||
{
|
||||
mo.tracer = originator;
|
||||
}
|
||||
if (flags & SXF_TRANSFERSCALE)
|
||||
{
|
||||
mo.Scale = self.Scale;
|
||||
}
|
||||
if (flags & SXF_TRANSFERAMBUSHFLAG)
|
||||
{
|
||||
mo.bAmbush = bAmbush;
|
||||
}
|
||||
if (flags & SXF_CLEARCALLERTID)
|
||||
{
|
||||
self.ChangeTid(0);
|
||||
}
|
||||
if (flags & SXF_TRANSFERSPECIAL)
|
||||
{
|
||||
mo.special = self.special;
|
||||
mo.args[0] = self.args[0];
|
||||
mo.args[1] = self.args[1];
|
||||
mo.args[2] = self.args[2];
|
||||
mo.args[3] = self.args[3];
|
||||
mo.args[4] = self.args[4];
|
||||
}
|
||||
if (flags & SXF_CLEARCALLERSPECIAL)
|
||||
{
|
||||
self.special = 0;
|
||||
mo.args[0] = 0;
|
||||
mo.args[1] = 0;
|
||||
mo.args[2] = 0;
|
||||
mo.args[3] = 0;
|
||||
mo.args[4] = 0;
|
||||
}
|
||||
if (flags & SXF_TRANSFERSTENCILCOL)
|
||||
{
|
||||
mo.SetShade(self.fillcolor);
|
||||
}
|
||||
if (flags & SXF_TRANSFERALPHA)
|
||||
{
|
||||
mo.Alpha = self.Alpha;
|
||||
}
|
||||
if (flags & SXF_TRANSFERRENDERSTYLE)
|
||||
{
|
||||
mo.RenderStyle = self.RenderStyle;
|
||||
}
|
||||
|
||||
if (flags & SXF_TRANSFERSPRITEFRAME)
|
||||
{
|
||||
mo.sprite = self.sprite;
|
||||
mo.frame = self.frame;
|
||||
}
|
||||
|
||||
if (flags & SXF_TRANSFERROLL)
|
||||
{
|
||||
mo.Roll = self.Roll;
|
||||
}
|
||||
|
||||
if (flags & SXF_ISTARGET)
|
||||
{
|
||||
self.target = mo;
|
||||
}
|
||||
if (flags & SXF_ISMASTER)
|
||||
{
|
||||
self.master = mo;
|
||||
}
|
||||
if (flags & SXF_ISTRACER)
|
||||
{
|
||||
self.tracer = mo;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// A_SpawnItem
|
||||
//
|
||||
// Spawns an item in front of the caller like Heretic's time bomb
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
action bool, Actor A_SpawnItem(class<Actor> missile = "Unknown", double distance = 0, double zheight = 0, bool useammo = true, bool transfer_translation = false)
|
||||
{
|
||||
if (missile == NULL)
|
||||
{
|
||||
return false, null;
|
||||
}
|
||||
|
||||
// Don't spawn monsters if this actor has been massacred
|
||||
if (DamageType == 'Massacre' && GetDefaultByType(missile).bIsMonster)
|
||||
{
|
||||
return true, null;
|
||||
}
|
||||
|
||||
if (stateinfo != null && stateinfo.mStateType == STATE_Psprite)
|
||||
{
|
||||
let player = self.player;
|
||||
if (player == null) return false, null;
|
||||
let weapon = player.ReadyWeapon;
|
||||
// Used from a weapon, so use some ammo
|
||||
|
||||
if (weapon == NULL || (useammo && !weapon.DepleteAmmo(weapon.bAltFire)))
|
||||
{
|
||||
return true, null;
|
||||
}
|
||||
}
|
||||
|
||||
let mo = Spawn(missile, Vec3Angle(distance, Angle, -Floorclip + GetBobOffset() + zheight), ALLOW_REPLACE);
|
||||
|
||||
int flags = (transfer_translation ? SXF_TRANSFERTRANSLATION : 0) + (useammo ? SXF_SETMASTER : 0);
|
||||
bool res = InitSpawnedItem(mo, flags); // for an inventory item's use state
|
||||
return res, mo;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// A_SpawnItemEx
|
||||
//
|
||||
// Enhanced spawning function
|
||||
//
|
||||
//===========================================================================
|
||||
bool, Actor A_SpawnItemEx(class<Actor> missile, double xofs = 0, double yofs = 0, double zofs = 0, double xvel = 0, double yvel = 0, double zvel = 0, double angle = 0, int flags = 0, int chance = 0, int tid=0)
|
||||
{
|
||||
if (missile == NULL)
|
||||
{
|
||||
return false, null;
|
||||
}
|
||||
if (chance > 0 && random[spawnitemex]() < chance)
|
||||
{
|
||||
return true, null;
|
||||
}
|
||||
// Don't spawn monsters if this actor has been massacred
|
||||
if (DamageType == 'Massacre' && GetDefaultByType(missile).bIsMonster)
|
||||
{
|
||||
return true, null;
|
||||
}
|
||||
|
||||
Vector2 pos;
|
||||
|
||||
if (!(flags & SXF_ABSOLUTEANGLE))
|
||||
{
|
||||
angle += self.Angle;
|
||||
}
|
||||
double s = sin(angle);
|
||||
double c = cos(angle);
|
||||
|
||||
if (flags & SXF_ABSOLUTEPOSITION)
|
||||
{
|
||||
pos = 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 = Vec2Offset(xofs * c + yofs * s, xofs * s - yofs*c);
|
||||
}
|
||||
|
||||
if (!(flags & SXF_ABSOLUTEVELOCITY))
|
||||
{
|
||||
// Same orientation issue here!
|
||||
double newxvel = xvel * c + yvel * s;
|
||||
yvel = xvel * s - yvel * c;
|
||||
xvel = newxvel;
|
||||
}
|
||||
|
||||
let mo = Spawn(missile, (pos, self.pos.Z - Floorclip + GetBobOffset() + zofs), ALLOW_REPLACE);
|
||||
bool res = InitSpawnedItem(mo, flags);
|
||||
if (res)
|
||||
{
|
||||
if (tid != 0)
|
||||
{
|
||||
mo.ChangeTid(tid);
|
||||
}
|
||||
mo.Vel = (xvel, yvel, zvel);
|
||||
if (flags & SXF_MULTIPLYSPEED)
|
||||
{
|
||||
mo.Vel *= mo.Speed;
|
||||
}
|
||||
mo.Angle = angle;
|
||||
}
|
||||
return res, mo;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// A_ThrowGrenade
|
||||
//
|
||||
// Throws a grenade (like Hexen's fighter flechette)
|
||||
//
|
||||
//===========================================================================
|
||||
action bool, Actor A_ThrowGrenade(class<Actor> missile, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true)
|
||||
{
|
||||
if (missile == NULL)
|
||||
{
|
||||
return false, null;
|
||||
}
|
||||
if (stateinfo != null && stateinfo.mStateType == STATE_Psprite)
|
||||
{
|
||||
let player = self.player;
|
||||
if (player == null) return false, null;
|
||||
let weapon = player.ReadyWeapon;
|
||||
// Used from a weapon, so use some ammo
|
||||
|
||||
if (weapon == NULL || (useammo && !weapon.DepleteAmmo(weapon.bAltFire)))
|
||||
{
|
||||
return true, null;
|
||||
}
|
||||
}
|
||||
|
||||
let bo = Spawn(missile, pos + (0, 0, (-Floorclip + GetBobOffset() + zheight + 35 + (player? player.crouchoffset : 0.))), ALLOW_REPLACE);
|
||||
if (bo)
|
||||
{
|
||||
bo.PlaySpawnSound(self);
|
||||
if (xyvel != 0)
|
||||
bo.Speed = xyvel;
|
||||
bo.Angle = Angle + (((random[grenade]()&7) - 4) * (360./256.));
|
||||
|
||||
let pitch = -self.Pitch;
|
||||
let angle = bo.Angle;
|
||||
|
||||
// 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 * cos(pitch);
|
||||
double xy_velz = bo.Speed * sin(pitch);
|
||||
double xy_velx = xy_xyscale * cos(angle);
|
||||
double xy_vely = xy_xyscale * sin(angle);
|
||||
|
||||
pitch = self.Pitch;
|
||||
double z_xyscale = zvel * sin(pitch);
|
||||
double z_velz = zvel * cos(pitch);
|
||||
double z_velx = z_xyscale * cos(angle);
|
||||
double z_vely = z_xyscale * sin(angle);
|
||||
|
||||
bo.Vel.X = xy_velx + z_velx + Vel.X / 2;
|
||||
bo.Vel.Y = xy_vely + z_vely + Vel.Y / 2;
|
||||
bo.Vel.Z = xy_velz + z_velz;
|
||||
|
||||
bo.target = self;
|
||||
if (!bo.CheckMissileSpawn(radius)) bo = null;
|
||||
return true, bo;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false, null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue