2016-11-26 00:14:47 +00:00
|
|
|
class Minotaur : Actor
|
2016-10-16 22:21:52 +00:00
|
|
|
{
|
2016-11-26 00:14:47 +00:00
|
|
|
const MAULATORTICS = 25 * TICRATE;
|
|
|
|
const MNTR_CHARGE_SPEED =13.;
|
|
|
|
const MINOTAUR_LOOK_DIST = 16*54.;
|
|
|
|
|
2016-10-16 22:21:52 +00:00
|
|
|
Default
|
|
|
|
{
|
|
|
|
Health 3000;
|
|
|
|
Radius 28;
|
|
|
|
Height 100;
|
|
|
|
Mass 800;
|
|
|
|
Speed 16;
|
|
|
|
Damage 7;
|
|
|
|
Painchance 25;
|
|
|
|
Monster;
|
|
|
|
+DROPOFF
|
|
|
|
+FLOORCLIP
|
|
|
|
+BOSS
|
|
|
|
+NORADIUSDMG
|
|
|
|
+DONTMORPH
|
|
|
|
+NOTARGET
|
|
|
|
+BOSSDEATH
|
|
|
|
SeeSound "minotaur/sight";
|
|
|
|
AttackSound "minotaur/attack1";
|
|
|
|
PainSound "minotaur/pain";
|
|
|
|
DeathSound "minotaur/death";
|
|
|
|
ActiveSound "minotaur/active";
|
|
|
|
DropItem "ArtiSuperHealth", 51;
|
|
|
|
DropItem "PhoenixRodAmmo", 84, 10;
|
2017-06-19 08:12:57 +00:00
|
|
|
Obituary "$OB_MINOTAUR";
|
|
|
|
HitObituary "$OB_MINOTAURHIT";
|
2016-10-16 22:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
States
|
|
|
|
{
|
|
|
|
Spawn:
|
|
|
|
MNTR AB 10 A_MinotaurLook;
|
|
|
|
Loop;
|
|
|
|
Roam:
|
|
|
|
MNTR ABCD 5 A_MinotaurRoam;
|
|
|
|
Loop;
|
|
|
|
See:
|
|
|
|
MNTR ABCD 5 A_MinotaurChase;
|
|
|
|
Loop;
|
|
|
|
Melee:
|
|
|
|
MNTR V 10 A_FaceTarget;
|
|
|
|
MNTR W 7 A_FaceTarget;
|
|
|
|
MNTR X 12 A_MinotaurAtk1;
|
|
|
|
Goto See;
|
|
|
|
Missile:
|
|
|
|
MNTR V 10 A_MinotaurDecide;
|
|
|
|
MNTR Y 4 A_FaceTarget;
|
|
|
|
MNTR Z 9 A_MinotaurAtk2;
|
|
|
|
Goto See;
|
|
|
|
Hammer:
|
|
|
|
MNTR V 10 A_FaceTarget;
|
|
|
|
MNTR W 7 A_FaceTarget;
|
|
|
|
MNTR X 12 A_MinotaurAtk3;
|
|
|
|
Goto See;
|
|
|
|
HammerLoop:
|
|
|
|
MNTR X 12;
|
|
|
|
Goto Hammer;
|
|
|
|
Charge:
|
|
|
|
MNTR U 2 A_MinotaurCharge;
|
|
|
|
Loop;
|
|
|
|
Pain:
|
|
|
|
MNTR E 3;
|
|
|
|
MNTR E 6 A_Pain;
|
|
|
|
Goto See;
|
|
|
|
Death:
|
|
|
|
MNTR F 6 A_MinotaurDeath;
|
|
|
|
MNTR G 5;
|
|
|
|
MNTR H 6 A_Scream;
|
|
|
|
MNTR I 5;
|
|
|
|
MNTR J 6;
|
|
|
|
MNTR K 5;
|
|
|
|
MNTR L 6;
|
|
|
|
MNTR M 5 A_NoBlocking;
|
|
|
|
MNTR N 6;
|
|
|
|
MNTR O 5;
|
|
|
|
MNTR P 6;
|
|
|
|
MNTR Q 5;
|
|
|
|
MNTR R 6;
|
|
|
|
MNTR S 5;
|
|
|
|
MNTR T -1 A_BossDeath;
|
|
|
|
Stop;
|
|
|
|
FadeOut:
|
|
|
|
MNTR E 6;
|
|
|
|
MNTR E 2 A_Scream;
|
|
|
|
MNTR E 5 A_SpawnItemEx("MinotaurSmokeExit");
|
|
|
|
MNTR E 5;
|
|
|
|
MNTR E 5 A_NoBlocking;
|
|
|
|
MNTR E 5;
|
|
|
|
MNTR E 5 A_SetTranslucent(0.66, 0);
|
|
|
|
MNTR E 5 A_SetTranslucent(0.33, 0);
|
|
|
|
MNTR E 10 A_BossDeath;
|
|
|
|
Stop;
|
|
|
|
}
|
2016-11-26 00:14:47 +00:00
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// FUNC P_MinotaurSlam
|
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void MinotaurSlam (Actor target)
|
|
|
|
{
|
|
|
|
double ang = AngleTo(target);
|
|
|
|
double thrust = 16 + random[MinotaurSlam]() / 64.;
|
|
|
|
target.VelFromAngle(ang, thrust);
|
|
|
|
int damage = random[MinotaurSlam](1, 8) * (bSummonedMonster? 4 : 6);
|
|
|
|
int newdam = target.DamageMobj (null, null, damage, 'Melee');
|
|
|
|
target.TraceBleedAngle (newdam > 0 ? newdam : damage, ang, 0.);
|
|
|
|
if (target.player)
|
|
|
|
{
|
|
|
|
target.reactiontime = random[MinotaurSlam](14, 21);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
override void Tick ()
|
|
|
|
{
|
|
|
|
Super.Tick ();
|
|
|
|
|
|
|
|
// The unfriendly Minotaur (Heretic's) is invulnerable while charging
|
|
|
|
if (!bSummonedMonster)
|
|
|
|
{
|
|
|
|
bInvulnerable = bSkullFly;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override bool Slam (Actor thing)
|
|
|
|
{
|
|
|
|
// Slamming minotaurs shouldn't move non-creatures
|
|
|
|
if (!thing.bIsMonster && !thing.player)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return Super.Slam (thing);
|
|
|
|
}
|
|
|
|
|
|
|
|
override int DoSpecialDamage (Actor target, int damage, Name damagetype)
|
|
|
|
{
|
|
|
|
damage = Super.DoSpecialDamage (target, damage, damagetype);
|
|
|
|
if (damage != -1 && bSkullFly)
|
|
|
|
{ // Slam only when in charge mode
|
|
|
|
MinotaurSlam (target);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return damage;
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurAtk1
|
|
|
|
//
|
|
|
|
// Melee attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurAtk1()
|
|
|
|
{
|
2017-06-06 07:12:23 +00:00
|
|
|
let targ = target;
|
|
|
|
if (!targ)
|
2016-11-26 00:14:47 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
A_PlaySound ("minotaur/melee", CHAN_WEAPON);
|
|
|
|
if (CheckMeleeRange())
|
|
|
|
{
|
2017-06-06 07:12:23 +00:00
|
|
|
PlayerInfo player = targ.player;
|
2016-11-26 00:14:47 +00:00
|
|
|
int damage = random[MinotaurAtk1](1, 8) * 4;
|
2017-06-06 07:12:23 +00:00
|
|
|
int newdam = targ.DamageMobj (self, self, damage, 'Melee');
|
|
|
|
targ.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
|
|
if (player != null && player.mo == targ)
|
2016-11-26 00:14:47 +00:00
|
|
|
{ // Squish the player
|
|
|
|
player.deltaviewheight = -16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurDecide
|
|
|
|
//
|
|
|
|
// Choose a missile attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurDecide()
|
|
|
|
{
|
|
|
|
bool friendly = bSummonedMonster;
|
|
|
|
|
|
|
|
if (!target)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!friendly)
|
|
|
|
{
|
|
|
|
A_PlaySound ("minotaur/sight", CHAN_WEAPON);
|
|
|
|
}
|
|
|
|
double dist = Distance2D(target);
|
|
|
|
if (target.pos.z + target.height > pos.z
|
|
|
|
&& target.pos.z + target.height < pos.z + height
|
|
|
|
&& dist < (friendly ? 16*64. : 8*64.)
|
|
|
|
&& dist > 1*64.
|
|
|
|
&& random[MinotaurDecide]() < 150)
|
|
|
|
{ // Charge attack
|
|
|
|
// Don't call the state function right away
|
|
|
|
SetStateLabel("Charge", true);
|
|
|
|
bSkullFly = true;
|
|
|
|
if (!friendly)
|
|
|
|
{ // Heretic's Minotaur is invulnerable during charge attack
|
|
|
|
bInvulnerable = true;
|
|
|
|
}
|
|
|
|
A_FaceTarget ();
|
|
|
|
VelFromAngle(MNTR_CHARGE_SPEED);
|
|
|
|
special1 = TICRATE/2; // Charge duration
|
|
|
|
}
|
|
|
|
else if (target.pos.z == target.floorz
|
|
|
|
&& dist < 9*64.
|
|
|
|
&& random[MinotaurDecide]() < (friendly ? 100 : 220))
|
|
|
|
{ // Floor fire attack
|
|
|
|
SetStateLabel("Hammer");
|
|
|
|
special2 = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Swing attack
|
|
|
|
A_FaceTarget ();
|
|
|
|
// Don't need to call P_SetMobjState because the current state
|
|
|
|
// falls through to the swing attack
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurCharge
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurCharge()
|
|
|
|
{
|
|
|
|
if (target == null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (special1 > 0)
|
|
|
|
{
|
|
|
|
Class<Actor> type;
|
|
|
|
|
2017-01-19 16:40:34 +00:00
|
|
|
if (gameinfo.gametype == GAME_Heretic)
|
2016-11-26 00:14:47 +00:00
|
|
|
{
|
|
|
|
type = "PhoenixPuff";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
type = "PunchPuff";
|
|
|
|
}
|
|
|
|
Actor puff = Spawn (type, Pos, ALLOW_REPLACE);
|
2016-12-31 14:40:16 +00:00
|
|
|
if (puff != null) puff.Vel.Z = 2;
|
2016-11-26 00:14:47 +00:00
|
|
|
special1--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bSkullFly = false;
|
|
|
|
bInvulnerable = false;
|
|
|
|
SetState (SeeState);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurAtk2
|
|
|
|
//
|
|
|
|
// Swing attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurAtk2()
|
|
|
|
{
|
|
|
|
bool friendly = bSummonedMonster;
|
|
|
|
|
2017-06-06 07:12:23 +00:00
|
|
|
let targ = target;
|
|
|
|
if (targ == null)
|
2016-11-26 00:14:47 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
A_PlaySound ("minotaur/attack2", CHAN_WEAPON);
|
|
|
|
if (CheckMeleeRange())
|
|
|
|
{
|
|
|
|
int damage = random[MinotaurAtk2](1, 8) * (friendly ? 3 : 5);
|
2017-06-06 07:12:23 +00:00
|
|
|
int newdam = targ.DamageMobj (self, self, damage, 'Melee');
|
|
|
|
targ.TraceBleed (newdam > 0 ? newdam : damage, self);
|
2016-11-26 00:14:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
double z = pos.z + 40;
|
|
|
|
Class<Actor> fx = "MinotaurFX1";
|
2017-06-06 07:12:23 +00:00
|
|
|
Actor mo = SpawnMissileZ (z, targ, fx);
|
2016-11-26 00:14:47 +00:00
|
|
|
if (mo != null)
|
|
|
|
{
|
|
|
|
// S_Sound (mo, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM);
|
|
|
|
double vz = mo.Vel.Z;
|
|
|
|
double ang = mo.angle;
|
|
|
|
SpawnMissileAngleZ (z, fx, ang-(45./8), vz);
|
|
|
|
SpawnMissileAngleZ (z, fx, ang+(45./8), vz);
|
|
|
|
SpawnMissileAngleZ (z, fx, ang-(45./16), vz);
|
|
|
|
SpawnMissileAngleZ (z, fx, ang+(45./16), vz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurAtk3
|
|
|
|
//
|
|
|
|
// Floor fire attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurAtk3()
|
|
|
|
{
|
|
|
|
bool friendly = bSummonedMonster;
|
|
|
|
|
2017-06-06 07:12:23 +00:00
|
|
|
let targ = target;
|
|
|
|
if (!targ)
|
2016-11-26 00:14:47 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
A_PlaySound ("minotaur/attack3", CHAN_VOICE);
|
|
|
|
if (CheckMeleeRange())
|
|
|
|
{
|
2017-06-06 07:12:23 +00:00
|
|
|
PlayerInfo player = targ.player;
|
2016-11-26 00:14:47 +00:00
|
|
|
int damage = random[MinotaurAtk3](1, 8) * (friendly ? 3 : 5);
|
2017-06-06 07:12:23 +00:00
|
|
|
int newdam = targ.DamageMobj (self, self, damage, 'Melee');
|
|
|
|
targ.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
|
|
if (player != null && player.mo == targ)
|
2016-11-26 00:14:47 +00:00
|
|
|
{ // Squish the player
|
|
|
|
player.deltaviewheight = -16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Floorclip > 0 && compat_minotaur)
|
|
|
|
{
|
|
|
|
// only play the sound.
|
|
|
|
A_PlaySound ("minotaur/fx2hit", CHAN_WEAPON);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Actor mo = SpawnMissile (target, "MinotaurFX2");
|
|
|
|
if (mo != null)
|
|
|
|
{
|
|
|
|
mo.A_PlaySound ("minotaur/attack1", CHAN_WEAPON);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (random[MinotaurAtk3]() < 192 && special2 == 0)
|
|
|
|
{
|
|
|
|
SetStateLabel ("HammerLoop");
|
|
|
|
special2 = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurDeath
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurDeath()
|
|
|
|
{
|
|
|
|
if (Wads.CheckNumForName ("MNTRF1", Wads.ns_sprites) < 0 &&
|
|
|
|
Wads.CheckNumForName ("MNTRF0", Wads.ns_sprites) < 0)
|
|
|
|
SetStateLabel("FadeOut");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// A_MinotaurRoam
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurRoam()
|
|
|
|
{
|
|
|
|
// In case pain caused him to skip his fade in.
|
|
|
|
A_SetRenderStyle(1, STYLE_Normal);
|
|
|
|
|
2016-12-05 13:52:34 +00:00
|
|
|
let mf = MinotaurFriend(self);
|
2016-11-26 00:14:47 +00:00
|
|
|
if (mf)
|
|
|
|
{
|
|
|
|
if (mf.StartTime >= 0 && (level.maptime - mf.StartTime) >= MAULATORTICS)
|
|
|
|
{
|
|
|
|
DamageMobj (null, null, TELEFRAG_DAMAGE, 'None');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (random[MinotaurRoam]() < 30)
|
|
|
|
A_MinotaurLook(); // adjust to closest target
|
|
|
|
|
|
|
|
if (random[MinotaurRoam]() < 6)
|
|
|
|
{
|
|
|
|
//Choose new direction
|
|
|
|
movedir = random[MinotaurRoam]() % 8;
|
|
|
|
FaceMovementDirection ();
|
|
|
|
}
|
|
|
|
if (!MonsterMove())
|
|
|
|
{
|
|
|
|
// Turn
|
|
|
|
if (random[MinotaurRoam]() & 1)
|
|
|
|
movedir = (movedir + 1) % 8;
|
|
|
|
else
|
|
|
|
movedir = (movedir + 7) % 8;
|
|
|
|
FaceMovementDirection ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurLook
|
|
|
|
//
|
|
|
|
// Look for enemy of player
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurLook()
|
|
|
|
{
|
|
|
|
if (!(self is "MinotaurFriend"))
|
|
|
|
{
|
|
|
|
A_Look();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Actor mo = null;
|
|
|
|
PlayerInfo player;
|
|
|
|
double dist;
|
|
|
|
Actor master = tracer;
|
|
|
|
|
|
|
|
target = null;
|
|
|
|
if (deathmatch) // Quick search for players
|
|
|
|
{
|
|
|
|
for (int i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (!playeringame[i]) continue;
|
|
|
|
player = players[i];
|
|
|
|
mo = player.mo;
|
|
|
|
if (mo == master) continue;
|
|
|
|
if (mo.health <= 0) continue;
|
|
|
|
dist = Distance2D(mo);
|
|
|
|
if (dist > MINOTAUR_LOOK_DIST) continue;
|
|
|
|
target = mo;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!target) // Near player monster search
|
|
|
|
{
|
|
|
|
if (master && (master.health > 0) && (master.player))
|
|
|
|
mo = master.RoughMonsterSearch(20);
|
|
|
|
else
|
|
|
|
mo = RoughMonsterSearch(20);
|
|
|
|
target = mo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!target) // Normal monster search
|
|
|
|
{
|
|
|
|
ThinkerIterator it = ThinkerIterator.Create("Actor");
|
|
|
|
|
|
|
|
while ((mo = Actor(it.Next())) != null)
|
|
|
|
{
|
|
|
|
if (!mo.bIsMonster) continue;
|
|
|
|
if (mo.health <= 0) continue;
|
|
|
|
if (!mo.bShootable) continue;
|
|
|
|
dist = Distance2D(mo);
|
|
|
|
if (dist > MINOTAUR_LOOK_DIST) continue;
|
|
|
|
if (mo == master || mo == self) continue;
|
|
|
|
if (mo.bSummonedMonster && mo.tracer == master) continue;
|
|
|
|
target = mo;
|
|
|
|
break; // Found actor to attack
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target)
|
|
|
|
{
|
|
|
|
SetState (SeeState, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetStateLabel ("Roam", true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurChase
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MinotaurChase()
|
|
|
|
{
|
2016-12-05 13:52:34 +00:00
|
|
|
let mf = MinotaurFriend(self);
|
2016-11-26 00:14:47 +00:00
|
|
|
if (!mf)
|
|
|
|
{
|
|
|
|
A_Chase();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// In case pain caused him to skip his fade in.
|
|
|
|
A_SetRenderStyle(1, STYLE_Normal);
|
|
|
|
|
|
|
|
if (mf.StartTime >= 0 && (level.maptime - mf.StartTime) >= MAULATORTICS)
|
|
|
|
{
|
|
|
|
DamageMobj (null, null, TELEFRAG_DAMAGE, 'None');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (random[MinotaurChase]() < 30)
|
|
|
|
A_MinotaurLook(); // adjust to closest target
|
|
|
|
|
|
|
|
if (!target || (target.health <= 0) || !target.bShootable)
|
|
|
|
{ // look for a new target
|
|
|
|
SetIdle();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FaceMovementDirection ();
|
|
|
|
reactiontime = 0;
|
|
|
|
|
|
|
|
// Melee attack
|
|
|
|
if (MeleeState && CheckMeleeRange ())
|
|
|
|
{
|
|
|
|
if (AttackSound)
|
|
|
|
{
|
|
|
|
A_PlaySound (AttackSound, CHAN_WEAPON);
|
|
|
|
}
|
|
|
|
SetState (MeleeState);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Missile attack
|
|
|
|
if (MissileState && CheckMissileRange())
|
|
|
|
{
|
|
|
|
SetState (MissileState);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// chase towards target
|
|
|
|
if (!MonsterMove ())
|
|
|
|
{
|
|
|
|
NewChaseDir ();
|
|
|
|
FaceMovementDirection ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Active sound
|
|
|
|
if (random[MinotaurChase]() < 6)
|
|
|
|
{
|
|
|
|
PlayActiveSound ();
|
|
|
|
}
|
|
|
|
}
|
2016-10-16 22:21:52 +00:00
|
|
|
}
|
|
|
|
|
2016-11-26 00:14:47 +00:00
|
|
|
class MinotaurFriend : Minotaur
|
2016-10-16 22:21:52 +00:00
|
|
|
{
|
2016-11-26 00:14:47 +00:00
|
|
|
int StartTime;
|
|
|
|
|
2016-10-16 22:21:52 +00:00
|
|
|
Default
|
|
|
|
{
|
|
|
|
Health 2500;
|
|
|
|
-DROPOFF
|
|
|
|
-BOSS
|
|
|
|
-DONTMORPH
|
|
|
|
+FRIENDLY
|
|
|
|
+NOTARGETSWITCH
|
|
|
|
+STAYMORPHED
|
|
|
|
+TELESTOMP
|
|
|
|
+SUMMONEDMONSTER
|
|
|
|
RenderStyle "Translucent";
|
|
|
|
Alpha 0.3333;
|
|
|
|
DropItem "None";
|
|
|
|
}
|
|
|
|
States
|
|
|
|
{
|
|
|
|
Spawn:
|
|
|
|
MNTR A 15;
|
|
|
|
MNTR A 15 A_SetTranslucent(0.66, 0);
|
|
|
|
MNTR A 3 A_SetTranslucent(1, 0);
|
|
|
|
Goto Super::Spawn;
|
|
|
|
Idle:
|
|
|
|
Goto Super::Spawn;
|
|
|
|
Death:
|
|
|
|
Goto FadeOut;
|
|
|
|
}
|
2016-11-26 00:14:47 +00:00
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
override void BeginPlay ()
|
|
|
|
{
|
|
|
|
Super.BeginPlay ();
|
|
|
|
StartTime = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
override void Die (Actor source, Actor inflictor, int dmgflags)
|
|
|
|
{
|
|
|
|
Super.Die (source, inflictor, dmgflags);
|
|
|
|
|
|
|
|
if (tracer && tracer.health > 0 && tracer.player)
|
|
|
|
{
|
|
|
|
// Search thinker list for minotaur
|
|
|
|
ThinkerIterator it = ThinkerIterator.Create("MinotaurFriend");
|
|
|
|
MinotaurFriend mo;
|
|
|
|
|
|
|
|
while ((mo = MinotaurFriend(it.Next())) != null)
|
|
|
|
{
|
|
|
|
if (mo.health <= 0) continue;
|
|
|
|
// [RH] Minotaurs can't be morphed, so this isn't needed
|
|
|
|
//if (!(mo.flags&MF_COUNTKILL)) continue; // for morphed minotaurs
|
|
|
|
if (mo.bCorpse) continue;
|
|
|
|
if (mo.StartTime >= 0 && (level.maptime - StartTime) >= MAULATORTICS) continue;
|
|
|
|
if (mo.tracer != null && mo.tracer.player == tracer.player) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mo == null)
|
|
|
|
{
|
|
|
|
Inventory power = tracer.FindInventory("PowerMinotaur");
|
|
|
|
if (power != null)
|
|
|
|
{
|
|
|
|
power.Destroy ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-16 22:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Minotaur FX 1 ------------------------------------------------------------
|
|
|
|
|
|
|
|
class MinotaurFX1 : Actor
|
|
|
|
{
|
|
|
|
Default
|
|
|
|
{
|
|
|
|
Radius 10;
|
|
|
|
Height 6;
|
|
|
|
Speed 20;
|
|
|
|
FastSpeed 26;
|
|
|
|
Damage 3;
|
|
|
|
DamageType "Fire";
|
|
|
|
Projectile;
|
|
|
|
-ACTIVATEIMPACT
|
|
|
|
-ACTIVATEPCROSS
|
2017-06-01 18:03:51 +00:00
|
|
|
+ZDOOMTRANS
|
2016-10-16 22:21:52 +00:00
|
|
|
RenderStyle "Add";
|
|
|
|
}
|
|
|
|
States
|
|
|
|
{
|
|
|
|
Spawn:
|
|
|
|
FX12 AB 6 Bright;
|
|
|
|
Loop;
|
|
|
|
Death:
|
|
|
|
FX12 CDEFGH 5 Bright;
|
|
|
|
Stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Minotaur FX 2 ------------------------------------------------------------
|
|
|
|
|
|
|
|
class MinotaurFX2 : MinotaurFX1
|
|
|
|
{
|
|
|
|
Default
|
|
|
|
{
|
|
|
|
Radius 5;
|
|
|
|
Height 12;
|
|
|
|
Speed 14;
|
|
|
|
FastSpeed 20;
|
|
|
|
Damage 4;
|
|
|
|
+FLOORHUGGER
|
|
|
|
ExplosionDamage 24;
|
|
|
|
DeathSound "minotaur/fx2hit";
|
|
|
|
}
|
|
|
|
|
|
|
|
states
|
|
|
|
{
|
|
|
|
Spawn:
|
|
|
|
FX13 A 2 Bright A_MntrFloorFire;
|
|
|
|
Loop;
|
|
|
|
Death:
|
|
|
|
FX13 I 4 Bright A_Explode;
|
|
|
|
FX13 JKLM 4 Bright;
|
|
|
|
Stop;
|
|
|
|
}
|
2016-11-26 00:14:47 +00:00
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MntrFloorFire
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void A_MntrFloorFire()
|
|
|
|
{
|
|
|
|
SetZ(floorz);
|
|
|
|
double x = Random2[MntrFloorFire]() / 64.;
|
|
|
|
double y = Random2[MntrFloorFire]() / 64.;
|
|
|
|
|
|
|
|
Actor mo = Spawn("MinotaurFX3", Vec2OffsetZ(x, y, floorz), ALLOW_REPLACE);
|
2016-12-31 14:40:16 +00:00
|
|
|
if (mo != null)
|
|
|
|
{
|
|
|
|
mo.target = target;
|
|
|
|
mo.Vel.X = MinVel; // Force block checking
|
|
|
|
mo.CheckMissileSpawn (radius);
|
|
|
|
}
|
2016-11-26 00:14:47 +00:00
|
|
|
}
|
2016-10-16 22:21:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Minotaur FX 3 ------------------------------------------------------------
|
|
|
|
|
|
|
|
class MinotaurFX3 : MinotaurFX2
|
|
|
|
{
|
|
|
|
Default
|
|
|
|
{
|
|
|
|
Radius 8;
|
|
|
|
Height 16;
|
|
|
|
Speed 0;
|
|
|
|
DeathSound "minotaur/fx3hit";
|
|
|
|
ExplosionDamage 128;
|
|
|
|
}
|
|
|
|
States
|
|
|
|
{
|
|
|
|
Spawn:
|
|
|
|
FX13 DC 4 Bright;
|
|
|
|
FX13 BCDE 5 Bright;
|
|
|
|
FX13 FGH 4 Bright;
|
|
|
|
Stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Minotaur Smoke Exit ------------------------------------------------------
|
|
|
|
|
|
|
|
class MinotaurSmokeExit : Actor
|
|
|
|
{
|
|
|
|
Default
|
|
|
|
{
|
|
|
|
+NOBLOCKMAP
|
|
|
|
+NOTELEPORT
|
|
|
|
RenderStyle "Translucent";
|
|
|
|
Alpha 0.4;
|
|
|
|
}
|
|
|
|
States
|
|
|
|
{
|
|
|
|
Spawn:
|
|
|
|
MNSM ABCDEFGHIJIHGFEDCBA 3;
|
|
|
|
Stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-29 18:50:34 +00:00
|
|
|
extend class Actor
|
|
|
|
{
|
|
|
|
enum dirtype_t
|
|
|
|
{
|
|
|
|
DI_EAST,
|
|
|
|
DI_NORTHEAST,
|
|
|
|
DI_NORTH,
|
|
|
|
DI_NORTHWEST,
|
|
|
|
DI_WEST,
|
|
|
|
DI_SOUTHWEST,
|
|
|
|
DI_SOUTH,
|
|
|
|
DI_SOUTHEAST,
|
|
|
|
DI_NODIR,
|
|
|
|
NUMDIRS
|
|
|
|
};
|
|
|
|
|
|
|
|
void FaceMovementDirection()
|
|
|
|
{
|
|
|
|
switch (movedir)
|
|
|
|
{
|
|
|
|
case DI_EAST:
|
|
|
|
angle = 0.;
|
|
|
|
break;
|
|
|
|
case DI_NORTHEAST:
|
|
|
|
angle = 45.;
|
|
|
|
break;
|
|
|
|
case DI_NORTH:
|
|
|
|
angle = 90.;
|
|
|
|
break;
|
|
|
|
case DI_NORTHWEST:
|
|
|
|
angle = 135.;
|
|
|
|
break;
|
|
|
|
case DI_WEST:
|
|
|
|
angle = 180.;
|
|
|
|
break;
|
|
|
|
case DI_SOUTHWEST:
|
|
|
|
angle = 225.;
|
|
|
|
break;
|
|
|
|
case DI_SOUTH:
|
|
|
|
angle = 270.;
|
|
|
|
break;
|
|
|
|
case DI_SOUTHEAST:
|
|
|
|
angle = 315.;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|