mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-15 07:01:10 +00:00
298 lines
8.9 KiB
C++
298 lines
8.9 KiB
C++
|
#include "actor.h"
|
||
|
#include "m_random.h"
|
||
|
#include "a_action.h"
|
||
|
#include "p_local.h"
|
||
|
#include "p_enemy.h"
|
||
|
#include "s_sound.h"
|
||
|
#include "a_strifeglobal.h"
|
||
|
#include "f_finale.h"
|
||
|
|
||
|
static FRandom pr_prog ("Programmer");
|
||
|
|
||
|
void A_SentinelBob (AActor *);
|
||
|
void A_TossGib (AActor *);
|
||
|
void A_Countdown (AActor *);
|
||
|
|
||
|
void A_ProgrammerMelee (AActor *);
|
||
|
void A_SpotLightning (AActor *);
|
||
|
void A_SpawnProgrammerBase (AActor *);
|
||
|
void A_ProgrammerDeath (AActor *);
|
||
|
|
||
|
// Programmer ---------------------------------------------------------------
|
||
|
|
||
|
class AProgrammer : public AActor
|
||
|
{
|
||
|
DECLARE_ACTOR (AProgrammer, AActor)
|
||
|
public:
|
||
|
void NoBlockingSet ();
|
||
|
};
|
||
|
|
||
|
FState AProgrammer::States[] =
|
||
|
{
|
||
|
#define S_PROG_STND 0
|
||
|
S_NORMAL (PRGR, 'A', 5, A_Look, &States[S_PROG_STND+1]),
|
||
|
S_NORMAL (PRGR, 'A', 1, A_SentinelBob, &States[S_PROG_STND]),
|
||
|
|
||
|
#define S_PROG_CHASE (S_PROG_STND+2)
|
||
|
S_NORMAL (PRGR, 'A', 160, A_SentinelBob, &States[S_PROG_CHASE+1]),
|
||
|
S_NORMAL (PRGR, 'B', 5, A_SentinelBob, &States[S_PROG_CHASE+2]),
|
||
|
S_NORMAL (PRGR, 'C', 5, A_SentinelBob, &States[S_PROG_CHASE+3]),
|
||
|
S_NORMAL (PRGR, 'D', 5, A_SentinelBob, &States[S_PROG_CHASE+4]),
|
||
|
S_NORMAL (PRGR, 'E', 2, A_SentinelBob, &States[S_PROG_CHASE+5]),
|
||
|
S_NORMAL (PRGR, 'F', 2, A_SentinelBob, &States[S_PROG_CHASE+6]),
|
||
|
S_NORMAL (PRGR, 'E', 3, A_Chase, &States[S_PROG_CHASE+7]),
|
||
|
S_NORMAL (PRGR, 'F', 3, A_Chase, &States[S_PROG_CHASE+4]),
|
||
|
|
||
|
#define S_PROG_MELEE (S_PROG_CHASE+8)
|
||
|
S_NORMAL (PRGR, 'E', 2, A_SentinelBob, &States[S_PROG_MELEE+1]),
|
||
|
S_NORMAL (PRGR, 'F', 3, A_SentinelBob, &States[S_PROG_MELEE+2]),
|
||
|
S_NORMAL (PRGR, 'E', 3, A_FaceTarget, &States[S_PROG_MELEE+4]),
|
||
|
S_NORMAL (PRGR, 'F', 4, A_ProgrammerMelee, &States[S_PROG_CHASE+4]),
|
||
|
|
||
|
#define S_PROG_MISSILE (S_PROG_MELEE+4)
|
||
|
S_NORMAL (PRGR, 'G', 5, A_FaceTarget, &States[S_PROG_MISSILE+1]),
|
||
|
S_NORMAL (PRGR, 'H', 5, A_SentinelBob, &States[S_PROG_MISSILE+2]),
|
||
|
S_BRIGHT (PRGR, 'I', 5, A_FaceTarget, &States[S_PROG_MISSILE+3]),
|
||
|
S_BRIGHT (PRGR, 'J', 5, A_SpotLightning, &States[S_PROG_CHASE+4]),
|
||
|
|
||
|
#define S_PROG_PAIN (S_PROG_MISSILE+4)
|
||
|
S_NORMAL (PRGR, 'K', 5, A_Pain, &States[S_PROG_PAIN+1]),
|
||
|
S_NORMAL (PRGR, 'L', 5, A_SentinelBob, &States[S_PROG_CHASE+4]),
|
||
|
|
||
|
#define S_PROG_DIE (S_PROG_PAIN+2)
|
||
|
S_BRIGHT (PRGR, 'L', 7, A_TossGib, &States[S_PROG_DIE+1]),
|
||
|
S_BRIGHT (PRGR, 'M', 7, A_Scream, &States[S_PROG_DIE+2]),
|
||
|
S_BRIGHT (PRGR, 'N', 7, A_TossGib, &States[S_PROG_DIE+3]),
|
||
|
S_BRIGHT (PRGR, 'O', 7, A_NoBlocking, &States[S_PROG_DIE+4]),
|
||
|
S_BRIGHT (PRGR, 'P', 7, A_TossGib, &States[S_PROG_DIE+5]),
|
||
|
S_BRIGHT (PRGR, 'Q', 7, A_SpawnProgrammerBase,&States[S_PROG_DIE+6]),
|
||
|
S_BRIGHT (PRGR, 'R', 7, NULL, &States[S_PROG_DIE+7]),
|
||
|
S_BRIGHT (PRGR, 'S', 6, NULL, &States[S_PROG_DIE+8]),
|
||
|
S_BRIGHT (PRGR, 'T', 5, NULL, &States[S_PROG_DIE+9]),
|
||
|
S_BRIGHT (PRGR, 'U', 5, NULL, &States[S_PROG_DIE+10]),
|
||
|
S_BRIGHT (PRGR, 'V', 5, NULL, &States[S_PROG_DIE+11]),
|
||
|
S_BRIGHT (PRGR, 'W', 5, NULL, &States[S_PROG_DIE+12]),
|
||
|
S_BRIGHT (PRGR, 'X', 32, NULL, &States[S_PROG_DIE+13]),
|
||
|
S_BRIGHT (PRGR, 'X', -1, A_ProgrammerDeath, NULL)
|
||
|
};
|
||
|
|
||
|
IMPLEMENT_ACTOR (AProgrammer, Strife, 71, 0)
|
||
|
PROP_StrifeType (95)
|
||
|
PROP_SpawnHealth (1100)
|
||
|
PROP_SpawnState (S_PROG_STND)
|
||
|
PROP_SeeState (S_PROG_CHASE)
|
||
|
PROP_PainState (S_PROG_PAIN)
|
||
|
PROP_PainChance (50)
|
||
|
PROP_MeleeState (S_PROG_MELEE)
|
||
|
PROP_MissileState (S_PROG_MISSILE)
|
||
|
PROP_DeathState (S_PROG_DIE)
|
||
|
PROP_SpeedFixed (26)
|
||
|
PROP_RadiusFixed (45)
|
||
|
PROP_HeightFixed (60)
|
||
|
PROP_Mass (800)
|
||
|
PROP_Damage (4)
|
||
|
PROP_Flags (MF_SOLID|MF_SHOOTABLE|MF_NOGRAVITY|MF_FLOAT|
|
||
|
MF_NOBLOOD|MF_COUNTKILL|MF_NOTDMATCH)
|
||
|
PROP_Flags2 (MF2_PASSMOBJ|MF2_PUSHWALL|MF2_MCROSS)
|
||
|
PROP_Flags3 (MF3_DONTMORPH|MF3_NOBLOCKMONST)
|
||
|
PROP_Flags4 (MF4_LOOKALLAROUND|MF4_FIRERESIST|MF4_NOICEDEATH|MF4_NOTARGETSWITCH)
|
||
|
PROP_MinMissileChance (150)
|
||
|
PROP_AttackSound ("programmer/attack")
|
||
|
PROP_PainSound ("programmer/pain")
|
||
|
PROP_DeathSound ("programmer/death")
|
||
|
PROP_ActiveSound ("programmer/active")
|
||
|
END_DEFAULTS
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// AProgrammer :: NoBlockingSet
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void AProgrammer::NoBlockingSet ()
|
||
|
{
|
||
|
P_DropItem (this, "Sigil1", -1, 256);
|
||
|
}
|
||
|
|
||
|
// The Programmer's base for when he dies -----------------------------------
|
||
|
|
||
|
class AProgrammerBase : public AActor
|
||
|
{
|
||
|
DECLARE_ACTOR (AProgrammerBase, AActor)
|
||
|
public:
|
||
|
void GetExplodeParms (int &damage, int &dist, bool &hurtSource);
|
||
|
};
|
||
|
|
||
|
FState AProgrammerBase::States[] =
|
||
|
{
|
||
|
S_BRIGHT (BASE, 'A', 5, A_ExplodeAndAlert, &States[1]),
|
||
|
S_BRIGHT (BASE, 'B', 5, NULL, &States[2]),
|
||
|
S_BRIGHT (BASE, 'C', 5, NULL, &States[3]),
|
||
|
S_BRIGHT (BASE, 'D', 5, NULL, &States[4]),
|
||
|
S_NORMAL (BASE, 'E', 5, NULL, &States[5]),
|
||
|
S_NORMAL (BASE, 'F', 5, NULL, &States[6]),
|
||
|
S_NORMAL (BASE, 'G', 5, NULL, &States[7]),
|
||
|
S_NORMAL (BASE, 'H', -1, NULL, NULL)
|
||
|
};
|
||
|
|
||
|
IMPLEMENT_ACTOR (AProgrammerBase, Strife, -1, 0)
|
||
|
PROP_StrifeType (96)
|
||
|
PROP_SpawnState (0)
|
||
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOCLIP|MF_NOBLOOD)
|
||
|
END_DEFAULTS
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// AProgrammerBase :: GetExplodeParms
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void AProgrammerBase::GetExplodeParms (int &damage, int &dist, bool &hurtSource)
|
||
|
{
|
||
|
damage = dist = 32;
|
||
|
}
|
||
|
|
||
|
// The Programmer level ending thing ----------------------------------------
|
||
|
// [RH] I took some liberties to make this "cooler" than it was in Strife.
|
||
|
|
||
|
class AProgLevelEnder : public AInventory
|
||
|
{
|
||
|
DECLARE_STATELESS_ACTOR (AProgLevelEnder, AInventory)
|
||
|
public:
|
||
|
void Tick ();
|
||
|
PalEntry GetBlend ();
|
||
|
};
|
||
|
|
||
|
IMPLEMENT_STATELESS_ACTOR (AProgLevelEnder, Strife, -1, 0)
|
||
|
PROP_Inventory_FlagsSet (IF_UNDROPPABLE)
|
||
|
END_DEFAULTS
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// AProgLevelEnder :: Tick
|
||
|
//
|
||
|
// Fade to black, end the level, then unfade.
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void AProgLevelEnder::Tick ()
|
||
|
{
|
||
|
if (special2 == 0)
|
||
|
{ // fade out over .66 second
|
||
|
special1 += 255 / (TICRATE*2/3);
|
||
|
if (++special1 >= 255)
|
||
|
{
|
||
|
special1 = 255;
|
||
|
special2 = 1;
|
||
|
G_ExitLevel (0, false);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{ // fade in over two seconds
|
||
|
special1 -= 255 / (TICRATE*2);
|
||
|
if (special1 <= 0)
|
||
|
{
|
||
|
Destroy ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// AProgLevelEnder :: GetBlend
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
PalEntry AProgLevelEnder::GetBlend ()
|
||
|
{
|
||
|
return PalEntry (special1, 0, 0, 0);
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_ProgrammerMelee
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_ProgrammerMelee (AActor *self)
|
||
|
{
|
||
|
int damage;
|
||
|
|
||
|
if (self->target == NULL)
|
||
|
return;
|
||
|
|
||
|
A_FaceTarget (self);
|
||
|
|
||
|
if (!self->CheckMeleeRange ())
|
||
|
return;
|
||
|
|
||
|
S_Sound (self, CHAN_WEAPON, "programmer/clank", 1, ATTN_NORM);
|
||
|
|
||
|
damage = ((pr_prog() % 10) + 1) * 6;
|
||
|
P_DamageMobj (self->target, self, self, damage, MOD_HIT);
|
||
|
P_TraceBleed (damage, self->target, self);
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_SpotLightning
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_SpotLightning (AActor *self)
|
||
|
{
|
||
|
AActor *spot;
|
||
|
|
||
|
if (self->target == NULL)
|
||
|
return;
|
||
|
|
||
|
spot = Spawn<ASpectralLightningSpot> (self->target->x, self->target->y, ONFLOORZ);
|
||
|
if (spot != NULL)
|
||
|
{
|
||
|
spot->threshold = 25;
|
||
|
spot->target = self;
|
||
|
spot->health = -2;
|
||
|
spot->tracer = self->target;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_SpawnProgrammerBase
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_SpawnProgrammerBase (AActor *self)
|
||
|
{
|
||
|
AActor *foo = Spawn<AProgrammerBase> (self->x, self->y, self->z + 24*FRACUNIT);
|
||
|
if (foo != NULL)
|
||
|
{
|
||
|
foo->angle = self->angle + ANGLE_180 + (pr_prog.Random2() << 22);
|
||
|
foo->momx = FixedMul (foo->Speed, finecosine[foo->angle >> ANGLETOFINESHIFT]);
|
||
|
foo->momy = FixedMul (foo->Speed, finesine[foo->angle >> ANGLETOFINESHIFT]);
|
||
|
foo->momz = pr_prog() << 9;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
//
|
||
|
// A_ProgrammerDeath
|
||
|
//
|
||
|
//============================================================================
|
||
|
|
||
|
void A_ProgrammerDeath (AActor *self)
|
||
|
{
|
||
|
if (!CheckBossDeath (self))
|
||
|
return;
|
||
|
|
||
|
for (int i = 0; i < MAXPLAYERS; ++i)
|
||
|
{
|
||
|
if (playeringame[i] && players[i].health > 0)
|
||
|
{
|
||
|
players[i].mo->GiveInventoryType (RUNTIME_CLASS(AProgLevelEnder));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
P_StartScript (self, NULL, 250, NULL, 0, 0, 0, 0, false, false);
|
||
|
}
|