qzdoom-gpl/wadsrc/static/zscript/hexen/flechette.txt
Christoph Oelckers 3d61d2c1f4 - reviewd script code for spawn calls that did not check their results.
Nothing should ever assume that spawning an actor is unconditionally successful. There can always be some edge cases where this is not the case.
2016-12-31 15:40:51 +01:00

602 lines
12 KiB
Text

// Poison Bag (Flechette used by Cleric) ------------------------------------
class PoisonBag : Actor
{
Default
{
Radius 5;
Height 5;
+NOBLOCKMAP +NOGRAVITY
}
States
{
Spawn:
PSBG A 18 Bright;
PSBG B 4 Bright;
PSBG C 3;
PSBG C 1 A_PoisonBagInit;
Stop;
}
//===========================================================================
//
// A_PoisonBagInit
//
//===========================================================================
void A_PoisonBagInit()
{
Actor mo = Spawn("PoisonCloud", pos + (0, 0, 28), ALLOW_REPLACE);
if (mo)
{
mo.target = target;
}
}
}
// Fire Bomb (Flechette used by Mage) ---------------------------------------
class FireBomb : Actor
{
Default
{
DamageType "Fire";
+NOGRAVITY
+FOILINVUL
RenderStyle "Translucent";
Alpha 0.6;
DeathSound "FlechetteExplode";
}
States
{
Spawn:
PSBG A 20;
PSBG AA 10;
PSBG B 4;
PSBG C 4 A_Scream;
XPL1 A 4 Bright A_TimeBomb;
XPL1 BCDEF 4 Bright;
Stop;
}
void A_TimeBomb()
{
AddZ(32, false);
A_SetRenderStyle(1., STYLE_Add);
A_Explode();
}
}
// Throwing Bomb (Flechette used by Fighter) --------------------------------
class ThrowingBomb : Actor
{
Default
{
Health 48;
Speed 12;
Radius 8;
Height 10;
DamageType "Fire";
+NOBLOCKMAP +DROPOFF +MISSILE
BounceType "HexenCompat";
SeeSound "FlechetteBounce";
DeathSound "FlechetteExplode";
}
States
{
Spawn:
THRW A 4 A_CheckThrowBomb;
THRW BCDE 3 A_CheckThrowBomb;
THRW F 3 A_CheckThrowBomb2;
Loop;
Tail:
THRW G 6 A_CheckThrowBomb;
THRW F 4 A_CheckThrowBomb;
THRW H 6 A_CheckThrowBomb;
THRW F 4 A_CheckThrowBomb;
THRW G 6 A_CheckThrowBomb;
THRW F 3 A_CheckThrowBomb;
Wait;
Death:
CFCF Q 4 Bright A_NoGravity;
CFCF R 3 Bright A_Scream;
CFCF S 4 Bright A_Explode;
CFCF T 3 Bright;
CFCF U 4 Bright;
CFCF W 3 Bright;
CFCF X 4 Bright;
CFCF Z 3 Bright;
Stop;
}
//===========================================================================
//
// A_CheckThrowBomb
//
//===========================================================================
void A_CheckThrowBomb()
{
if (--health <= 0)
{
SetStateLabel("Death");
}
}
//===========================================================================
//
// A_CheckThrowBomb2
//
//===========================================================================
void A_CheckThrowBomb2()
{
// [RH] Check using actual velocity, although the vel.z < 2 check still stands
if (Vel.Z < 2 && Vel.Length() < 1.5)
{
SetStateLabel("Tail");
SetZ(floorz);
Vel.Z = 0;
ClearBounce();
bMissile = false;
}
A_CheckThrowBomb();
}
}
// Poison Bag Artifact (Flechette) ------------------------------------------
class ArtiPoisonBag : Inventory
{
Default
{
+FLOATBOB
Inventory.DefMaxAmount;
Inventory.PickupFlash "PickupFlash";
+INVENTORY.INVBAR +INVENTORY.FANCYPICKUPSOUND
Inventory.Icon "ARTIPSBG";
Inventory.PickupSound "misc/p_pkup";
Inventory.PickupMessage "$TXT_ARTIPOISONBAG";
Tag "$TAG_ARTIPOISONBAG";
}
States
{
Spawn:
PSBG A -1;
Stop;
}
//============================================================================
//
// AArtiPoisonBag :: BeginPlay
//
//============================================================================
override void BeginPlay ()
{
Super.BeginPlay ();
// If a subclass's specific icon is not defined, let it use the base class's.
if (!Icon.isValid())
{
Icon = GetDefaultByType("ArtiPoisonBag").Icon;
}
}
//============================================================================
//
// GetFlechetteType
//
//============================================================================
private class<Actor> GetFlechetteType(Actor other)
{
class<Actor> spawntype = null;
PlayerPawn pp = PlayerPawn(other);
if (pp)
{
spawntype = pp.FlechetteType;
}
if (spawntype == null)
{
// default fallback if nothing valid defined.
spawntype = "ArtiPoisonBag3";
}
return spawntype;
}
//============================================================================
//
// AArtiPoisonBag :: HandlePickup
//
//============================================================================
override bool HandlePickup (Inventory item)
{
// Only do special handling when picking up the base class
if (item.GetClass() != "ArtiPoisonBag")
{
return Super.HandlePickup (item);
}
if (GetClass() == GetFlechetteType(Owner))
{
if (Amount < MaxAmount || sv_unlimited_pickup)
{
Amount += item.Amount;
if (Amount > MaxAmount && !sv_unlimited_pickup)
{
Amount = MaxAmount;
}
item.bPickupGood = true;
}
return true;
}
return false;
}
//============================================================================
//
// AArtiPoisonBag :: CreateCopy
//
//============================================================================
override Inventory CreateCopy (Actor other)
{
// Only the base class gets special handling
if (GetClass() != "ArtiPoisonBag")
{
return Super.CreateCopy (other);
}
class<Actor> spawntype = GetFlechetteType(other);
let copy = Inventory(Spawn (spawntype));
if (copy != null)
{
copy.Amount = Amount;
copy.MaxAmount = MaxAmount;
GoAwayAndDie ();
}
return copy;
}
}
// Poison Bag 1 (The Cleric's) ----------------------------------------------
class ArtiPoisonBag1 : ArtiPoisonBag
{
Default
{
Inventory.Icon "ARTIPSB1";
Tag "$TAG_ARTIPOISONBAG1";
}
override bool Use (bool pickup)
{
Actor mo = Spawn("PoisonBag", Owner.Vec3Offset(
16 * cos(Owner.angle),
24 * sin(Owner.angle),
-Owner.Floorclip + 8), ALLOW_REPLACE);
if (mo)
{
mo.target = Owner;
return true;
}
return false;
}
}
// Poison Bag 2 (The Mage's) ------------------------------------------------
class ArtiPoisonBag2 : ArtiPoisonBag
{
Default
{
Inventory.Icon "ARTIPSB2";
Tag "$TAG_ARTIPOISONBAG2";
}
override bool Use (bool pickup)
{
Actor mo = Spawn("FireBomb", Owner.Vec3Offset(
16 * cos(Owner.angle),
24 * sin(Owner.angle),
-Owner.Floorclip + 8), ALLOW_REPLACE);
if (mo)
{
mo.target = Owner;
return true;
}
return false;
}
}
// Poison Bag 3 (The Fighter's) ---------------------------------------------
class ArtiPoisonBag3 : ArtiPoisonBag
{
Default
{
Inventory.Icon "ARTIPSB3";
Tag "$TAG_ARTIPOISONBAG3";
}
override bool Use (bool pickup)
{
Actor mo = Spawn("ThrowingBomb", Owner.Pos + (0,0,35. - Owner.Floorclip + (Owner.player? Owner.player.crouchoffset : 0)), ALLOW_REPLACE);
if (mo)
{
mo.angle = Owner.angle + (((random[PoisonBag]() & 7) - 4) * (360./256.));
/* Original flight code from Hexen
* mo.momz = 4*F.RACUNIT+((player.lookdir)<<(F.RACBITS-4));
* mo.z += player.lookdir<<(F.RACBITS-4);
* P_ThrustMobj(mo, mo.ang, mo.info.speed);
* mo.momx += player.mo.momx>>1;
* mo.momy += player.mo.momy>>1;
*/
// When looking straight ahead, it uses a z velocity of 4 while the xy velocity
// is as set by the projectile. To accommodate self with a proper trajectory, we
// aim the projectile ~20 degrees higher than we're looking at and increase the
// speed we fire at accordingly.
double orgpitch = -Owner.Pitch;
double modpitch = clamp(-Owner.Pitch + 20, -89., 89.);
double ang = mo.angle;
double speed = (mo.Speed, 4.).Length();
double xyscale = speed * cos(modpitch);
mo.Vel.Z = speed * sin(modpitch);
mo.Vel.X = xyscale * cos(ang) + Owner.Vel.X / 2;
mo.Vel.Y = xyscale * sin(ang) + Owner.Vel.Y / 2;
mo.AddZ(mo.Speed * sin(modpitch));
mo.target = Owner;
mo.tics -= random[PoisonBag]()&3;
mo.CheckMissileSpawn(Owner.radius);
return true;
}
return false;
}
}
// Poison Bag 4 (Custom Giver) ----------------------------------------------
class ArtiPoisonBagGiver : ArtiPoisonBag
{
Default
{
Inventory.Icon "ARTIPSB4";
}
override bool Use (bool pickup)
{
Class<Actor> missiletype = MissileName;
if (missiletype != null)
{
Actor mo = Spawn (missiletype, Owner.Pos, ALLOW_REPLACE);
if (mo != null)
{
Inventory inv = Inventory(mo);
if (inv && inv.CallTryPickup(Owner)) return true;
mo.Destroy(); // Destroy if not inventory or couldn't be picked up
}
}
return false;
}
}
// Poison Bag 5 (Custom Thrower) --------------------------------------------
class ArtiPoisonBagShooter : ArtiPoisonBag
{
Default
{
Inventory.Icon "ARTIPSB5";
}
override bool Use (bool pickup)
{
Class<Actor> missiletype = MissileName;
if (missiletype != null)
{
Actor mo = Owner.SpawnPlayerMissile(missiletype);
if (mo != null)
{
// automatic handling of seeker missiles
if (mo.bSeekerMissile)
{
mo.tracer = Owner.target;
}
return true;
}
}
return false;
}
}
// Poison Cloud -------------------------------------------------------------
class PoisonCloud : Actor
{
Default
{
Radius 20;
Height 30;
Mass 0x7fffffff;
+NOBLOCKMAP +NOGRAVITY +DROPOFF
+NODAMAGETHRUST
+DONTSPLASH +FOILINVUL +CANBLAST +BLOODLESSIMPACT +BLOCKEDBYSOLIDACTORS
RenderStyle "Translucent";
Alpha 0.6;
DeathSound "PoisonShroomDeath";
DamageType "PoisonCloud";
}
States
{
Spawn:
PSBG D 1;
PSBG D 1 A_Scream;
PSBG DEEEFFFGGGHHHII 2 A_PoisonBagDamage;
PSBG I 2 A_PoisonBagCheck;
PSBG I 1 A_PoisonBagCheck;
Goto Spawn + 3;
Death:
PSBG HG 7;
PSBG FD 6;
Stop;
}
//===========================================================================
//
//
//
//===========================================================================
override void BeginPlay ()
{
Vel.X = MinVel; // missile objects must move to impact other objects
special1 = 24+(random[PoisonCloud]() & 7);
special2 = 0;
}
//===========================================================================
//
//
//
//===========================================================================
override int DoSpecialDamage (Actor victim, int damage, Name damagetype)
{
if (victim.player)
{
bool mate = (target != null && victim.player != target.player && victim.IsTeammate (target));
bool dopoison;
if (!mate)
{
dopoison = victim.player.poisoncount < 4;
}
else
{
dopoison = victim.player.poisoncount < (int)(4. * level.teamdamage);
}
if (dopoison)
{
damage = 15 + (random[PoisonCloud]() & 15);
if (mate)
{
damage = (int)(damage * level.teamdamage);
}
// Handle passive damage modifiers (e.g. PowerProtection)
damage = victim.GetModifiedDamage(damagetype, damage, true);
// Modify with damage factors
damage = victim.ApplyDamageFactor(damagetype, damage);
if (damage > 0)
{
victim.player.PoisonDamage (self, 15 + (random[PoisonCloud]() & 15), false); // Don't play painsound
// If successful, play the poison sound.
if (victim.player.PoisonPlayer (self, self.target, 50))
victim.A_PlaySound ("*poison", CHAN_VOICE);
}
}
return -1;
}
else if (!victim.bIsMonster)
{ // only damage monsters/players with the poison cloud
return -1;
}
return damage;
}
//===========================================================================
//
// A_PoisonBagCheck
//
//===========================================================================
void A_PoisonBagCheck()
{
if (--special1 <= 0)
{
SetStateLabel("Death");
}
}
//===========================================================================
//
// A_PoisonBagDamage
//
//===========================================================================
void A_PoisonBagDamage()
{
A_Explode(4, 40);
AddZ(BobSin(special2) / 16);
special2 = (special2 + 1) & 63;
}
}
// Poison Shroom ------------------------------------------------------------
class ZPoisonShroom : PoisonBag
{
Default
{
Radius 6;
Height 20;
PainChance 255;
Health 30;
Mass 0x7fffffff;
+SHOOTABLE
+SOLID
+NOBLOOD
+NOICEDEATH
-NOBLOCKMAP
-NOGRAVITY
PainSound "PoisonShroomPain";
DeathSound "PoisonShroomDeath";
}
States
{
Spawn:
SHRM A 5 A_PoisonShroom;
Goto Pain+1;
Pain:
SHRM A 6;
SHRM B 8 A_Pain;
Goto Spawn;
Death:
SHRM CD 5;
SHRM E 5 A_PoisonBagInit;
SHRM F -1;
Stop;
}
//===========================================================================
//
// A_PoisonShroom
//
//===========================================================================
void A_PoisonShroom()
{
tics = 128 + (random[PoisonShroom]() << 1);
}
}