- scriptified hexenspecialdecs.

- made '->' a single token. Although ZScript does not use it, the parser tends to get confused and fatally chokes on leftover arrows so this ensures more robust error handling.
This commit is contained in:
Christoph Oelckers 2016-11-27 20:14:43 +01:00
parent de6969997a
commit 5ce5466e18
11 changed files with 284 additions and 500 deletions

View file

@ -859,9 +859,7 @@ set( NOT_COMPILED_SOURCE_FILES
${OTHER_SYSTEM_SOURCES}
sc_man_scanner.h
sc_man_scanner.re
g_hexen/a_flies.cpp
g_hexen/a_heresiarch.cpp
g_hexen/a_hexenspecialdecs.cpp
g_hexen/a_magecone.cpp
g_hexen/a_magelightning.cpp
g_hexen/a_magestaff.cpp

View file

@ -1,110 +0,0 @@
static FRandom pr_fly("GetOffMeFly");
//===========================================================================
//
// FindCorpse
//
// Finds a corpse to buzz around. We can't use a blockmap check because
// corpses generally aren't linked into the blockmap.
//
//===========================================================================
static AActor *FindCorpse(AActor *fly, sector_t *sec, int recurselimit)
{
AActor *fallback = NULL;
sec->validcount = validcount;
// Search the current sector
for (AActor *check = sec->thinglist; check != NULL; check = check->snext)
{
if (check == fly)
continue;
if (!(check->flags & MF_CORPSE))
continue;
if (!P_CheckSight(fly, check))
continue;
fallback = check;
if (pr_fly(2)) // 50% chance to try to pick a different corpse
continue;
return check;
}
if (--recurselimit <= 0 || (fallback != NULL && pr_fly(2)))
{
return fallback;
}
// Try neighboring sectors
for (int i = 0; i < sec->linecount; ++i)
{
line_t *line = sec->lines[i];
sector_t *sec2 = (line->frontsector == sec) ? line->backsector : line->frontsector;
if (sec2 != NULL && sec2->validcount != validcount)
{
AActor *neighbor = FindCorpse(fly, sec2, recurselimit);
if (neighbor != NULL)
{
return neighbor;
}
}
}
return fallback;
}
DEFINE_ACTION_FUNCTION(AActor, A_FlySearch)
{
// The version from the retail beta is not so great for general use:
// 1. Pick one of the first fifty thinkers at random.
// 2. Starting from that thinker, find one that is an actor, not itself,
// and within sight. Give up after 100 sequential thinkers.
// It's effectively useless if there are more than 150 thinkers on a map.
//
// So search the sectors instead. We can't potentially find something all
// the way on the other side of the map and we can't find invisible corpses,
// but at least we aren't crippled on maps with lots of stuff going on.
PARAM_SELF_PROLOGUE(AActor);
validcount++;
AActor *other = FindCorpse(self, self->Sector, 5);
if (other != NULL)
{
self->target = other;
self->SetState(self->FindState("Buzz"));
}
return 0;
}
DEFINE_ACTION_FUNCTION(AActor, A_FlyBuzz)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *targ = self->target;
if (targ == NULL || !(targ->flags & MF_CORPSE) || pr_fly() < 5)
{
self->SetIdle();
return 0;
}
self->Angles.Yaw = self->AngleTo(targ);
self->args[0]++;
if (!P_TryMove(self, self->Pos().XY() + self->Angles.Yaw.ToVector(6), true))
{
self->SetIdle(true);
return 0;
}
if (self->args[0] & 2)
{
self->Vel.X += (pr_fly() - 128) / 512.;
self->Vel.Y += (pr_fly() - 128) / 512.;
}
int zrand = pr_fly();
if (targ->Z() + 5. < self->Z() && zrand > 150)
{
zrand = -zrand;
}
self->Vel.Z = zrand / 512.;
if (pr_fly() < 40)
{
S_Sound(self, CHAN_VOICE, self->ActiveSound, 0.5f, ATTN_STATIC);
}
return 0;
}

View file

@ -24,9 +24,7 @@
#include "a_pickups.h"
// Include all the Hexen stuff here to reduce compile time
#include "a_flies.cpp"
#include "a_heresiarch.cpp"
#include "a_hexenspecialdecs.cpp"
#include "a_magecone.cpp"
#include "a_magelightning.cpp"
#include "a_magestaff.cpp"

View file

@ -1,365 +0,0 @@
/*
** Decorations that do special things
*/
/*
#include "actor.h"
#include "info.h"
#include "a_action.h"
#include "m_random.h"
#include "s_sound.h"
#include "p_local.h"
#include "p_lnspec.h"
#include "a_hexenglobal.h"
#include "vm.h"
#include "g_level.h"
#include "doomstat.h"
*/
static FRandom pr_pottery ("PotteryExplode");
static FRandom pr_bit ("PotteryChooseBit");
static FRandom pr_drip ("CorpseBloodDrip");
static FRandom pr_foo ("CorpseExplode");
static FRandom pr_leaf ("LeafSpawn");
static FRandom pr_leafthrust ("LeafThrust");
static FRandom pr_leafcheck ("LeafCheck");
static FRandom pr_shroom ("PoisonShroom");
static FRandom pr_soaexplode ("SoAExplode");
// Pottery1 ------------------------------------------------------------------
void A_PotteryExplode (AActor *);
void A_PotteryChooseBit (AActor *);
void A_PotteryCheck (AActor *);
//============================================================================
//
// A_PotteryExplode
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PotteryExplode)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo = NULL;
int i;
for(i = (pr_pottery()&3)+3; i; i--)
{
mo = Spawn ("PotteryBit", self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + (pr_pottery()%5));
mo->Vel.X = pr_pottery.Random2() / 64.;
mo->Vel.Y = pr_pottery.Random2() / 64.;
mo->Vel.Z = ((pr_pottery() & 7) + 5) * 0.75;
}
}
S_Sound (mo, CHAN_BODY, "PotteryExplode", 1, ATTN_NORM);
// Spawn an item?
PClassActor *type = P_GetSpawnableType(self->args[0]);
if (type != NULL)
{
if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS))
|| !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER))
{ // Only spawn monsters if not -nomonsters
Spawn (type, self->Pos(), ALLOW_REPLACE);
}
}
return 0;
}
//============================================================================
//
// A_PotteryChooseBit
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PotteryChooseBit)
{
PARAM_SELF_PROLOGUE(AActor);
self->SetState (self->FindState(NAME_Death) + 1 + 2*(pr_bit()%5));
self->tics = 256+(pr_bit()<<1);
return 0;
}
//============================================================================
//
// A_PotteryCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_PotteryCheck)
{
PARAM_SELF_PROLOGUE(AActor);
int i;
for(i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
AActor *pmo = players[i].mo;
if (P_CheckSight (self, pmo) && (absangle(pmo->AngleTo(self), pmo->Angles.Yaw) <= 45))
{ // Previous state (pottery bit waiting state)
self->SetState (self->state - 1);
return 0;
}
}
}
return 0;
}
// Lynched corpse (no heart) ------------------------------------------------
class AZCorpseLynchedNoHeart : public AActor
{
DECLARE_CLASS (AZCorpseLynchedNoHeart, AActor)
public:
void PostBeginPlay ();
};
IMPLEMENT_CLASS(AZCorpseLynchedNoHeart, false, false)
void AZCorpseLynchedNoHeart::PostBeginPlay ()
{
Super::PostBeginPlay ();
Spawn ("BloodPool", PosAtZ(floorz), ALLOW_REPLACE);
}
//============================================================================
//
// A_CorpseBloodDrip
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CorpseBloodDrip)
{
PARAM_SELF_PROLOGUE(AActor);
if (pr_drip() <= 128)
{
Spawn ("CorpseBloodDrip", self->PosPlusZ(self->Height / 2), ALLOW_REPLACE);
}
return 0;
}
//============================================================================
//
// A_CorpseExplode
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_CorpseExplode)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
for (i = (pr_foo()&3)+3; i; i--)
{
mo = Spawn ("CorpseBit", self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + (pr_foo()%3));
mo->Vel.X = pr_foo.Random2() / 64.;
mo->Vel.Y = pr_foo.Random2() / 64.;
mo->Vel.Z = ((pr_foo() & 7) + 5) * 0.75;
}
}
// Spawn a skull
mo = Spawn ("CorpseBit", self->Pos(), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + 3);
mo->Vel.X = pr_foo.Random2() / 64.;
mo->Vel.Y = pr_foo.Random2() / 64.;
mo->Vel.Z = ((pr_foo() & 7) + 5) * 0.75;
}
S_Sound (self, CHAN_BODY, self->DeathSound, 1, ATTN_IDLE);
self->Destroy ();
return 0;
}
//============================================================================
//
// A_LeafSpawn
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LeafSpawn)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
for (i = (pr_leaf()&3)+1; i; i--)
{
double xo = pr_leaf.Random2() / 4.;
double yo = pr_leaf.Random2() / 4.;
double zo = pr_leaf() / 4.;
mo = Spawn (pr_leaf()&1 ? PClass::FindActor ("Leaf1") : PClass::FindActor ("Leaf2"),
self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->Thrust(pr_leaf() / 128. + 3);
mo->target = self;
mo->special1 = 0;
}
}
return 0;
}
//============================================================================
//
// A_LeafThrust
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LeafThrust)
{
PARAM_SELF_PROLOGUE(AActor);
if (pr_leafthrust() <= 96)
{
self->Vel.Z += pr_leafthrust() / 128. + 1;
}
return 0;
}
//============================================================================
//
// A_LeafCheck
//
//============================================================================
DEFINE_ACTION_FUNCTION(AActor, A_LeafCheck)
{
PARAM_SELF_PROLOGUE(AActor);
self->special1++;
if (self->special1 >= 20)
{
self->SetState (NULL);
return 0;
}
DAngle ang = self->target ? self->target->Angles.Yaw : self->Angles.Yaw;
if (pr_leafcheck() > 64)
{
if (self->Vel.X == 0 && self->Vel.Y == 0)
{
self->Thrust(ang, pr_leafcheck() / 128. + 1);
}
return 0;
}
self->SetState (self->SpawnState + 7);
self->Vel.Z = pr_leafcheck() / 128. + 1;
self->Thrust(ang, pr_leafcheck() / 128. + 2);
self->flags |= MF_MISSILE;
return 0;
}
//===========================================================================
//
// A_SoAExplode - Suit of Armor Explode
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_SoAExplode)
{
PARAM_SELF_PROLOGUE(AActor);
AActor *mo;
int i;
for (i = 0; i < 10; i++)
{
double xo = (pr_soaexplode() - 128) / 16.;
double yo = (pr_soaexplode() - 128) / 16.;
double zo = pr_soaexplode()*self->Height / 256.;
mo = Spawn ("ZArmorChunk", self->Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo->SetState (mo->SpawnState + i);
mo->Vel.X = pr_soaexplode.Random2() / 64.;
mo->Vel.Y = pr_soaexplode.Random2() / 64.;
mo->Vel.Z = (pr_soaexplode() & 7) + 5;
}
}
// Spawn an item?
PClassActor *type = P_GetSpawnableType(self->args[0]);
if (type != NULL)
{
if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS))
|| !(GetDefaultByType (type)->flags3 & MF3_ISMONSTER))
{ // Only spawn monsters if not -nomonsters
Spawn (type, self->Pos(), ALLOW_REPLACE);
}
}
S_Sound (self, CHAN_BODY, self->DeathSound, 1, ATTN_NORM);
self->Destroy ();
return 0;
}
// Bell ---------------------------------------------------------------------
class AZBell : public AActor
{
DECLARE_CLASS (AZBell, AActor)
public:
void Activate (AActor *activator);
};
IMPLEMENT_CLASS(AZBell, false, false)
void AZBell::Activate (AActor *activator)
{
if (health > 0)
{
P_DamageMobj (this, activator, activator, 10, NAME_Melee, DMG_THRUSTLESS); // 'ring' the bell
}
}
//===========================================================================
//
// A_BellReset1
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_BellReset1)
{
PARAM_SELF_PROLOGUE(AActor);
self->flags |= MF_NOGRAVITY;
self->Height *= 4;
if (self->special)
{ // Initiate death action
P_ExecuteSpecial(self->special, NULL, NULL, false, self->args[0],
self->args[1], self->args[2], self->args[3], self->args[4]);
self->special = 0;
}
return 0;
}
//===========================================================================
//
// A_BellReset2
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AActor, A_BellReset2)
{
PARAM_SELF_PROLOGUE(AActor);
self->flags |= MF_SHOOTABLE;
self->flags &= ~MF_CORPSE;
self->flags6 &= ~MF6_KILLED;
self->health = 5;
return 0;
}

View file

@ -1889,6 +1889,7 @@ DEFINE_FIELD_BIT(FLevelLocals, flags2, missilesactivateimpact, LEVEL2_MISSILESAC
DEFINE_FIELD_BIT(FLevelLocals, flags2, monsterfallingdamage, LEVEL2_MONSTERFALLINGDAMAGE)
DEFINE_FIELD_BIT(FLevelLocals, flags2, checkswitchrange, LEVEL2_CHECKSWITCHRANGE)
DEFINE_FIELD_BIT(FLevelLocals, flags2, polygrind, LEVEL2_POLYGRIND)
DEFINE_FIELD_BIT(FLevelLocals, flags2, nomonsters, LEVEL2_NOMONSTERS)
//==========================================================================
//

View file

@ -520,6 +520,13 @@ PClassActor *P_GetSpawnableType(int spawnnum)
return NULL;
}
DEFINE_ACTION_FUNCTION(AActor, GetSpawnableType)
{
PARAM_PROLOGUE;
PARAM_INT(num);
ACTION_RETURN_OBJECT(P_GetSpawnableType(num));
}
struct MapinfoSpawnItem
{
FName classname; // DECORATE is read after MAPINFO so we do not have the actual classes available here yet.

View file

@ -162,13 +162,11 @@ std2:
'private' { RET(TK_Private); }
'dot' { RET(TK_Dot); }
'cross' { RET(TK_Cross); }
'ignores' { RET(TK_Ignores); }
'localized' { RET(TK_Localized); }
'latent' { RET(TK_Latent); }
'singular' { RET(TK_Singular); }
'config' { RET(TK_Config); }
'coerce' { RET(TK_Coerce); }
'iterator' { RET(TK_Iterator); }
'optional' { RET(TK_Optional); }
'export' { RET(TK_Export); }
'virtual' { RET(TK_Virtual); }
@ -240,6 +238,7 @@ std2:
"<>=" { RET(TK_LtGtEq); }
"**" { RET(TK_MulMul); }
"::" { RET(TK_ColonColon); }
"->" { RET(TK_Arrow); }
";" { StateOptions = false; RET(';'); }
"{" { StateOptions = false; RET('{'); }
"}" { RET('}'); }

View file

@ -33,6 +33,7 @@ xx(TK_Neq, "'!='")
xx(TK_ApproxEq, "'~=='")
xx(TK_LtGtEq, "'<>='")
xx(TK_MulMul, "'**'")
xx(TK_Arrow, "'->'")
xx(TK_Action, "'action'")
xx(TK_Break, "'break'")
xx(TK_Case, "'case'")

View file

@ -270,6 +270,7 @@ class Actor : Thinker native
native static double GetDefaultSpeed(class<Actor> type);
native static class<Actor> GetSpawnableType(int spawnnum);
native void RemoveFromHash();
native string GetTag(string defstr = "");
native double GetBobOffset(double frac = 0);

View file

@ -92,6 +92,7 @@ struct LevelLocals native
native bool monsterfallingdamage;
native bool checkswitchrange;
native bool polygrind;
native bool nomonsters;
// level_info_t *info cannot be done yet.
}

View file

@ -95,7 +95,6 @@ class Pottery1 : Actor
+NOICEDEATH
}
native void A_PotteryExplode();
States
{
@ -106,6 +105,40 @@ class Pottery1 : Actor
POT1 A 0 A_PotteryExplode;
Stop;
}
//============================================================================
//
// A_PotteryExplode
//
//============================================================================
void A_PotteryExplode()
{
Actor mo = null;
int i;
for(i = (random[Pottery]()&3)+3; i; i--)
{
mo = Spawn ("PotteryBit", Pos, ALLOW_REPLACE);
if (mo)
{
mo.SetState (mo.SpawnState + (random[Pottery]()%5));
mo.Vel.X = random2[Pottery]() / 64.;
mo.Vel.Y = random2[Pottery]() / 64.;
mo.Vel.Z = ((random[Pottery]() & 7) + 5) * 0.75;
}
}
mo.A_PlaySound ("PotteryExplode", CHAN_BODY);
// Spawn an item?
Class<Actor> type = GetSpawnableType(args[0]);
if (type != null)
{
if (!(level.nomonsters || sv_nomonsters) || !(GetDefaultByType (type).bIsMonster))
{ // Only spawn monsters if not -nomonsters
Spawn (type, Pos, ALLOW_REPLACE);
}
}
}
}
// Pottery2 -----------------------------------------------------------------
@ -144,6 +177,8 @@ class Pottery3 : Pottery1
class PotteryBit : Actor
{
State LoopState;
Default
{
Radius 5;
@ -153,9 +188,6 @@ class PotteryBit : Actor
+NOICEDEATH
}
native void A_PotteryChooseBit();
native void A_PotteryCheck();
States
{
Spawn:
@ -185,6 +217,42 @@ class PotteryBit : Actor
PBIT J 1 A_PotteryCheck;
Stop;
}
//============================================================================
//
// A_PotteryChooseBit
//
//============================================================================
void A_PotteryChooseBit()
{
static const statelabel bits[] = { "Pottery1", "Pottery2", "Pottery3", "Pottery4", "Pottery5" };
LoopState = FindState(bits[random[PotteryBit]() % 5]); // Save the state for jumping back to.
SetState (LoopState);
tics = 256 + (random[PotteryBit]() << 1);
}
//============================================================================
//
// A_PotteryCheck
//
//============================================================================
void A_PotteryCheck()
{
for(int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
Actor pmo = players[i].mo;
if (CheckSight (pmo) && (absangle(pmo.AngleTo(self), pmo.Angle) <= 45))
{ // jump back to previpusly set state.
SetState (LoopState);
return;
}
}
}
}
}
@ -203,7 +271,7 @@ class BloodPool : Actor
// Lynched corpse (no heart) ------------------------------------------------
class ZCorpseLynchedNoHeart : Actor native
class ZCorpseLynchedNoHeart : Actor
{
Default
{
@ -212,14 +280,32 @@ class ZCorpseLynchedNoHeart : Actor native
+SOLID +SPAWNCEILING +NOGRAVITY
}
native void A_CorpseBloodDrip();
States
{
Spawn:
CPS5 A 140 A_CorpseBloodDrip;
Loop;
}
override void PostBeginPlay ()
{
Super.PostBeginPlay ();
Spawn ("BloodPool", (pos.xy, floorz), ALLOW_REPLACE);
}
//============================================================================
//
// A_CorpseBloodDrip
//
//============================================================================
void A_CorpseBloodDrip()
{
if (random[CorpseDrip]() <= 128)
{
Spawn ("CorpseBloodDrip", pos + (0, 0, Height / 2), ALLOW_REPLACE);
}
}
}
@ -289,8 +375,6 @@ class ZCorpseSitting : Actor
DeathSound "FireDemonDeath";
}
native void A_CorpseExplode();
States
{
Spawn:
@ -300,6 +384,40 @@ class ZCorpseSitting : Actor
CPS6 A 1 A_CorpseExplode;
Stop;
}
//============================================================================
//
// A_CorpseExplode
//
//============================================================================
void A_CorpseExplode()
{
Actor mo;
for (int i = (random[CorpseExplode]() & 3) + 3; i; i--)
{
mo = Spawn ("CorpseBit", Pos, ALLOW_REPLACE);
if (mo)
{
mo.SetState (mo.SpawnState + (random[CorpseExplode]() % 3));
mo.Vel.X = random2[CorpseExplode]() / 64.;
mo.Vel.Y = random2[CorpseExplode]() / 64.;
mo.Vel.Z = ((random[CorpseExplode]() & 7) + 5) * 0.75;
}
}
// Spawn a skull
mo = Spawn ("CorpseBit", Pos, ALLOW_REPLACE);
if (mo)
{
mo.SetState (mo.SpawnState + 3);
mo.Vel.X = random2[CorpseExplode]() / 64.;
mo.Vel.Y = random2[CorpseExplode]() / 64.;
mo.Vel.Z = ((random[CorpseExplode]() & 7) + 5) * 0.75;
}
A_PlaySound (DeathSound, CHAN_BODY);
Destroy ();
}
}
@ -313,7 +431,6 @@ class LeafSpawner : Actor
+INVISIBLE
}
native void A_LeafSpawn();
States
{
@ -321,6 +438,32 @@ class LeafSpawner : Actor
TNT1 A 20 A_LeafSpawn;
Loop;
}
//============================================================================
//
// A_LeafSpawn
//
//============================================================================
void A_LeafSpawn()
{
static const class<Actor> leaves[] = { "Leaf1", "Leaf2" };
for (int i = (random[LeafSpawn]() & 3) + 1; i; i--)
{
double xo = random2[LeafSpawn]() / 4.;
double yo = random2[LeafSpawn]() / 4.;
double zo = random[LeafSpawn]() / 4.;
Actor mo = Spawn (leaves[random[LeafSpawn]()&1], Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo.Thrust(random[LeafSpawn]() / 128. + 3);
mo.target = self;
mo.special1 = 0;
}
}
}
}
@ -338,15 +481,13 @@ class Leaf1 : Actor
+NOICEDEATH
}
native void A_LeafThrust();
native void A_LeafCheck();
States
{
Spawn:
LEF1 ABC 4;
LEF1 D 4 A_LeafThrust;
LEF1 EFG 4;
Looping:
LEF1 H 4 A_LeafThrust;
LEF1 I 4;
LEF1 AB 4;
@ -359,6 +500,49 @@ class Leaf1 : Actor
LEF3 D 10 A_LeafCheck;
Wait;
}
//============================================================================
//
// A_LeafThrust
//
//============================================================================
void A_LeafThrust()
{
if (random[LeafThrust]() <= 96)
{
Vel.Z += random[LeafThrust]() / 128. + 1;
}
}
//============================================================================
//
// A_LeafCheck
//
//============================================================================
void A_LeafCheck()
{
special1++;
if (special1 >= 20)
{
Destroy();
return;
}
double ang = target ? target.angle : angle;
if (random[LeafCheck]() > 64)
{
if (Vel.X == 0 && Vel.Y == 0)
{
Thrust(random[LeafCheck]() / 128. + 1, ang);
}
return;
}
SetStateLabel ("Looping");
Vel.Z = random[LeafCheck]() / 128. + 1;
Thrust(random[LeafCheck]() / 128. + 2, ang);
bMissile = true;
}
}
@ -556,8 +740,6 @@ class ZSuitOfArmor : Actor
DeathSound "SuitofArmorBreak";
}
native void A_SoAExplode();
States
{
Spawn:
@ -567,6 +749,41 @@ class ZSuitOfArmor : Actor
ZSUI A 1 A_SoAExplode;
Stop;
}
//===========================================================================
//
// A_SoAExplode - Suit of Armor Explode
//
//===========================================================================
void A_SoAExplode()
{
for (int i = 0; i < 10; i++)
{
double xo = (random[SoAExplode]() - 128) / 16.;
double yo = (random[SoAExplode]() - 128) / 16.;
double zo = random[SoAExplode]() * Height / 256.;
Actor mo = Spawn ("ZArmorChunk", Vec3Offset(xo, yo, zo), ALLOW_REPLACE);
if (mo)
{
mo.SetState (mo.SpawnState + i);
mo.Vel.X = random2[SoAExplode]() / 64.;
mo.Vel.Y = random2[SoAExplode]() / 64.;
mo.Vel.Z = (random[SoAExplode]() & 7) + 5;
}
}
// Spawn an item?
Class<Actor> type = GetSpawnableType(args[0]);
if (type != null)
{
if (!(level.nomonsters || sv_nomonsters) || !(GetDefaultByType (type).bIsMonster))
{ // Only spawn monsters if not -nomonsters
Spawn (type, Pos, ALLOW_REPLACE);
}
}
A_PlaySound (DeathSound, CHAN_BODY);
Destroy ();
}
}
@ -608,7 +825,7 @@ class ZArmorChunk : Actor
// Bell ---------------------------------------------------------------------
class ZBell : Actor native
class ZBell : Actor
{
Default
{
@ -621,9 +838,6 @@ class ZBell : Actor native
DeathSound "BellRing";
}
native void A_BellReset1();
native void A_BellReset2();
States
{
Spawn:
@ -665,6 +879,45 @@ class ZBell : Actor native
BBLL A 1 A_BellReset2;
Goto Spawn;
}
override void Activate (Actor activator)
{
if (health > 0) A_Die();
{
DamageMobj (activator, activator, 10, 'Melee', DMG_THRUSTLESS); // 'ring' the bell
}
}
//===========================================================================
//
// A_BellReset1
//
//===========================================================================
void A_BellReset1()
{
bNoGravity = true;
Height = Default.Height;
if (special)
{ // Initiate death action
A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
special = 0;
}
}
//===========================================================================
//
// A_BellReset2
//
//===========================================================================
void A_BellReset2()
{
bShootable = true;
bCorpse = false;
bKilled = false;
health = 5;
}
}