qzdoom/wadsrc/static/zscript/actors/heretic/weaponmace.zs
Christoph Oelckers a638cfbd6e - fixed: player_t::GetPSprite cannot guarantee success
As a consequence it must return null in the failure case instead of asserting and all calls to this function must check for the failure case.
2020-03-11 00:56:29 +01:00

481 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_StartSound ("weapons/maceshoot", CHAN_BODY);
ball.CheckMissileSpawn (radius);
}
}
else
{
let psp = player.GetPSprite(PSP_WEAPON);
if (psp)
{
psp.x = random[MaceAtk](-2, 1);
psp.y = WEAPONTOP + random[MaceAtk](0, 3);
}
Actor ball = SpawnPlayerMissile("MaceFX1", angle + (random[MaceAtk](-4, 3) * (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_StartSound ("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_StartSound ("weapons/macebounce", CHAN_BODY);
}
else
{ // Explode
Vel = (0,0,0);
bNoGravity = true;
Gravity = 1;
A_StartSound ("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_StartSound ("weapons/macestop", CHAN_BODY);
return;
}
}
Vel = (0,0,0);
bNoGravity = true;
Gravity = 1;
A_StartSound ("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;
}
}