mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-08 05:51:26 +00:00
59d932b972
- fixed: The BossCube could be blocked by floors and ceiling resulting in incorrect movement. I changed it so that A_BrainSpit now sets the MF5_NOINTERACTION flag for anything it spawns that has the MF_NOCLIP flag. For travelling cubes active collision detection makes no sense and only causes problems. This should also make the boss brain work in the other games which previously were excluded by a game mode check in the movement code. - fixed: ACS's GetUserVariable did not work for the script activator. - fixed: Moving floors could be blocked by 2 actors without MF2_PASSMOBJ overlapping each other (common mapping bug, check Herian 2 MAP30.) SVN r1891 (trunk)
284 lines
7.1 KiB
C++
284 lines
7.1 KiB
C++
/*
|
|
#include "actor.h"
|
|
#include "info.h"
|
|
#include "m_random.h"
|
|
#include "p_local.h"
|
|
#include "p_enemy.h"
|
|
#include "s_sound.h"
|
|
#include "statnums.h"
|
|
#include "a_specialspot.h"
|
|
#include "thingdef/thingdef.h"
|
|
#include "doomstat.h"
|
|
#include "g_level.h"
|
|
*/
|
|
|
|
static FRandom pr_brainscream ("BrainScream");
|
|
static FRandom pr_brainexplode ("BrainExplode");
|
|
static FRandom pr_spawnfly ("SpawnFly");
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_BrainAwake)
|
|
{
|
|
// killough 3/26/98: only generates sound now
|
|
S_Sound (self, CHAN_VOICE, "brain/sight", 1, ATTN_NONE);
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_BrainPain)
|
|
{
|
|
S_Sound (self, CHAN_VOICE, "brain/pain", 1, ATTN_NONE);
|
|
}
|
|
|
|
static void BrainishExplosion (fixed_t x, fixed_t y, fixed_t z)
|
|
{
|
|
AActor *boom = Spawn("Rocket", x, y, z, NO_REPLACE);
|
|
if (boom != NULL)
|
|
{
|
|
boom->DeathSound = "misc/brainexplode";
|
|
boom->velz = pr_brainscream() << 9;
|
|
|
|
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;
|
|
if (boom->tics < 1)
|
|
boom->tics = 1;
|
|
}
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_BrainScream)
|
|
{
|
|
fixed_t x;
|
|
|
|
for (x = self->x - 196*FRACUNIT; x < self->x + 320*FRACUNIT; x += 8*FRACUNIT)
|
|
{
|
|
BrainishExplosion (x, self->y - 320*FRACUNIT,
|
|
128 + (pr_brainscream() << (FRACBITS + 1)));
|
|
}
|
|
S_Sound (self, CHAN_VOICE, "brain/death", 1, ATTN_NONE);
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_BrainExplode)
|
|
{
|
|
fixed_t x = self->x + pr_brainexplode.Random2()*2048;
|
|
fixed_t z = 128 + pr_brainexplode()*2*FRACUNIT;
|
|
BrainishExplosion (x, self->y, z);
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_BrainDie)
|
|
{
|
|
// [RH] If noexit, then don't end the level.
|
|
if ((deathmatch || alwaysapplydmflags) && (dmflags & DF_NO_EXIT))
|
|
return;
|
|
|
|
G_ExitLevel (0, false);
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit)
|
|
{
|
|
DSpotState *state = DSpotState::GetSpotState();
|
|
AActor *targ;
|
|
AActor *spit;
|
|
bool isdefault = false;
|
|
|
|
ACTION_PARAM_START(1);
|
|
ACTION_PARAM_CLASS(spawntype, 0);
|
|
|
|
// shoot a cube at current target
|
|
targ = state->GetNextInList(PClass::FindClass("BossTarget"), G_SkillProperty(SKILLP_EasyBossBrain));
|
|
|
|
if (targ != NULL)
|
|
{
|
|
if (spawntype == NULL)
|
|
{
|
|
spawntype = PClass::FindClass("SpawnShot");
|
|
isdefault = true;
|
|
}
|
|
|
|
// spawn brain missile
|
|
spit = P_SpawnMissile (self, targ, spawntype);
|
|
|
|
// Boss cubes should move freely to their destination so it's
|
|
// probably best to disable all collision detection for them.
|
|
if (spit->flags & MF_NOCLIP) spit->flags5 |= MF5_NOINTERACTION;
|
|
|
|
if (spit != NULL)
|
|
{
|
|
spit->target = targ;
|
|
spit->master = self;
|
|
// [RH] Do this correctly for any trajectory. Doom would divide by 0
|
|
// if the target had the same y coordinate as the spitter.
|
|
if ((spit->velx | spit->vely) == 0)
|
|
{
|
|
spit->special2 = 0;
|
|
}
|
|
else if (abs(spit->vely) > abs(spit->velx))
|
|
{
|
|
spit->special2 = (targ->y - self->y) / spit->vely;
|
|
}
|
|
else
|
|
{
|
|
spit->special2 = (targ->x - self->x) / spit->velx;
|
|
}
|
|
// [GZ] Calculates when the projectile will have reached destination
|
|
spit->special2 += level.maptime;
|
|
}
|
|
|
|
if (!isdefault)
|
|
{
|
|
S_Sound(self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NONE);
|
|
}
|
|
else
|
|
{
|
|
// compatibility fallback
|
|
S_Sound (self, CHAN_WEAPON, "brain/spit", 1, ATTN_NONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SpawnFly(AActor *self, const PClass *spawntype, FSoundID sound)
|
|
{
|
|
AActor *newmobj;
|
|
AActor *fog;
|
|
AActor *eye = self->master; // The eye is the spawnshot's master, not the target!
|
|
AActor *targ = self->target; // Unlike other projectiles, the target is the intended destination.
|
|
int r;
|
|
|
|
// [GZ] Should be more viable than a countdown...
|
|
if (self->special2 != 0)
|
|
{
|
|
if (self->special2 > level.maptime)
|
|
return; // still flying
|
|
}
|
|
else
|
|
{
|
|
if (self->reactiontime == 0 || --self->reactiontime != 0)
|
|
return; // still flying
|
|
}
|
|
|
|
if (spawntype != NULL)
|
|
{
|
|
fog = Spawn (spawntype, targ->x, targ->y, targ->z, ALLOW_REPLACE);
|
|
if (fog != NULL) S_Sound (fog, CHAN_BODY, sound, 1, ATTN_NORM);
|
|
}
|
|
|
|
FName SpawnName;
|
|
|
|
FDropItem *di; // di will be our drop item list iterator
|
|
FDropItem *drop; // while drop stays as the reference point.
|
|
int n = 0;
|
|
|
|
// First see if this cube has its own actor list
|
|
drop = self->GetDropItems();
|
|
|
|
// If not, then default back to its master's list
|
|
if (drop == NULL && eye != NULL)
|
|
drop = eye->GetDropItems();
|
|
|
|
if (drop != NULL)
|
|
{
|
|
for (di = drop; di != NULL; di = di->Next)
|
|
{
|
|
if (di->Name != NAME_None)
|
|
{
|
|
if (di->amount < 0)
|
|
{
|
|
di->amount = 1; // default value is -1, we need a positive value.
|
|
}
|
|
n += di->amount; // this is how we can weight the list.
|
|
}
|
|
}
|
|
di = drop;
|
|
n = pr_spawnfly(n);
|
|
while (n >= 0)
|
|
{
|
|
if (di->Name != NAME_None)
|
|
{
|
|
n -= di->amount; // logically, none of the -1 values have survived by now.
|
|
}
|
|
if ((di->Next != NULL) && (n >= 0))
|
|
{
|
|
di = di->Next;
|
|
}
|
|
else
|
|
{
|
|
n = -1;
|
|
}
|
|
}
|
|
SpawnName = di->Name;
|
|
}
|
|
if (SpawnName == NAME_None)
|
|
{
|
|
// Randomly select monster to spawn.
|
|
r = pr_spawnfly ();
|
|
|
|
// Probability distribution (kind of :),
|
|
// decreasing likelihood.
|
|
if (r < 50) SpawnName = "DoomImp";
|
|
else if (r < 90) SpawnName = "Demon";
|
|
else if (r < 120) SpawnName = "Spectre";
|
|
else if (r < 130) SpawnName = "PainElemental";
|
|
else if (r < 160) SpawnName = "Cacodemon";
|
|
else if (r < 162) SpawnName = "Archvile";
|
|
else if (r < 172) SpawnName = "Revenant";
|
|
else if (r < 192) SpawnName = "Arachnotron";
|
|
else if (r < 222) SpawnName = "Fatso";
|
|
else if (r < 246) SpawnName = "HellKnight";
|
|
else SpawnName = "BaronOfHell";
|
|
}
|
|
spawntype = PClass::FindClass(SpawnName);
|
|
if (spawntype != NULL)
|
|
{
|
|
newmobj = Spawn (spawntype, targ->x, targ->y, targ->z, ALLOW_REPLACE);
|
|
if (newmobj != NULL)
|
|
{
|
|
// Make the new monster hate what the boss eye hates
|
|
if (eye != NULL)
|
|
{
|
|
newmobj->CopyFriendliness (eye, false);
|
|
}
|
|
if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true, NULL))
|
|
newmobj->SetState (newmobj->SeeState);
|
|
|
|
if (!(newmobj->ObjectFlags & OF_EuthanizeMe))
|
|
{
|
|
// telefrag anything in this spot
|
|
P_TeleportMove (newmobj, newmobj->x, newmobj->y, newmobj->z, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove self (i.e., cube).
|
|
self->Destroy ();
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SpawnFly)
|
|
{
|
|
FSoundID sound;
|
|
|
|
ACTION_PARAM_START(1);
|
|
ACTION_PARAM_CLASS(spawntype, 0);
|
|
|
|
if (spawntype != NULL)
|
|
{
|
|
sound = GetDefaultByType(spawntype)->SeeSound;
|
|
}
|
|
else
|
|
{
|
|
spawntype = PClass::FindClass ("SpawnFire");
|
|
sound = "brain/spawn";
|
|
}
|
|
SpawnFly(self, spawntype, sound);
|
|
}
|
|
|
|
// travelling cube sound
|
|
DEFINE_ACTION_FUNCTION(AActor, A_SpawnSound)
|
|
{
|
|
S_Sound (self, CHAN_BODY, "brain/cube", 1, ATTN_IDLE);
|
|
SpawnFly(self, PClass::FindClass("SpawnFire"), "brain/spawn");
|
|
}
|