From 5cc1b4991c936d0243703dbe6895a1d9c5438d12 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 20 Jul 2008 14:42:54 +0000 Subject: [PATCH] - converted the boss brain to DECORATE. - added an abstract base class for special map spots that are maintained in lists and rewrote the boss brain, the mace and DSparil to use it. - fixed: RandomSpawners didn't destroy themselves after finishing their work. SVN r1076 (trunk) --- docs/rh-log.txt | 4 + src/g_doom/a_bossbrain.cpp | 226 ++--------------- src/g_doom/a_doomglobal.h | 5 - src/g_heretic/a_dsparil.cpp | 79 +----- src/g_heretic/a_hereticglobal.h | 6 - src/g_heretic/a_hereticweaps.cpp | 101 ++------ src/g_shared/a_randomspawner.cpp | 5 +- src/g_shared/a_specialspot.cpp | 374 +++++++++++++++++++++++++++++ src/g_shared/a_specialspot.h | 45 ++++ src/namedef.h | 1 + src/version.h | 2 +- wadsrc/decorate/decorate.txt | 1 + wadsrc/decorate/doom/bossbrain.txt | 123 ++++++++++ wadsrc/zdoom.lst | 1 + zdoom.vcproj | 8 + 15 files changed, 611 insertions(+), 370 deletions(-) create mode 100644 src/g_shared/a_specialspot.cpp create mode 100644 src/g_shared/a_specialspot.h create mode 100644 wadsrc/decorate/doom/bossbrain.txt diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 117f73624..1d607e93b 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,4 +1,8 @@ July 20, 2008 (Changes by Graf Zahl) +- converted the boss brain to DECORATE. +- added an abstract base class for special map spots that are maintained in + lists and rewrote the boss brain, the mace and DSparil to use it. +- fixed: RandomSpawners didn't destroy themselves after finishing their work. - fixed: Textures marked as complex must not redirect to the base patch. - fixed: Alpha for composite textures was not applied. - fixed: The CentaurMash didn't inherit from the Centaur. diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp index b82857c47..9f65336bb 100644 --- a/src/g_doom/a_bossbrain.cpp +++ b/src/g_doom/a_bossbrain.cpp @@ -6,172 +6,13 @@ #include "s_sound.h" #include "a_doomglobal.h" #include "statnums.h" +#include "a_specialspot.h" #include "thingdef/thingdef.h" -void A_Fire (AActor *); // from m_archvile.cpp - -void A_BrainAwake (AActor *); -void A_BrainPain (AActor *); -void A_BrainScream (AActor *); -void A_BrainExplode (AActor *); -void A_BrainDie (AActor *); -void A_BrainSpit (AActor *); -void A_SpawnFly (AActor *); -void A_SpawnSound (AActor *); - static FRandom pr_brainscream ("BrainScream"); static FRandom pr_brainexplode ("BrainExplode"); static FRandom pr_spawnfly ("SpawnFly"); -class ABossTarget : public AActor -{ - DECLARE_STATELESS_ACTOR (ABossTarget, AActor) -public: - void BeginPlay (); -}; - -class DBrainState : public DThinker -{ - DECLARE_CLASS (DBrainState, DThinker) -public: - DBrainState () - : DThinker (STAT_BOSSTARGET), - Targets (STAT_BOSSTARGET), - SerialTarget (NULL), - Easy (false) - {} - void Serialize (FArchive &arc); - ABossTarget *GetTarget (); -protected: - TThinkerIterator Targets; - ABossTarget *SerialTarget; - bool Easy; -}; - -FState ABossBrain::States[] = -{ -#define S_BRAINEXPLODE 0 - S_BRIGHT (MISL, 'B', 10, NULL , &States[S_BRAINEXPLODE+1]), - S_BRIGHT (MISL, 'C', 10, NULL , &States[S_BRAINEXPLODE+2]), - S_BRIGHT (MISL, 'D', 10, A_BrainExplode , NULL), - -#define S_BRAIN (S_BRAINEXPLODE+3) - S_NORMAL (BBRN, 'A', -1, NULL , NULL), - -#define S_BRAIN_PAIN (S_BRAIN+1) - S_NORMAL (BBRN, 'B', 36, A_BrainPain , &States[S_BRAIN]), - -#define S_BRAIN_DIE (S_BRAIN_PAIN+1) - S_NORMAL (BBRN, 'A', 100, A_BrainScream , &States[S_BRAIN_DIE+1]), - S_NORMAL (BBRN, 'A', 10, NULL , &States[S_BRAIN_DIE+2]), - S_NORMAL (BBRN, 'A', 10, NULL , &States[S_BRAIN_DIE+3]), - S_NORMAL (BBRN, 'A', -1, A_BrainDie , NULL) -}; - -IMPLEMENT_ACTOR (ABossBrain, Doom, 88, 0) - PROP_SpawnHealth (250) - //PROP_HeightFixed (86) // don't do this; it messes up some non-id levels - PROP_MassLong (10000000) - PROP_PainChance (255) - PROP_Flags (MF_SOLID|MF_SHOOTABLE) - PROP_Flags4 (MF4_NOICEDEATH) - PROP_Flags5 (MF5_OLDRADIUSDMG) - - PROP_SpawnState (S_BRAIN) - PROP_PainState (S_BRAIN_PAIN) - PROP_DeathState (S_BRAIN_DIE) - - PROP_PainSound ("brain/pain") - PROP_DeathSound ("brain/death") -END_DEFAULTS - -class ABossEye : public AActor -{ - DECLARE_ACTOR (ABossEye, AActor) -}; - -FState ABossEye::States[] = -{ -#define S_BRAINEYE 0 - S_NORMAL (SSWV, 'A', 10, A_Look , &States[S_BRAINEYE]), - -#define S_BRAINEYESEE (S_BRAINEYE+1) - S_NORMAL (SSWV, 'A', 181, A_BrainAwake , &States[S_BRAINEYESEE+1]), - S_NORMAL (SSWV, 'A', 150, A_BrainSpit , &States[S_BRAINEYESEE+1]) -}; - -IMPLEMENT_ACTOR (ABossEye, Doom, 89, 0) - PROP_HeightFixed (32) - PROP_Flags (MF_NOBLOCKMAP|MF_NOSECTOR) - - PROP_SpawnState (S_BRAINEYE) - PROP_SeeState (S_BRAINEYESEE) -END_DEFAULTS - -IMPLEMENT_STATELESS_ACTOR (ABossTarget, Doom, 87, 0) - PROP_HeightFixed (32) - PROP_Flags (MF_NOBLOCKMAP|MF_NOSECTOR) -END_DEFAULTS - -void ABossTarget::BeginPlay () -{ - Super::BeginPlay (); - ChangeStatNum (STAT_BOSSTARGET); -} - -class ASpawnShot : public AActor -{ - DECLARE_ACTOR (ASpawnShot, AActor) -}; - -FState ASpawnShot::States[] = -{ - S_BRIGHT (BOSF, 'A', 3, A_SpawnSound , &States[1]), - S_BRIGHT (BOSF, 'B', 3, A_SpawnFly , &States[2]), - S_BRIGHT (BOSF, 'C', 3, A_SpawnFly , &States[3]), - S_BRIGHT (BOSF, 'D', 3, A_SpawnFly , &States[0]) -}; - -IMPLEMENT_ACTOR (ASpawnShot, Doom, -1, 0) - PROP_RadiusFixed (6) - PROP_HeightFixed (32) - PROP_SpeedFixed (10) - PROP_Damage (3) - PROP_Flags (MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP) - PROP_Flags2 (MF2_NOTELEPORT) - PROP_Flags4 (MF4_RANDOMIZE) - - PROP_SpawnState (0) - - PROP_SeeSound ("brain/spit") - PROP_DeathSound ("brain/cubeboom") -END_DEFAULTS - -class ASpawnFire : public AActor -{ - DECLARE_ACTOR (ASpawnFire, AActor) -}; - -FState ASpawnFire::States[] = -{ - S_BRIGHT (FIRE, 'A', 4, A_Fire , &States[1]), - S_BRIGHT (FIRE, 'B', 4, A_Fire , &States[2]), - S_BRIGHT (FIRE, 'C', 4, A_Fire , &States[3]), - S_BRIGHT (FIRE, 'D', 4, A_Fire , &States[4]), - S_BRIGHT (FIRE, 'E', 4, A_Fire , &States[5]), - S_BRIGHT (FIRE, 'F', 4, A_Fire , &States[6]), - S_BRIGHT (FIRE, 'G', 4, A_Fire , &States[7]), - S_BRIGHT (FIRE, 'H', 4, A_Fire , NULL) -}; - -IMPLEMENT_ACTOR (ASpawnFire, Doom, -1, 0) - PROP_HeightFixed (78) - PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY) - PROP_RenderStyle (STYLE_Add) - - PROP_SpawnState (0) -END_DEFAULTS - void A_BrainAwake (AActor *self) { // killough 3/26/98: only generates sound now @@ -190,7 +31,15 @@ static void BrainishExplosion (fixed_t x, fixed_t y, fixed_t z) { boom->DeathSound = "misc/brainexplode"; boom->momz = pr_brainscream() << 9; - boom->SetState (&ABossBrain::States[S_BRAINEXPLODE]); + + const PClass *cls = PClass::FindClass("BossBrain"); + if (cls != NULL) + { + FState *state = cls->ActorInfo->FindState(NAME_Brainexplode); + if (state != NULL) + boom->SetState (state); + + } boom->effects = 0; boom->Damage = 0; // disables collision detection which is not wanted here boom->tics -= pr_brainscream() & 7; @@ -229,24 +78,19 @@ void A_BrainDie (AActor *self) void A_BrainSpit (AActor *self) { - TThinkerIterator iterator (STAT_BOSSTARGET); - DBrainState *state; + DSpotState *state = DSpotState::GetSpotState(); AActor *targ; AActor *spit; // shoot a cube at current target - if (NULL == (state = iterator.Next ())) - { - state = new DBrainState; - } - targ = state->GetTarget (); + targ = state->GetNextInList(PClass::FindClass("BossTarget"), G_SkillProperty(SKILLP_EasyBossBrain)); if (targ != NULL) { const PClass *spawntype = NULL; int index = CheckIndex (1, NULL); if (index >= 0) spawntype = PClass::FindClass ((ENamedName)StateParameters[index]); - if (spawntype == NULL) spawntype = RUNTIME_CLASS(ASpawnShot); + if (spawntype == NULL) spawntype = PClass::FindClass("SpawnShot"); // spawn brain missile spit = P_SpawnMissile (self, targ, spawntype); @@ -313,7 +157,7 @@ void A_SpawnFly (AActor *self) } else { - fog = Spawn (targ->x, targ->y, targ->z, ALLOW_REPLACE); + fog = Spawn("SpawnFire", targ->x, targ->y, targ->z, ALLOW_REPLACE); if (fog != NULL) S_Sound (fog, CHAN_BODY, "brain/spawn", 1, ATTN_NORM); } @@ -408,45 +252,3 @@ void A_SpawnSound (AActor *self) A_SpawnFly (self); } -// Each brain on the level shares a single global state -IMPLEMENT_CLASS (DBrainState) - -ABossTarget *DBrainState::GetTarget () -{ - Easy = !Easy; - - if (G_SkillProperty(SKILLP_EasyBossBrain) && !Easy) - return NULL; - - ABossTarget *target; - - if (SerialTarget) - { - do - { - target = Targets.Next (); - } while (target != NULL && target != SerialTarget); - SerialTarget = NULL; - } - else - { - target = Targets.Next (); - } - return (target == NULL) ? Targets.Next () : target; -} - -void DBrainState::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << Easy; - if (arc.IsStoring ()) - { - ABossTarget *target = Targets.Next (); - arc << target; - } - else - { - arc << SerialTarget; - } -} - diff --git a/src/g_doom/a_doomglobal.h b/src/g_doom/a_doomglobal.h index f3526264c..cdd6839b6 100644 --- a/src/g_doom/a_doomglobal.h +++ b/src/g_doom/a_doomglobal.h @@ -5,11 +5,6 @@ #include "info.h" #include "d_player.h" -class ABossBrain : public AActor -{ - DECLARE_ACTOR (ABossBrain, AActor) -}; - class AScriptedMarine : public AActor { DECLARE_ACTOR (AScriptedMarine, AActor) diff --git a/src/g_heretic/a_dsparil.cpp b/src/g_heretic/a_dsparil.cpp index f5da7632e..203f360fb 100644 --- a/src/g_heretic/a_dsparil.cpp +++ b/src/g_heretic/a_dsparil.cpp @@ -8,6 +8,7 @@ #include "m_random.h" #include "a_sharedglobal.h" #include "gstrings.h" +#include "a_specialspot.h" static FRandom pr_s2fx1 ("S2FX1"); static FRandom pr_scrc1atk ("Srcr1Attack"); @@ -35,31 +36,15 @@ void A_GenWizard (AActor *); // Boss spot ---------------------------------------------------------------- -class ABossSpot : public AActor +class ABossSpot : public ASpecialSpot { - DECLARE_STATELESS_ACTOR (ABossSpot, AActor) -public: - ABossSpot *NextSpot; - void Serialize (FArchive &arc); - void BeginPlay (); + DECLARE_STATELESS_ACTOR (ABossSpot, ASpecialSpot) }; -void ABossSpot::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << NextSpot; -} - IMPLEMENT_STATELESS_ACTOR (ABossSpot, Heretic, 56, 141) PROP_RenderFlags (RF_INVISIBLE) END_DEFAULTS -void ABossSpot::BeginPlay () -{ - Super::BeginPlay (); - NextSpot = NULL; -} - // Sorcerer (D'Sparil on his serpent) --------------------------------------- class ASorcerer1 : public AActor @@ -263,29 +248,6 @@ IMPLEMENT_ACTOR (ASorcerer2, Heretic, -1, 143) PROP_HitObituary("$OB_DSPARIL2HIT") END_DEFAULTS -void ASorcerer2::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << NumBossSpots << FirstBossSpot; -} - -void ASorcerer2::BeginPlay () -{ - TThinkerIterator iterator; - ABossSpot *spot; - - Super::BeginPlay (); - NumBossSpots = 0; - spot = iterator.Next (); - FirstBossSpot = static_cast (spot); - while (spot) - { - NumBossSpots++; - spot->NextSpot = iterator.Next (); - spot = spot->NextSpot; - } -} - // Sorcerer 2 FX 1 ---------------------------------------------------------- class ASorcerer2FX1 : public AActor @@ -525,39 +487,18 @@ void A_SorcererRise (AActor *actor) void P_DSparilTeleport (AActor *actor) { - int i; fixed_t prevX; fixed_t prevY; fixed_t prevZ; AActor *mo; - ASorcerer2 *self = static_cast (actor); - ABossSpot *spot; - ABossSpot *initial; + AActor *spot; + + DSpotState *state = DSpotState::GetSpotState(); + if (state == NULL) return; + + spot = state->GetSpotWithMinDistance(RUNTIME_CLASS(ABossSpot), actor->x, actor->y, 128*FRACUNIT); + if (spot == NULL) return; - if (!self->NumBossSpots) - { // No spots - return; - } - i = pr_dst () % self->NumBossSpots; - spot = static_cast (self->FirstBossSpot); - while (i-- > 0) - { - spot = spot->NextSpot; - } - initial = spot; - while (P_AproxDistance (actor->x - spot->x, actor->y - spot->y) < 128*FRACUNIT) - { - spot = spot->NextSpot; - if (spot == NULL) - { - spot = static_cast (self->FirstBossSpot); - } - if (spot == initial) - { - // [RH] Don't inifinite loop if no spots further than 128*FRACUNIT - return; - } - } prevX = actor->x; prevY = actor->y; prevZ = actor->z; diff --git a/src/g_heretic/a_hereticglobal.h b/src/g_heretic/a_hereticglobal.h index bd94b7922..cde20aff3 100644 --- a/src/g_heretic/a_hereticglobal.h +++ b/src/g_heretic/a_hereticglobal.h @@ -24,12 +24,6 @@ class APhoenixPuff : public AActor class ASorcerer2 : public AActor { DECLARE_ACTOR (ASorcerer2, AActor) -public: - void Serialize (FArchive &arc); - void BeginPlay (); - - int NumBossSpots; - AActor *FirstBossSpot; }; class AWizard : public AActor diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index fd2ed7e82..10db6cc50 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -14,6 +14,7 @@ #include "p_enemy.h" #include "gi.h" #include "r_translate.h" +#include "a_specialspot.h" static FRandom pr_sap ("StaffAtkPL1"); static FRandom pr_sap2 ("StaffAtkPL2"); @@ -791,16 +792,8 @@ void A_DeathBallImpact (AActor *); class AMace : public AHereticWeapon { DECLARE_ACTOR (AMace, AHereticWeapon) - HAS_OBJECT_POINTERS -public: - void Serialize (FArchive &arc); protected: bool DoRespawn (); - int NumMaceSpots; - TObjPtr FirstSpot; -private: - - friend void A_SpawnMace (AActor *self); }; class AMacePowered : public AMace @@ -808,16 +801,6 @@ class AMacePowered : public AMace DECLARE_STATELESS_ACTOR (AMacePowered, AMace) }; -IMPLEMENT_POINTY_CLASS (AMace) - DECLARE_POINTER (FirstSpot) -END_POINTERS - -void AMace::Serialize (FArchive &arc) -{ - Super::Serialize (arc); - arc << NumMaceSpots << FirstSpot; -} - FState AMace::States[] = { #define S_WMCE 0 @@ -851,7 +834,7 @@ FState AMace::States[] = S_NORMAL (MACE, 'A', 8, A_ReFire , &States[S_MACEREADY]) }; -BEGIN_DEFAULTS (AMace, Heretic, -1, 0) +IMPLEMENT_ACTOR (AMace, Heretic, -1, 0) PROP_Flags (MF_SPECIAL) PROP_SpawnState (0) @@ -1027,11 +1010,9 @@ int AMaceFX4::DoSpecialDamage (AActor *target, int damage) void A_SpawnMace (AActor *); -class AMaceSpawner : public AActor +class AMaceSpawner : public ASpecialSpot { - DECLARE_ACTOR (AMaceSpawner, AActor) - - // Uses target to point to the next mace spawner in the list + DECLARE_ACTOR (AMaceSpawner, ASpecialSpot) }; FState AMaceSpawner::States[] = @@ -1046,25 +1027,6 @@ IMPLEMENT_ACTOR (AMaceSpawner, Heretic, 2002, 0) END_DEFAULTS -static bool RespawnMace (AActor *mace, AActor *FirstSpot, int NumMaceSpots) -{ - if (NumMaceSpots > 0) - { - int spotnum = pr_macerespawn () % NumMaceSpots; - AActor *spot = FirstSpot; - - while (spotnum > 0) - { - spot = spot->target; - spotnum--; - } - - mace->SetOrigin (spot->x, spot->y, spot->z); - mace->z = mace->floorz; - } - return true; -} - // Every mace spawn spot will execute this action. The first one // will build a list of all mace spots in the level and spawn a // mace. The rest of the spots will do nothing. @@ -1076,58 +1038,45 @@ void A_SpawnMace (AActor *self) return; } - TThinkerIterator iterator; - AActor *spot; - AMaceSpawner *firstSpot; - AMace *mace; - int numspots = 0; + AActor *spot = NULL; + DSpotState *state = DSpotState::GetSpotState(); + + if (state != NULL) spot = state->GetRandomSpot(RUNTIME_TYPE(self), true); + if (spot == NULL) return; - spot = firstSpot = iterator.Next (); - while (spot != NULL) - { - numspots++; - spot->target = iterator.Next (); - if (spot->target == NULL) - { - spot->target = firstSpot; - spot = NULL; - } - else - { - spot = spot->target; - } - } - if (numspots == 0) - { - return; - } if (!deathmatch && pr_spawnmace() < 64) { // Sometimes doesn't show up if not in deathmatch return; } - mace = Spawn (self->x, self->y, self->z, ALLOW_REPLACE); + + AActor *mace = Spawn (self->x, self->y, self->z, ALLOW_REPLACE); if (mace) { - if (mace->IsKindOf(RUNTIME_CLASS(AMace))) - { - // remember the values for later - // (works only for the original mace!) - mace->FirstSpot = firstSpot; - mace->NumMaceSpots = numspots; - } - RespawnMace(mace, firstSpot, numspots); + mace->SetOrigin (spot->x, spot->y, spot->z); + mace->z = mace->floorz; // We want this mace to respawn. mace->flags &= ~MF_DROPPED; } } +// FIXME: Generalize this so that it doesn't depend on item specific implementation! + // AMace::DoRespawn // Moves the mace to a different spot when it respawns bool AMace::DoRespawn () { - return RespawnMace(this, FirstSpot, NumMaceSpots); + AActor *spot = NULL; + DSpotState *state = DSpotState::GetSpotState(); + + if (state != NULL) spot = state->GetRandomSpot(RUNTIME_CLASS(AMaceSpawner)); + if (spot != NULL) + { + SetOrigin (spot->x, spot->y, spot->z); + z = floorz; + } + return true; } //---------------------------------------------------------------------------- diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index f0e646c24..226a5f430 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -55,7 +55,7 @@ class ARandomSpawner : public AActor if (di->Name != NAME_None) { n -= di->amount; - di = di->Next; + if (di->Next != NULL) di = di->Next; else n=0; } } // So now we can spawn the dropped item. @@ -72,12 +72,15 @@ class ARandomSpawner : public AActor newmobj->args[4] = args[4]; newmobj->SpawnFlags = SpawnFlags; newmobj->HandleSpawnFlags(); + newmobj->tid = tid; + newmobj->AddToHash(); newmobj->momx = momx; newmobj->momy = momy; newmobj->momz = momz; newmobj->CopyFriendliness(this, false); } } + Destroy(); } }; diff --git a/src/g_shared/a_specialspot.cpp b/src/g_shared/a_specialspot.cpp new file mode 100644 index 000000000..05e47b06e --- /dev/null +++ b/src/g_shared/a_specialspot.cpp @@ -0,0 +1,374 @@ +/* +** a_specialspot.cpp +** Handling for special spot actors +** like BrainTargets, MaceSpawners etc. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ +#include "a_specialspot.h" +#include "m_random.h" +#include "p_local.h" +#include "statnums.h" +#include "i_system.h" + +static FRandom pr_spot ("SpecialSpot"); + + +IMPLEMENT_CLASS(DSpotState) +IMPLEMENT_ABSTRACT_ACTOR (ASpecialSpot) +TObjPtr DSpotState::SpotState; + +//---------------------------------------------------------------------------- +// +// Spot list +// +//---------------------------------------------------------------------------- + +struct FSpotList +{ + const PClass *Type; + TArray Spots; + unsigned Index; + int SkipCount; + int numcalls; + + FSpotList() + { + } + + FSpotList(const PClass *type) + { + Type = type; + Index = 0; + SkipCount = 0; + numcalls = 0; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + void Serialize(FArchive &arc) + { + arc << Type << Spots << Index << SkipCount << numcalls; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + bool Add(ASpecialSpot *newspot) + { + for(unsigned i = 0; i < Spots.Size(); i++) + { + if (Spots[i] == newspot) return false; + } + Spots.Push(newspot); + return true; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + bool Remove(ASpecialSpot *spot) + { + for(unsigned i = 0; i < Spots.Size(); i++) + { + if (Spots[i] == spot) + { + Spots.Delete(i); + if (Index > i) Index--; + return true; + } + } + return false; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + ASpecialSpot *GetNextInList(int skipcounter) + { + if (++SkipCount > skipcounter) + { + SkipCount = 0; + + ASpecialSpot *spot = Spots[Index]; + if (++Index >= Spots.Size()) Index = 0; + numcalls++; + return spot; + } + return NULL; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + ASpecialSpot *GetSpotWithMinDistance(fixed_t x, fixed_t y, fixed_t distance) + { + int i = pr_spot() % Spots.Size(); + int initial = i; + + while (P_AproxDistance(Spots[i]->x - x, Spots[i]->y - y) < distance) + { + i = (i+1) % Spots.Size(); + if (i == initial) return NULL; + } + numcalls++; + return Spots[i]; + } + + //---------------------------------------------------------------------------- + // + // + // + //---------------------------------------------------------------------------- + + ASpecialSpot *GetRandomSpot(bool onlyfirst) + { + if (!numcalls) + { + int i = pr_spot() % Spots.Size(); + numcalls++; + return Spots[i]; + } + else return NULL; + } +}; + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +DSpotState::DSpotState () +: DThinker (STAT_INFO) +{ + if (SpotState) + { + I_Error ("Only one SpotState is allowed to exist at a time.\nCheck your code."); + } + else + { + SpotState = this; + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void DSpotState::Destroy () +{ + for(unsigned i = 0; i < SpotLists.Size(); i++) + { + delete SpotLists[i]; + } + SpotLists.Clear(); + SpotLists.ShrinkToFit(); + + SpotState = NULL; + Super::Destroy(); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void DSpotState::Tick () +{ +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +DSpotState *DSpotState::GetSpotState(bool create) +{ + if (SpotState == NULL && create) SpotState = new DSpotState; + return SpotState; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +FSpotList *DSpotState::FindSpotList(const PClass *type) +{ + for(unsigned i = 0; i < SpotLists.Size(); i++) + { + if (SpotLists[i]->Type == type) return SpotLists[i]; + } + return SpotLists[SpotLists.Push(new FSpotList(type))]; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +bool DSpotState::AddSpot(ASpecialSpot *spot) +{ + FSpotList *list = FindSpotList(RUNTIME_TYPE(spot)); + if (list != NULL) return list->Add(spot); + return false; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +bool DSpotState::RemoveSpot(ASpecialSpot *spot) +{ + FSpotList *list = FindSpotList(RUNTIME_TYPE(spot)); + if (list != NULL) return list->Remove(spot); + return false; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void DSpotState::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + if (arc.IsStoring()) + { + arc.WriteCount(SpotLists.Size()); + for(unsigned i = 0; i < SpotLists.Size(); i++) + { + SpotLists[i]->Serialize(arc); + } + } + else + { + unsigned c = arc.ReadCount(); + SpotLists.Resize(c); + for(unsigned i = 0; i < SpotLists.Size(); i++) + { + SpotLists[i] = new FSpotList; + SpotLists[i]->Serialize(arc); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +ASpecialSpot *DSpotState::GetNextInList(const PClass *type, int skipcounter) +{ + FSpotList *list = FindSpotList(type); + if (list != NULL) return list->GetNextInList(skipcounter); + return NULL; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +ASpecialSpot *DSpotState::GetSpotWithMinDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t distance) +{ + FSpotList *list = FindSpotList(type); + if (list != NULL) return list->GetSpotWithMinDistance(x, y, distance); + return NULL; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +ASpecialSpot *DSpotState::GetRandomSpot(const PClass *type, bool onlyonce) +{ + FSpotList *list = FindSpotList(type); + if (list != NULL) return list->GetRandomSpot(onlyonce); + return NULL; +} + + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void ASpecialSpot::BeginPlay() +{ + DSpotState *state = DSpotState::GetSpotState(); + if (state != NULL) state->AddSpot(this); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void ASpecialSpot::Destroy() +{ + DSpotState *state = DSpotState::GetSpotState(false); + if (state != NULL) state->RemoveSpot(this); + Super::Destroy(); +} + + diff --git a/src/g_shared/a_specialspot.h b/src/g_shared/a_specialspot.h new file mode 100644 index 000000000..1ee7ec243 --- /dev/null +++ b/src/g_shared/a_specialspot.h @@ -0,0 +1,45 @@ +#ifndef __A_SPECSPOT_H +#define __A_SPECSPOT_H + +#include "actor.h" +#include "tarray.h" + +class ASpecialSpot : public AActor +{ + DECLARE_STATELESS_ACTOR (ASpecialSpot, AActor) + +public: + + void BeginPlay(); + void Destroy(); +}; + + +struct FSpotList; + + +class DSpotState : public DThinker +{ + DECLARE_CLASS(DSpotState, DThinker) + static TObjPtr SpotState; + TArray SpotLists; + +public: + + + DSpotState (); + void Destroy (); + void Tick (); + static DSpotState *GetSpotState(bool create = true); + FSpotList *FindSpotList(const PClass *type); + bool AddSpot(ASpecialSpot *spot); + bool RemoveSpot(ASpecialSpot *spot); + void Serialize(FArchive &arc); + ASpecialSpot *GetNextInList(const PClass *type, int skipcounter); + ASpecialSpot *GetSpotWithMinDistance(const PClass *type, fixed_t x, fixed_t y, fixed_t distance); + ASpecialSpot *GetRandomSpot(const PClass *type, bool onlyonce = false); +}; + + + +#endif \ No newline at end of file diff --git a/src/namedef.h b/src/namedef.h index da2961e75..98a24ca2d 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -147,6 +147,7 @@ xx(XDeath) xx(Burn) //xx(Ice) // already defined above xx(Disintegrate) +xx(Brainexplode) // Weapon animator names. xx(Select) diff --git a/src/version.h b/src/version.h index 6f27c61ba..8878e17f5 100644 --- a/src/version.h +++ b/src/version.h @@ -75,7 +75,7 @@ // SAVESIG should match SAVEVER. // MINSAVEVER is the minimum level snapshot version that can be loaded. -#define MINSAVEVER 1063 +#define MINSAVEVER 1075 #if SVN_REVISION_NUMBER < MINSAVEVER // Never write a savegame with a version lower than what we need diff --git a/wadsrc/decorate/decorate.txt b/wadsrc/decorate/decorate.txt index 3448e863d..a500224ac 100644 --- a/wadsrc/decorate/decorate.txt +++ b/wadsrc/decorate/decorate.txt @@ -25,6 +25,7 @@ #include "actors/doom/cyberdemon.txt" #include "actors/doom/spidermaster.txt" #include "actors/doom/keen.txt" +#include "actors/doom/bossbrain.txt" #include "actors/doom/deadthings.txt" #include "actors/doom/doomammo.txt" diff --git a/wadsrc/decorate/doom/bossbrain.txt b/wadsrc/decorate/doom/bossbrain.txt new file mode 100644 index 000000000..d057be890 --- /dev/null +++ b/wadsrc/decorate/doom/bossbrain.txt @@ -0,0 +1,123 @@ + +//=========================================================================== +// +// Boss Brain +// +//=========================================================================== + +ACTOR BossBrain 88 +{ + Game Doom + Health 250 + Mass 10000000 + PainChance 255 + +SOLID +SHOOTABLE + +NOICEDEATH + +OLDRADIUSDMG + PainSound "brain/pain" + DeathSound "brain/death" + States + { + BrainExplode: + MISL BC 10 Bright + MISL D 10 A_BrainExplode + Stop + Spawn: + BBRN A -1 + Stop + Pain: + BBRN B 36 A_BrainPain + Goto Spawn + Death: + BBRN A 100 A_BrainScream + BBRN AA 10 + BBRN A -1 A_BrainDie + Stop + } +} + + +//=========================================================================== +// +// Boss Eye +// +//=========================================================================== + +ACTOR BossEye 89 +{ + Game Doom + Height 32 + +NOBLOCKMAP + +NOSECTOR + States + { + Spawn: + SSWV A 10 A_Look + Loop + See: + SSWV A 181 A_BrainAwake + SSWV A 150 A_BrainSpit + Loop + } +} + +//=========================================================================== +// +// Boss Target +// +//=========================================================================== + +ACTOR BossTarget : SpecialSpot 87 +{ + Game Doom + Height 32 + +NOBLOCKMAP + +NOSECTOR +} + +//=========================================================================== +// +// Spawn shot +// +//=========================================================================== + +ACTOR SpawnShot +{ + Radius 6 + Height 32 + Speed 10 + Damage 3 + Projectile + +NOCLIP + -ACTIVATEPCROSS + +RANDOMIZE + SeeSound "brain/spit" + DeathSound "brain/cubeboom" + States + { + Spawn: + BOSF A 3 BRIGHT A_SpawnSound + BOSF BCD 3 BRIGHT A_SpawnFly + Loop + } +} + +//=========================================================================== +// +// Spawn fire +// +//=========================================================================== + +ACTOR SpawnFire +{ + Height 78 + +NOBLOCKMAP + +NOGRAVITY + RenderStyle Add + States + { + Spawn: + FIRE ABCDEFGH 4 Bright A_Fire + Stop + } +} diff --git a/wadsrc/zdoom.lst b/wadsrc/zdoom.lst index 9f8975df3..058f28948 100644 --- a/wadsrc/zdoom.lst +++ b/wadsrc/zdoom.lst @@ -291,6 +291,7 @@ actors/doom/archvile.txt decorate/doom/archvile.txt actors/doom/cyberdemon.txt decorate/doom/cyberdemon.txt actors/doom/spidermaster.txt decorate/doom/spidermaster.txt actors/doom/keen.txt decorate/doom/keen.txt +actors/doom/bossbrain.txt decorate/doom/bossbrain.txt actors/doom/deadthings.txt decorate/doom/deadthings.txt actors/doom/doomammo.txt decorate/doom/doomammo.txt diff --git a/zdoom.vcproj b/zdoom.vcproj index db9dc0031..b7a74f2a9 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -2076,6 +2076,14 @@ RelativePath=".\src\g_shared\a_spark.cpp" > + + + +