mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-08 05:51:26 +00:00
e4af82ae96
velocity, and now it's known as such. The actor variables momx/momy/momz are now known as velx/vely/velz, and the ACS functions GetActorMomX/Y/Z are now known as GetActorVelX/Y/Z. For compatibility, momx/momy/momz will continue to work as aliases from DECORATE. The ACS functions, however, require you to use the new name, since they never saw an official release yet. SVN r1689 (trunk)
268 lines
6.5 KiB
C++
268 lines
6.5 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);
|
|
|
|
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->reactiontime = 0;
|
|
}
|
|
else if (abs(spit->vely) > abs(spit->velx))
|
|
{
|
|
spit->reactiontime = (targ->y - self->y) / spit->vely;
|
|
}
|
|
else
|
|
{
|
|
spit->reactiontime = (targ->x - self->x) / spit->velx;
|
|
}
|
|
// [GZ] Calculates when the projectile will have reached destination
|
|
spit->reactiontime += 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 *targ;
|
|
int r;
|
|
|
|
// [GZ] Should be more fiable than a countdown...
|
|
if (self->reactiontime > level.maptime)
|
|
return; // still flying
|
|
|
|
targ = self->target;
|
|
|
|
|
|
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;
|
|
|
|
if (self->master != NULL)
|
|
{
|
|
FDropItem *di; // di will be our drop item list iterator
|
|
FDropItem *drop; // while drop stays as the reference point.
|
|
int n=0;
|
|
|
|
drop = di = self->master->GetDropItems();
|
|
if (di != NULL)
|
|
{
|
|
while (di != NULL)
|
|
{
|
|
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 = di->Next;
|
|
}
|
|
}
|
|
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 > -1)) di = di->Next; else n = -1;
|
|
}
|
|
}
|
|
|
|
SpawnName = di->Name;
|
|
}
|
|
}
|
|
if (SpawnName == NAME_None)
|
|
{
|
|
const char *type;
|
|
// Randomly select monster to spawn.
|
|
r = pr_spawnfly ();
|
|
|
|
// Probability distribution (kind of :),
|
|
// decreasing likelihood.
|
|
if (r < 50) type = "DoomImp";
|
|
else if (r < 90) type = "Demon";
|
|
else if (r < 120) type = "Spectre";
|
|
else if (r < 130) type = "PainElemental";
|
|
else if (r < 160) type = "Cacodemon";
|
|
else if (r < 162) type = "Archvile";
|
|
else if (r < 172) type = "Revenant";
|
|
else if (r < 192) type = "Arachnotron";
|
|
else if (r < 222) type = "Fatso";
|
|
else if (r < 246) type = "HellKnight";
|
|
else type = "BaronOfHell";
|
|
|
|
SpawnName = type;
|
|
}
|
|
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
|
|
AActor *eye = self->target;
|
|
|
|
if (eye != NULL)
|
|
{
|
|
newmobj->CopyFriendliness (eye, false);
|
|
}
|
|
if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true))
|
|
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");
|
|
}
|