mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-23 04:11:53 +00:00
3d61d2c1f4
Nothing should ever assume that spawning an actor is unconditionally successful. There can always be some edge cases where this is not the case.
477 lines
8.9 KiB
Text
477 lines
8.9 KiB
Text
// The mace itself ----------------------------------------------------------
|
|
|
|
class Mace : HereticWeapon
|
|
{
|
|
Default
|
|
{
|
|
Weapon.SelectionOrder 1400;
|
|
Weapon.AmmoUse 1;
|
|
Weapon.AmmoGive1 50;
|
|
Weapon.YAdjust 15;
|
|
Weapon.AmmoType "MaceAmmo";
|
|
Weapon.SisterWeapon "MacePowered";
|
|
Inventory.PickupMessage "$TXT_WPNMACE";
|
|
Tag "$TAG_MACE";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
WMCE A -1;
|
|
Stop;
|
|
Ready:
|
|
MACE A 1 A_WeaponReady;
|
|
Loop;
|
|
Deselect:
|
|
MACE A 1 A_Lower;
|
|
Loop;
|
|
Select:
|
|
MACE A 1 A_Raise;
|
|
Loop;
|
|
Fire:
|
|
MACE B 4;
|
|
Hold:
|
|
MACE CDEF 3 A_FireMacePL1;
|
|
MACE C 4 A_ReFire;
|
|
MACE DEFB 4;
|
|
Goto Ready;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL1
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
action void A_FireMacePL1()
|
|
{
|
|
if (player == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Weapon weapon = player.ReadyWeapon;
|
|
if (weapon != null)
|
|
{
|
|
if (!weapon.DepleteAmmo (weapon.bAltFire))
|
|
return;
|
|
}
|
|
|
|
if (random[MaceAtk]() < 28)
|
|
{
|
|
Actor ball = Spawn("MaceFX2", Pos + (0, 0, 28 - Floorclip), ALLOW_REPLACE);
|
|
if (ball != null)
|
|
{
|
|
ball.Vel.Z = 2 - clamp(tan(pitch), -5, 5);
|
|
ball.target = self;
|
|
ball.angle = self.angle;
|
|
ball.AddZ(ball.Vel.Z);
|
|
ball.VelFromAngle();
|
|
ball.Vel += Vel.xy / 2;
|
|
ball.A_PlaySound ("weapons/maceshoot", CHAN_BODY);
|
|
ball.CheckMissileSpawn (radius);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
player.GetPSprite(PSP_WEAPON).x = ((random[MaceAtk]() & 3) - 2);
|
|
player.GetPSprite(PSP_WEAPON).y = WEAPONTOP + (random[MaceAtk]() & 3);
|
|
Actor ball = SpawnPlayerMissile("MaceFX1", angle + (((random[MaceAtk]() & 7) - 4) * (360. / 256)));
|
|
if (ball)
|
|
{
|
|
ball.special1 = 16; // tics till dropoff
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class MacePowered : Mace
|
|
{
|
|
Default
|
|
{
|
|
+WEAPON.POWERED_UP
|
|
Weapon.AmmoUse 5;
|
|
Weapon.AmmoGive 0;
|
|
Weapon.SisterWeapon "Mace";
|
|
Tag "$TAG_MACEP";
|
|
}
|
|
|
|
States
|
|
{
|
|
Fire:
|
|
Hold:
|
|
MACE B 4;
|
|
MACE D 4 A_FireMacePL2;
|
|
MACE B 4;
|
|
MACE A 8 A_ReFire;
|
|
Goto Ready;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_FireMacePL2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
action void A_FireMacePL2()
|
|
{
|
|
FTranslatedLineTarget t;
|
|
|
|
if (player == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Weapon weapon = player.ReadyWeapon;
|
|
if (weapon != null)
|
|
{
|
|
if (!weapon.DepleteAmmo (weapon.bAltFire))
|
|
return;
|
|
}
|
|
Actor mo = SpawnPlayerMissile ("MaceFX4", angle, pLineTarget:t);
|
|
if (mo)
|
|
{
|
|
mo.Vel.xy += Vel.xy;
|
|
mo.Vel.Z = 2 - clamp(tan(pitch), -5, 5);
|
|
if (t.linetarget && !t.unlinked)
|
|
{
|
|
mo.tracer = t.linetarget;
|
|
}
|
|
}
|
|
A_PlaySound ("weapons/maceshoot", CHAN_WEAPON);
|
|
}
|
|
}
|
|
|
|
// Mace FX1 -----------------------------------------------------------------
|
|
|
|
class MaceFX1 : Actor
|
|
{
|
|
const MAGIC_JUNK = 1234;
|
|
|
|
Default
|
|
{
|
|
Radius 8;
|
|
Height 6;
|
|
Speed 20;
|
|
Damage 2;
|
|
Projectile;
|
|
+THRUGHOST
|
|
BounceType "HereticCompat";
|
|
SeeSound "weapons/maceshoot";
|
|
Obituary "$OB_MPMACE";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX02 AB 4 A_MacePL1Check;
|
|
Loop;
|
|
Death:
|
|
FX02 F 4 BRIGHT A_MaceBallImpact;
|
|
FX02 GHIJ 4 BRIGHT;
|
|
Stop;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MacePL1Check
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_MacePL1Check()
|
|
{
|
|
if (special1 == 0) return;
|
|
special1 -= 4;
|
|
if (special1 > 0) return;
|
|
special1 = 0;
|
|
bNoGravity = false;
|
|
Gravity = 1. / 8;
|
|
// [RH] Avoid some precision loss by scaling the velocity directly
|
|
double velscale = 7 / Vel.XY.Length();
|
|
Vel.XY *= velscale;
|
|
Vel.Z *= 0.5;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MaceBallImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_MaceBallImpact()
|
|
{
|
|
if ((health != MAGIC_JUNK) && bInFloat)
|
|
{ // Bounce
|
|
health = MAGIC_JUNK;
|
|
Vel.Z *= 0.75;
|
|
bBounceOnFloors = bBounceOnCeilings = false;
|
|
SetState (SpawnState);
|
|
A_PlaySound ("weapons/macebounce", CHAN_BODY);
|
|
}
|
|
else
|
|
{ // Explode
|
|
Vel = (0,0,0);
|
|
bNoGravity = true;
|
|
Gravity = 1;
|
|
A_PlaySound ("weapons/macehit", CHAN_BODY);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mace FX2 -----------------------------------------------------------------
|
|
|
|
class MaceFX2 : MaceFX1
|
|
{
|
|
Default
|
|
{
|
|
Speed 10;
|
|
Damage 6;
|
|
Gravity 0.125;
|
|
-NOGRAVITY
|
|
SeeSound "";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX02 CD 4;
|
|
Loop;
|
|
Death:
|
|
FX02 F 4 A_MaceBallImpact2;
|
|
goto Super::Death+1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MaceBallImpact2
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_MaceBallImpact2()
|
|
{
|
|
if ((pos.Z <= floorz) && HitFloor ())
|
|
{ // Landed in some sort of liquid
|
|
Destroy ();
|
|
return;
|
|
}
|
|
if (bInFloat)
|
|
{
|
|
if (Vel.Z >= 2)
|
|
{
|
|
// Bounce
|
|
Vel.Z *= 0.75;
|
|
SetState (SpawnState);
|
|
|
|
Actor tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE);
|
|
if (tiny != null)
|
|
{
|
|
tiny.target = target;
|
|
tiny.angle = angle + 90.;
|
|
tiny.VelFromAngle(Vel.Z - 1.);
|
|
tiny.Vel += (Vel.XY * .5, Vel.Z);
|
|
tiny.CheckMissileSpawn (radius);
|
|
}
|
|
|
|
tiny = Spawn("MaceFX3", Pos, ALLOW_REPLACE);
|
|
if (tiny != null)
|
|
{
|
|
tiny.target = target;
|
|
tiny.angle = angle - 90.;
|
|
tiny.VelFromAngle(Vel.Z - 1.);
|
|
tiny.Vel += (Vel.XY * .5, Vel.Z);
|
|
tiny.CheckMissileSpawn (radius);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
Vel = (0,0,0);
|
|
bNoGravity = true;
|
|
bBounceOnFloors = bBounceOnCeilings = false;
|
|
Gravity = 1;
|
|
}
|
|
}
|
|
|
|
// Mace FX3 -----------------------------------------------------------------
|
|
|
|
class MaceFX3 : MaceFX1
|
|
{
|
|
Default
|
|
{
|
|
Speed 7;
|
|
Damage 4;
|
|
-NOGRAVITY;
|
|
Gravity 0.125;
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX02 AB 4;
|
|
Loop;
|
|
}
|
|
}
|
|
|
|
|
|
// Mace FX4 -----------------------------------------------------------------
|
|
|
|
class MaceFX4 : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 8;
|
|
Height 6;
|
|
Speed 7;
|
|
Damage 18;
|
|
Gravity 0.125;
|
|
Projectile;
|
|
-NOGRAVITY
|
|
+TELESTOMP
|
|
+THRUGHOST
|
|
-NOTELEPORT
|
|
BounceType "HereticCompat";
|
|
SeeSound "";
|
|
Obituary "$OB_MPPMACE";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
FX02 E 99;
|
|
Loop;
|
|
Death:
|
|
FX02 C 4 A_DeathBallImpact;
|
|
FX02 GHIJ 4 BRIGHT;
|
|
Stop;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// FUNC P_AutoUseChaosDevice
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
private bool AutoUseChaosDevice (PlayerInfo player)
|
|
{
|
|
Inventory arti = player.mo.FindInventory("ArtiTeleport");
|
|
|
|
if (arti != null)
|
|
{
|
|
player.mo.UseInventory (arti);
|
|
player.health = player.mo.health = (player.health+1)/2;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC DoSpecialDamage
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
override int DoSpecialDamage (Actor target, int damage, Name damagetype)
|
|
{
|
|
if (target.bBoss || target.bDontSquash || target.IsTeammate (self.target))
|
|
{ // Don't allow cheap boss kills and don't instagib teammates
|
|
return damage;
|
|
}
|
|
else if (target.player)
|
|
{ // Player specific checks
|
|
if (target.player.mo.bInvulnerable)
|
|
{ // Can't hurt invulnerable players
|
|
return -1;
|
|
}
|
|
if (AutoUseChaosDevice (target.player))
|
|
{ // Player was saved using chaos device
|
|
return -1;
|
|
}
|
|
}
|
|
return TELEFRAG_DAMAGE; // Something's gonna die
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_DeathBallImpact
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_DeathBallImpact()
|
|
{
|
|
FTranslatedLineTarget t;
|
|
|
|
if ((pos.Z <= floorz) && HitFloor ())
|
|
{ // Landed in some sort of liquid
|
|
Destroy ();
|
|
return;
|
|
}
|
|
if (bInFloat)
|
|
{
|
|
if (Vel.Z >= 2)
|
|
{
|
|
// Bounce
|
|
bool newAngle = false;
|
|
double ang = 0;
|
|
if (tracer)
|
|
{
|
|
if (!tracer.bShootable)
|
|
{ // Target died
|
|
tracer = null;
|
|
}
|
|
else
|
|
{ // Seek
|
|
ang = AngleTo(tracer);
|
|
newAngle = true;
|
|
}
|
|
}
|
|
else
|
|
{ // Find new target
|
|
ang = 0.;
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
AimLineAttack (ang, 640., t, 0., ALF_NOFRIENDS|ALF_PORTALRESTRICT, null, target);
|
|
if (t.linetarget && target != t.linetarget)
|
|
{
|
|
tracer = t.linetarget;
|
|
ang = t.angleFromSource;
|
|
newAngle = true;
|
|
break;
|
|
}
|
|
ang += 22.5;
|
|
}
|
|
}
|
|
if (newAngle)
|
|
{
|
|
angle = ang;
|
|
VelFromAngle();
|
|
}
|
|
SetState (SpawnState);
|
|
A_PlaySound ("weapons/macestop", CHAN_BODY);
|
|
return;
|
|
}
|
|
}
|
|
Vel = (0,0,0);
|
|
bNoGravity = true;
|
|
Gravity = 1;
|
|
A_PlaySound ("weapons/maceexplode", CHAN_BODY);
|
|
}
|
|
}
|
|
|
|
|
|
// Mace spawn spot ----------------------------------------------------------
|
|
|
|
class MaceSpawner : SpecialSpot
|
|
{
|
|
Default
|
|
{
|
|
+NOSECTOR
|
|
+NOBLOCKMAP
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1;
|
|
TNT1 A -1 A_SpawnSingleItem("Mace", 64, 64, 0);
|
|
Stop;
|
|
}
|
|
}
|