mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-30 07:41:22 +00:00
7385cd70c0
Interesting tidbit: The damage calculation in P_MinotaurSlam had been incorrect for the Heretic version since the friendly Hexen Dark Servant was added, but nobody ever noticed in 14 years...
759 lines
15 KiB
Text
759 lines
15 KiB
Text
class Minotaur : Actor
|
|
{
|
|
const MAULATORTICS = 25 * TICRATE;
|
|
const MNTR_CHARGE_SPEED =13.;
|
|
const MINOTAUR_LOOK_DIST = 16*54.;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// 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()
|
|
{
|
|
if (!target)
|
|
{
|
|
return;
|
|
}
|
|
A_PlaySound ("minotaur/melee", CHAN_WEAPON);
|
|
if (CheckMeleeRange())
|
|
{
|
|
int damage = random[MinotaurAtk1](1, 8) * 4;
|
|
int newdam = target.DamageMobj (self, self, damage, 'Melee');
|
|
target.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
PlayerInfo player = target.player;
|
|
if (player != null && player.mo == target)
|
|
{ // 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;
|
|
|
|
//if (gameinfo.gametype == GAME_Heretic)
|
|
if (gametype() == GAME_Heretic)
|
|
{
|
|
type = "PhoenixPuff";
|
|
}
|
|
else
|
|
{
|
|
type = "PunchPuff";
|
|
}
|
|
Actor puff = Spawn (type, Pos, ALLOW_REPLACE);
|
|
puff.Vel.Z = 2;
|
|
special1--;
|
|
}
|
|
else
|
|
{
|
|
bSkullFly = false;
|
|
bInvulnerable = false;
|
|
SetState (SeeState);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// PROC A_MinotaurAtk2
|
|
//
|
|
// Swing attack.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void A_MinotaurAtk2()
|
|
{
|
|
bool friendly = bSummonedMonster;
|
|
|
|
if (target == null)
|
|
{
|
|
return;
|
|
}
|
|
A_PlaySound ("minotaur/attack2", CHAN_WEAPON);
|
|
if (CheckMeleeRange())
|
|
{
|
|
int damage = random[MinotaurAtk2](1, 8) * (friendly ? 3 : 5);
|
|
int newdam = target.DamageMobj (self, self, damage, 'Melee');
|
|
target.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
return;
|
|
}
|
|
double z = pos.z + 40;
|
|
Class<Actor> fx = "MinotaurFX1";
|
|
Actor mo = SpawnMissileZ (z, target, fx);
|
|
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;
|
|
|
|
if (!target)
|
|
{
|
|
return;
|
|
}
|
|
A_PlaySound ("minotaur/attack3", CHAN_VOICE);
|
|
if (CheckMeleeRange())
|
|
{
|
|
int damage = random[MinotaurAtk3](1, 8) * (friendly ? 3 : 5);
|
|
int newdam = target.DamageMobj (self, self, damage, 'Melee');
|
|
target.TraceBleed (newdam > 0 ? newdam : damage, self);
|
|
PlayerInfo player = target.player;
|
|
if (player != null && player.mo == target)
|
|
{ // 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);
|
|
|
|
MinotaurFriend mf = MinotaurFriend(self);
|
|
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()
|
|
{
|
|
MinotaurFriend mf = MinotaurFriend(self);
|
|
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 ();
|
|
}
|
|
}
|
|
}
|
|
|
|
class MinotaurFriend : Minotaur
|
|
{
|
|
int StartTime;
|
|
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
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 ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// Minotaur FX 1 ------------------------------------------------------------
|
|
|
|
class MinotaurFX1 : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 10;
|
|
Height 6;
|
|
Speed 20;
|
|
FastSpeed 26;
|
|
Damage 3;
|
|
DamageType "Fire";
|
|
Projectile;
|
|
-ACTIVATEIMPACT
|
|
-ACTIVATEPCROSS
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
mo.target = target;
|
|
mo.Vel.X = MinVel; // Force block checking
|
|
mo.CheckMissileSpawn (radius);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|