mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-24 21:01:35 +00:00
673 lines
13 KiB
Text
673 lines
13 KiB
Text
|
|
// Cleric Weapon Piece ------------------------------------------------------
|
|
|
|
class ClericWeaponPiece : WeaponPiece
|
|
{
|
|
Default
|
|
{
|
|
Inventory.PickupSound "misc/w_pkup";
|
|
Inventory.PickupMessage "$TXT_WRAITHVERGE_PIECE";
|
|
Inventory.ForbiddenTo "FighterPlayer", "MagePlayer";
|
|
WeaponPiece.Weapon "CWeapWraithverge";
|
|
+FLOATBOB
|
|
}
|
|
}
|
|
|
|
// Cleric Weapon Piece 1 ----------------------------------------------------
|
|
|
|
class CWeaponPiece1 : ClericWeaponPiece
|
|
{
|
|
Default
|
|
{
|
|
WeaponPiece.Number 1;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
WCH1 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Cleric Weapon Piece 2 ----------------------------------------------------
|
|
|
|
class CWeaponPiece2 : ClericWeaponPiece
|
|
{
|
|
Default
|
|
{
|
|
WeaponPiece.Number 2;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
WCH2 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Cleric Weapon Piece 3 ----------------------------------------------------
|
|
|
|
class CWeaponPiece3 : ClericWeaponPiece
|
|
{
|
|
Default
|
|
{
|
|
WeaponPiece.Number 3;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
WCH3 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Wraithverge Drop ---------------------------------------------------------
|
|
|
|
class WraithvergeDrop : Actor
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1;
|
|
TNT1 A 1 A_DropWeaponPieces("CWeaponPiece1", "CWeaponPiece2", "CWeaponPiece3");
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Cleric's Wraithverge (Holy Symbol?) --------------------------------------
|
|
|
|
class CWeapWraithverge : ClericWeapon
|
|
{
|
|
int CHolyCount;
|
|
|
|
Default
|
|
{
|
|
Health 3;
|
|
Weapon.SelectionOrder 3000;
|
|
+WEAPON.PRIMARY_USES_BOTH;
|
|
+Inventory.NoAttenPickupSound
|
|
Weapon.AmmoUse1 18;
|
|
Weapon.AmmoUse2 18;
|
|
Weapon.AmmoGive1 20;
|
|
Weapon.AmmoGive2 20;
|
|
Weapon.KickBack 150;
|
|
Weapon.AmmoType1 "Mana1";
|
|
Weapon.AmmoType2 "Mana2";
|
|
Inventory.PickupMessage "$TXT_WEAPON_C4";
|
|
Tag "$TAG_CWEAPWRAITHVERGE";
|
|
Inventory.PickupSound "WeaponBuild";
|
|
}
|
|
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A -1;
|
|
Stop;
|
|
Ready:
|
|
CHLY A 1 A_WeaponReady;
|
|
Loop;
|
|
Select:
|
|
CHLY A 1 A_Raise;
|
|
Loop;
|
|
Deselect:
|
|
CHLY A 1 A_Lower;
|
|
Loop;
|
|
Fire:
|
|
CHLY AB 1 Bright Offset (0, 40);
|
|
CHLY CD 2 Bright Offset (0, 43);
|
|
CHLY E 2 Bright Offset (0, 45);
|
|
CHLY F 6 Bright Offset (0, 48) A_CHolyAttack;
|
|
CHLY GG 2 Bright Offset (0, 40) A_CHolyPalette;
|
|
CHLY G 2 Offset (0, 36) A_CHolyPalette;
|
|
Goto Ready;
|
|
}
|
|
|
|
override color GetBlend ()
|
|
{
|
|
if (paletteflash & PF_HEXENWEAPONS)
|
|
{
|
|
if (CHolyCount == 3)
|
|
return Color(128, 70, 70, 70);
|
|
else if (CHolyCount == 2)
|
|
return Color(128, 100, 100, 100);
|
|
else if (CHolyCount == 1)
|
|
return Color(128, 130, 130, 130);
|
|
else
|
|
return Color(0, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
return Color(CHolyCount * 128 / 3, 131, 131, 131);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyAttack
|
|
//
|
|
//============================================================================
|
|
|
|
action void A_CHolyAttack()
|
|
{
|
|
FTranslatedLineTarget t;
|
|
|
|
if (player == null)
|
|
{
|
|
return;
|
|
}
|
|
Weapon weapon = player.ReadyWeapon;
|
|
if (weapon != null)
|
|
{
|
|
if (!weapon.DepleteAmmo (weapon.bAltFire))
|
|
return;
|
|
}
|
|
Actor missile = SpawnPlayerMissile ("HolyMissile", angle, pLineTarget:t);
|
|
if (missile != null && !t.unlinked)
|
|
{
|
|
missile.tracer = t.linetarget;
|
|
}
|
|
|
|
invoker.CHolyCount = 3;
|
|
A_PlaySound ("HolySymbolFire", CHAN_WEAPON);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyPalette
|
|
//
|
|
//============================================================================
|
|
|
|
action void A_CHolyPalette()
|
|
{
|
|
if (invoker.CHolyCount > 0) invoker.CHolyCount--;
|
|
}
|
|
}
|
|
|
|
// Holy Missile -------------------------------------------------------------
|
|
|
|
class HolyMissile : Actor
|
|
{
|
|
Default
|
|
{
|
|
Speed 30;
|
|
Radius 15;
|
|
Height 8;
|
|
Damage 4;
|
|
Projectile;
|
|
-ACTIVATEIMPACT -ACTIVATEPCROSS
|
|
+EXTREMEDEATH
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
SPIR PPPP 3 Bright A_SpawnItemEx("HolyMissilePuff");
|
|
Death:
|
|
SPIR P 1 Bright A_CHolyAttack2;
|
|
Stop;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyAttack2
|
|
//
|
|
// Spawns the spirits
|
|
//============================================================================
|
|
|
|
void A_CHolyAttack2()
|
|
{
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
Actor mo = Spawn("HolySpirit", Pos, ALLOW_REPLACE);
|
|
if (!mo)
|
|
{
|
|
continue;
|
|
}
|
|
switch (j)
|
|
{ // float bob index
|
|
|
|
case 0:
|
|
mo.WeaveIndexZ = random[HolyAtk2]() & 7; // upper-left
|
|
break;
|
|
case 1:
|
|
mo.WeaveIndexZ = 32 + (random[HolyAtk2]() & 7); // upper-right
|
|
break;
|
|
case 2:
|
|
mo.WeaveIndexXY = 32 + (random[HolyAtk2]() & 7); // lower-left
|
|
break;
|
|
case 3:
|
|
mo.WeaveIndexXY = 32 + (random[HolyAtk2]() & 7);
|
|
mo.WeaveIndexZ = 32 + (random[HolyAtk2]() & 7);
|
|
break;
|
|
}
|
|
mo.SetZ(pos.z);
|
|
mo.angle = angle + 67.5 - 45.*j;
|
|
mo.Thrust();
|
|
mo.target = target;
|
|
mo.args[0] = 10; // initial turn value
|
|
mo.args[1] = 0; // initial look angle
|
|
if (deathmatch)
|
|
{ // Ghosts last slightly less longer in DeathMatch
|
|
mo.health = 85;
|
|
}
|
|
if (tracer)
|
|
{
|
|
mo.tracer = tracer;
|
|
mo.bNoClip = true;
|
|
mo.bSkullFly = true;
|
|
mo.bMissile = false;
|
|
}
|
|
HolyTail.SpawnSpiritTail (mo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Holy Missile Puff --------------------------------------------------------
|
|
|
|
class HolyMissilePuff : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 4;
|
|
Height 8;
|
|
+NOBLOCKMAP +NOGRAVITY +DROPOFF
|
|
+NOTELEPORT
|
|
RenderStyle "Translucent";
|
|
Alpha 0.4;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SPIR QRSTU 3;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Holy Puff ----------------------------------------------------------------
|
|
|
|
class HolyPuff : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOBLOCKMAP +NOGRAVITY
|
|
RenderStyle "Translucent";
|
|
Alpha 0.6;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SPIR KLMNO 3;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Holy Spirit --------------------------------------------------------------
|
|
|
|
class HolySpirit : Actor
|
|
{
|
|
Default
|
|
{
|
|
Health 105;
|
|
Speed 12;
|
|
Radius 10;
|
|
Height 6;
|
|
Damage 3;
|
|
Projectile;
|
|
+RIPPER +SEEKERMISSILE
|
|
+FOILINVUL +SKYEXPLODE +NOEXPLODEFLOOR +CANBLAST
|
|
+EXTREMEDEATH +NOSHIELDREFLECT
|
|
RenderStyle "Translucent";
|
|
Alpha 0.4;
|
|
DeathSound "SpiritDie";
|
|
Obituary "$OB_MPCWEAPWRAITHVERGE";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
SPIR AAB 2 A_CHolySeek;
|
|
SPIR B 2 A_CHolyCheckScream;
|
|
Loop;
|
|
Death:
|
|
SPIR D 4;
|
|
SPIR E 4 A_Scream;
|
|
SPIR FGHI 4;
|
|
Stop;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
//
|
|
//
|
|
//============================================================================
|
|
|
|
override bool Slam(Actor thing)
|
|
{
|
|
if (thing.bShootable && thing != target)
|
|
{
|
|
if (multiplayer && !deathmatch && thing.player && target.player)
|
|
{ // don't attack other co-op players
|
|
return true;
|
|
}
|
|
if (thing.bReflective && (thing.player || thing.bBoss))
|
|
{
|
|
tracer = target;
|
|
target = thing;
|
|
return true;
|
|
}
|
|
if (thing.bIsMonster || thing.player)
|
|
{
|
|
tracer = thing;
|
|
}
|
|
if (random[SpiritSlam]() < 96)
|
|
{
|
|
int dam = 12;
|
|
if (thing.player || thing.bBoss)
|
|
{
|
|
dam = 3;
|
|
// ghost burns out faster when attacking players/bosses
|
|
health -= 6;
|
|
}
|
|
thing.DamageMobj(self, target, dam, 'Melee');
|
|
if (random[SpiritSlam]() < 128)
|
|
{
|
|
Spawn("HolyPuff", Pos, ALLOW_REPLACE);
|
|
A_PlaySound("SpiritAttack", CHAN_WEAPON);
|
|
if (thing.bIsMonster && random[SpiritSlam]() < 128)
|
|
{
|
|
thing.Howl();
|
|
}
|
|
}
|
|
}
|
|
if (thing.health <= 0)
|
|
{
|
|
tracer = null;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
override bool SpecialBlastHandling (Actor source, double strength)
|
|
{
|
|
if (tracer == source)
|
|
{
|
|
tracer = target;
|
|
target = source;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolyFindTarget
|
|
//
|
|
//============================================================================
|
|
|
|
private void CHolyFindTarget ()
|
|
{
|
|
Actor target;
|
|
|
|
if ( (target = RoughMonsterSearch (6, true)) )
|
|
{
|
|
tracer = target;
|
|
bNoClip = true;
|
|
bSkullFly = true;
|
|
bMissile = false;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolySeekerMissile
|
|
//
|
|
// Similar to P_SeekerMissile, but seeks to a random Z on the target
|
|
//============================================================================
|
|
|
|
private void CHolySeekerMissile (double thresh, double turnMax)
|
|
{
|
|
Actor target = tracer;
|
|
if (target == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (!target.bShootable || (!target.bIsMonster && !target.player))
|
|
{ // Target died/target isn't a player or creature
|
|
tracer = null;
|
|
bNoClip = false;
|
|
bSkullFly = false;
|
|
bMissile = true;
|
|
CHolyFindTarget();
|
|
return;
|
|
}
|
|
double ang = deltaangle(angle, AngleTo(target));
|
|
double delta = abs(ang);
|
|
|
|
if (delta > thresh)
|
|
{
|
|
delta /= 2;
|
|
if (delta > turnMax)
|
|
{
|
|
delta = turnMax;
|
|
}
|
|
}
|
|
if (ang > 0)
|
|
{ // Turn clockwise
|
|
angle += delta;
|
|
}
|
|
else
|
|
{ // Turn counter clockwise
|
|
angle -= delta;
|
|
}
|
|
VelFromAngle();
|
|
|
|
if (!(level.time&15)
|
|
|| pos.z > target.pos.z + target.height
|
|
|| pos.z + height < target.pos.z)
|
|
{
|
|
double newZ = target.pos.z + ((random[HolySeeker]()*target.Height) / 256.);
|
|
double deltaZ = newZ - pos.z;
|
|
if (abs(deltaZ) > 15)
|
|
{
|
|
if (deltaZ > 0)
|
|
{
|
|
deltaZ = 15;
|
|
}
|
|
else
|
|
{
|
|
deltaZ = -15;
|
|
}
|
|
}
|
|
Vel.Z = deltaZ / DistanceBySpeed(target, Speed);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolySeek
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolySeek()
|
|
{
|
|
health--;
|
|
if (health <= 0)
|
|
{
|
|
Vel.X /= 4;
|
|
Vel.Y /= 4;
|
|
Vel.Z = 0;
|
|
SetStateLabel ("Death");
|
|
tics -= random[HolySeeker]()&3;
|
|
return;
|
|
}
|
|
if (tracer)
|
|
{
|
|
CHolySeekerMissile (args[0], args[0]*2.);
|
|
if (!((level.time+7)&15))
|
|
{
|
|
args[0] = 5+(random[HolySeeker]()/20);
|
|
}
|
|
}
|
|
|
|
int xyspeed = (random[HolySeeker]() % 5);
|
|
int zspeed = (random[HolySeeker]() % 5);
|
|
A_Weave(xyspeed, zspeed, 4., 2.);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyCheckScream
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolyCheckScream()
|
|
{
|
|
A_CHolySeek();
|
|
if (random[HolyScream]() < 20)
|
|
{
|
|
A_PlaySound ("SpiritActive", CHAN_VOICE);
|
|
}
|
|
if (!tracer)
|
|
{
|
|
CHolyFindTarget();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Holy Tail ----------------------------------------------------------------
|
|
|
|
class HolyTail : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 1;
|
|
Height 1;
|
|
+NOBLOCKMAP +NOGRAVITY +DROPOFF +NOCLIP
|
|
+NOTELEPORT
|
|
RenderStyle "Translucent";
|
|
Alpha 0.6;
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
SPIR C 1 A_CHolyTail;
|
|
Loop;
|
|
TailTrail:
|
|
SPIR D -1;
|
|
Stop;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// SpawnSpiritTail
|
|
//
|
|
//============================================================================
|
|
|
|
static void SpawnSpiritTail (Actor spirit)
|
|
{
|
|
Actor tail = Spawn ("HolyTail", spirit.Pos, ALLOW_REPLACE);
|
|
tail.target = spirit; // parent
|
|
for (int i = 1; i < 3; i++)
|
|
{
|
|
Actor next = Spawn ("HolyTailTrail", spirit.Pos, ALLOW_REPLACE);
|
|
tail.tracer = next;
|
|
tail = next;
|
|
}
|
|
tail.tracer = null; // last tail bit
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolyTailFollow
|
|
//
|
|
//============================================================================
|
|
|
|
private void CHolyTailFollow(double dist)
|
|
{
|
|
Actor mo = self;
|
|
while (mo)
|
|
{
|
|
Actor child = mo.tracer;
|
|
if (child)
|
|
{
|
|
double an = mo.AngleTo(child);
|
|
double oldDistance = child.Distance2D(mo);
|
|
if (child.TryMove(mo.Pos.XY + AngleToVector(an, dist), true))
|
|
{
|
|
double newDistance = child.Distance2D(mo) - 1;
|
|
if (oldDistance < 1)
|
|
{
|
|
if (child.pos.z < mo.pos.z)
|
|
{
|
|
child.SetZ(mo.pos.z - dist);
|
|
}
|
|
else
|
|
{
|
|
child.SetZ(mo.pos.z + dist);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
child.SetZ(mo.pos.z + (newDistance * (child.pos.z - mo.pos.z) / oldDistance));
|
|
}
|
|
}
|
|
}
|
|
mo = child;
|
|
dist -= 1;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// CHolyTailRemove
|
|
//
|
|
//============================================================================
|
|
|
|
private void CHolyTailRemove ()
|
|
{
|
|
Actor mo = self;
|
|
while (mo)
|
|
{
|
|
Actor next = mo.tracer;
|
|
mo.Destroy ();
|
|
mo = next;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_CHolyTail
|
|
//
|
|
//============================================================================
|
|
|
|
void A_CHolyTail()
|
|
{
|
|
Actor parent = target;
|
|
|
|
if (parent == null || parent.health <= 0) // better check for health than current state - it's safer!
|
|
{ // Ghost removed, so remove all tail parts
|
|
CHolyTailRemove ();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (TryMove(parent.Vec2Angle(14., parent.Angle, true), true))
|
|
{
|
|
SetZ(parent.pos.z - 5.);
|
|
}
|
|
CHolyTailFollow(10);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Holy Tail Trail ---------------------------------------------------------
|
|
|
|
class HolyTailTrail : HolyTail
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
Goto TailTrail;
|
|
}
|
|
}
|