mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-11 13:11:48 +00:00
480dd347c9
This time there was one important exported script function: Actor.Spawn. Since this will require a level pointer in the new scheme of things the old version had to be deprecated, because it is static with no argument that allows retrieving the level. However, since this is probably one of the most widely used functions I added a workaround to let it continue to work if used from inside an actor class, which should constitute >95% of all uses. This required a little bit of hackery in the compiler backend to swap out the function if appropriate. Aside from that there were 5 places in the internal ZScript that needed handling, which mostly consisted of making a formerly static internal function non-static.
680 lines
No EOL
13 KiB
Text
680 lines
No EOL
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](0, 7); // upper-left
|
|
break;
|
|
case 1:
|
|
mo.WeaveIndexZ = random[HolyAtk2](32, 39); // upper-right
|
|
break;
|
|
case 2:
|
|
mo.WeaveIndexXY = random[HolyAtk2](32, 39); // lower-left
|
|
break;
|
|
case 3:
|
|
mo.WeaveIndexXY = random[HolyAtk2](32, 39);
|
|
mo.WeaveIndexZ = random[HolyAtk2](32, 39);
|
|
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;
|
|
}
|
|
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](0, 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](0, 4);
|
|
int zspeed = random[HolySeeker](0, 4);
|
|
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;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
extend class Actor
|
|
{
|
|
//============================================================================
|
|
//
|
|
// SpawnSpiritTail
|
|
//
|
|
//============================================================================
|
|
|
|
void SpawnSpiritTail (Actor spirit)
|
|
{
|
|
Actor tail = Spawn ("HolyTail", spirit.Pos, ALLOW_REPLACE);
|
|
if (tail != null)
|
|
{
|
|
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
|
|
}
|
|
}
|
|
|
|
} |