2006-02-24 04:48:15 +00:00
|
|
|
#include "actor.h"
|
|
|
|
#include "info.h"
|
|
|
|
#include "m_random.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "p_enemy.h"
|
|
|
|
#include "ravenshared.h"
|
|
|
|
#include "a_action.h"
|
|
|
|
#include "gi.h"
|
|
|
|
#include "w_wad.h"
|
2008-08-10 20:48:55 +00:00
|
|
|
#include "thingdef/thingdef.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
#define MAULATORTICS (25*35)
|
|
|
|
|
|
|
|
static FRandom pr_minotauratk1 ("MinotaurAtk1");
|
|
|
|
static FRandom pr_minotaurdecide ("MinotaurDecide");
|
|
|
|
static FRandom pr_atk ("MinotaurAtk2");
|
|
|
|
static FRandom pr_minotauratk3 ("MinotaurAtk3");
|
|
|
|
static FRandom pr_fire ("MntrFloorFire");
|
|
|
|
static FRandom pr_minotaurslam ("MinotaurSlam");
|
|
|
|
static FRandom pr_minotaurroam ("MinotaurRoam");
|
|
|
|
static FRandom pr_minotaurchase ("MinotaurChase");
|
|
|
|
|
|
|
|
void P_MinotaurSlam (AActor *source, AActor *target);
|
|
|
|
|
2008-08-10 22:48:37 +00:00
|
|
|
DECLARE_ACTION(A_MinotaurLook)
|
|
|
|
|
2008-08-06 19:25:59 +00:00
|
|
|
IMPLEMENT_CLASS(AMinotaur)
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
void AMinotaur::Tick ()
|
|
|
|
{
|
|
|
|
Super::Tick ();
|
|
|
|
|
|
|
|
// The unfriendly Minotaur (Heretic's) is invulnerable while charging
|
2008-08-06 19:25:59 +00:00
|
|
|
if (!(flags5 & MF5_SUMMONEDMONSTER))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
// Get MF_SKULLFLY bit and shift it so it matches MF2_INVULNERABLE
|
|
|
|
DWORD flying = (flags & MF_SKULLFLY) << 3;
|
|
|
|
if ((flags2 & MF2_INVULNERABLE) != flying)
|
|
|
|
{
|
|
|
|
flags2 ^= MF2_INVULNERABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AMinotaur::Slam (AActor *thing)
|
|
|
|
{
|
|
|
|
// Slamming minotaurs shouldn't move non-creatures
|
|
|
|
if (!(thing->flags3&MF3_ISMONSTER) && !thing->player)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return Super::Slam (thing);
|
|
|
|
}
|
|
|
|
|
|
|
|
int AMinotaur::DoSpecialDamage (AActor *target, int damage)
|
|
|
|
{
|
|
|
|
damage = Super::DoSpecialDamage (target, damage);
|
|
|
|
if ((damage != -1) && (flags & MF_SKULLFLY))
|
|
|
|
{ // Slam only when in charge mode
|
|
|
|
P_MinotaurSlam (this, target);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return damage;
|
|
|
|
}
|
|
|
|
|
2006-04-23 20:12:27 +00:00
|
|
|
// Minotaur Friend ----------------------------------------------------------
|
|
|
|
|
2008-08-06 19:25:59 +00:00
|
|
|
IMPLEMENT_CLASS(AMinotaurFriend)
|
2006-04-23 20:12:27 +00:00
|
|
|
|
|
|
|
void AMinotaurFriend::BeginPlay ()
|
|
|
|
{
|
|
|
|
Super::BeginPlay ();
|
|
|
|
StartTime = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AMinotaurFriend::Serialize (FArchive &arc)
|
|
|
|
{
|
|
|
|
Super::Serialize (arc);
|
|
|
|
arc << StartTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AMinotaurFriend::IsOkayToAttack (AActor *link)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if ((link->flags3&MF3_ISMONSTER) && (link != tracer))
|
|
|
|
{
|
|
|
|
if (!((link->flags ^ flags) & MF_FRIENDLY))
|
|
|
|
{ // Don't attack friends
|
|
|
|
if (link->flags & MF_FRIENDLY)
|
|
|
|
{
|
|
|
|
if (!deathmatch || link->FriendPlayer == 0 || FriendPlayer == 0 ||
|
|
|
|
link->FriendPlayer != FriendPlayer)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!(link->flags&MF_SHOOTABLE))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (link->flags2&MF2_DORMANT)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-06 19:25:59 +00:00
|
|
|
if ((link->flags5 & MF5_SUMMONEDMONSTER) && (link->tracer == tracer))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (multiplayer && !deathmatch && link->player)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (P_CheckSight (this, link))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-04-23 20:12:27 +00:00
|
|
|
void AMinotaurFriend::Die (AActor *source, AActor *inflictor)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
Super::Die (source, inflictor);
|
|
|
|
|
|
|
|
if (tracer && tracer->health > 0 && tracer->player)
|
|
|
|
{
|
|
|
|
// Search thinker list for minotaur
|
2006-04-23 20:12:27 +00:00
|
|
|
TThinkerIterator<AMinotaurFriend> iterator;
|
|
|
|
AMinotaurFriend *mo;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
while ((mo = iterator.Next()) != NULL)
|
|
|
|
{
|
|
|
|
if (mo->health <= 0) continue;
|
|
|
|
// [RH] Minotaurs can't be morphed, so this isn't needed
|
|
|
|
//if (!(mo->flags&MF_COUNTKILL)) continue; // for morphed minotaurs
|
|
|
|
if (mo->flags&MF_CORPSE) continue;
|
|
|
|
if (mo->StartTime >= 0 && (level.maptime - StartTime) >= MAULATORTICS) continue;
|
|
|
|
if (mo->tracer != NULL && mo->tracer->player == tracer->player) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mo == NULL)
|
|
|
|
{
|
2008-08-06 19:25:59 +00:00
|
|
|
AInventory *power = tracer->FindInventory (PClass::FindClass("PowerMinotaur"));
|
2006-02-24 04:48:15 +00:00
|
|
|
if (power != NULL)
|
|
|
|
{
|
|
|
|
power->Destroy ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-23 20:12:27 +00:00
|
|
|
bool AMinotaurFriend::OkayToSwitchTarget (AActor *other)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-04-23 20:12:27 +00:00
|
|
|
if (other == tracer) return false; // Do not target the master
|
|
|
|
return Super::OkayToSwitchTarget (other);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Action functions for the minotaur ----------------------------------------
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurAtk1
|
|
|
|
//
|
|
|
|
// Melee attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurDeath)
|
2008-08-06 19:25:59 +00:00
|
|
|
{
|
|
|
|
if (Wads.CheckNumForName ("MNTRF1", ns_sprites) < 0 &&
|
|
|
|
Wads.CheckNumForName ("MNTRF0", ns_sprites) < 0)
|
2008-08-10 20:48:55 +00:00
|
|
|
self->SetState(self->FindState ("FadeOut"));
|
2008-08-06 19:25:59 +00:00
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk1)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
player_t *player;
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->target)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
S_Sound (self, CHAN_WEAPON, "minotaur/melee", 1, ATTN_NORM);
|
|
|
|
if (self->CheckMeleeRange())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int damage = pr_minotauratk1.HitDice (4);
|
2008-08-10 20:48:55 +00:00
|
|
|
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
|
|
|
|
P_TraceBleed (damage, self->target, self);
|
|
|
|
if ((player = self->target->player) != NULL &&
|
|
|
|
player->mo == self->target)
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // Squish the player
|
|
|
|
player->deltaviewheight = -16*FRACUNIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurDecide
|
|
|
|
//
|
|
|
|
// Choose a missile attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#define MNTR_CHARGE_SPEED (13*FRACUNIT)
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurDecide)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER);
|
2006-02-24 04:48:15 +00:00
|
|
|
angle_t angle;
|
|
|
|
AActor *target;
|
|
|
|
int dist;
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
target = self->target;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (!target)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!friendly)
|
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
S_Sound (self, CHAN_WEAPON, "minotaur/sight", 1, ATTN_NORM);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
dist = P_AproxDistance (self->x-target->x, self->y-target->y);
|
|
|
|
if (target->z+target->height > self->z
|
|
|
|
&& target->z+target->height < self->z+self->height
|
2006-02-24 04:48:15 +00:00
|
|
|
&& dist < (friendly ? 16*64*FRACUNIT : 8*64*FRACUNIT)
|
|
|
|
&& dist > 1*64*FRACUNIT
|
|
|
|
&& pr_minotaurdecide() < 150)
|
|
|
|
{ // Charge attack
|
|
|
|
// Don't call the state function right away
|
2008-08-10 20:48:55 +00:00
|
|
|
self->SetStateNF (self->FindState ("Charge"));
|
|
|
|
self->flags |= MF_SKULLFLY;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (!friendly)
|
|
|
|
{ // Heretic's Minotaur is invulnerable during charge attack
|
2008-08-10 20:48:55 +00:00
|
|
|
self->flags2 |= MF2_INVULNERABLE;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
A_FaceTarget (self);
|
|
|
|
angle = self->angle>>ANGLETOFINESHIFT;
|
|
|
|
self->momx = FixedMul (MNTR_CHARGE_SPEED, finecosine[angle]);
|
|
|
|
self->momy = FixedMul (MNTR_CHARGE_SPEED, finesine[angle]);
|
|
|
|
self->special1 = TICRATE/2; // Charge duration
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else if (target->z == target->floorz
|
|
|
|
&& dist < 9*64*FRACUNIT
|
|
|
|
&& pr_minotaurdecide() < (friendly ? 100 : 220))
|
|
|
|
{ // Floor fire attack
|
2008-08-10 20:48:55 +00:00
|
|
|
self->SetState (self->FindState ("Hammer"));
|
|
|
|
self->special2 = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Swing attack
|
2008-08-10 20:48:55 +00:00
|
|
|
A_FaceTarget (self);
|
2006-02-24 04:48:15 +00:00
|
|
|
// Don't need to call P_SetMobjState because the current state
|
|
|
|
// falls through to the swing attack
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurCharge
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurCharge)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AActor *puff;
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->target) return;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self->special1 > 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
const PClass *type;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (gameinfo.gametype == GAME_Heretic)
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
type = PClass::FindClass ("PhoenixPuff");
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-05-10 02:40:43 +00:00
|
|
|
type = PClass::FindClass ("PunchPuff");
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
puff = Spawn (type, self->x, self->y, self->z, ALLOW_REPLACE);
|
2006-02-24 04:48:15 +00:00
|
|
|
puff->momz = 2*FRACUNIT;
|
2008-08-10 20:48:55 +00:00
|
|
|
self->special1--;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
self->flags &= ~MF_SKULLFLY;
|
|
|
|
self->flags2 &= ~MF2_INVULNERABLE;
|
|
|
|
self->SetState (self->SeeState);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurAtk2
|
|
|
|
//
|
|
|
|
// Swing attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk2)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AActor *mo;
|
|
|
|
angle_t angle;
|
|
|
|
fixed_t momz;
|
|
|
|
fixed_t z;
|
2008-08-10 20:48:55 +00:00
|
|
|
bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->target)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
S_Sound (self, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM);
|
|
|
|
if (self->CheckMeleeRange())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int damage;
|
2006-04-23 20:12:27 +00:00
|
|
|
damage = pr_atk.HitDice (friendly ? 3 : 5);
|
2008-08-10 20:48:55 +00:00
|
|
|
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
|
|
|
|
P_TraceBleed (damage, self->target, self);
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
z = self->z + 40*FRACUNIT;
|
2008-08-04 19:25:13 +00:00
|
|
|
const PClass *fx = PClass::FindClass("MinotaurFX1");
|
|
|
|
if (fx)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
mo = P_SpawnMissileZ (self, z, self->target, fx);
|
2008-08-04 19:25:13 +00:00
|
|
|
if (mo != NULL)
|
|
|
|
{
|
|
|
|
// S_Sound (mo, CHAN_WEAPON, "minotaur/attack2", 1, ATTN_NORM);
|
|
|
|
momz = mo->momz;
|
|
|
|
angle = mo->angle;
|
2008-08-10 20:48:55 +00:00
|
|
|
P_SpawnMissileAngleZ (self, z, fx, angle-(ANG45/8), momz);
|
|
|
|
P_SpawnMissileAngleZ (self, z, fx, angle+(ANG45/8), momz);
|
|
|
|
P_SpawnMissileAngleZ (self, z, fx, angle-(ANG45/16), momz);
|
|
|
|
P_SpawnMissileAngleZ (self, z, fx, angle+(ANG45/16), momz);
|
2008-08-04 19:25:13 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurAtk3
|
|
|
|
//
|
|
|
|
// Floor fire attack.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurAtk3)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AActor *mo;
|
|
|
|
player_t *player;
|
2008-08-10 20:48:55 +00:00
|
|
|
bool friendly = !!(self->flags5 & MF5_SUMMONEDMONSTER);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->target)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
S_Sound (self, CHAN_VOICE, "minotaur/attack3", 1, ATTN_NORM);
|
|
|
|
if (self->CheckMeleeRange())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int damage;
|
|
|
|
|
2006-04-23 20:12:27 +00:00
|
|
|
damage = pr_minotauratk3.HitDice (friendly ? 3 : 5);
|
2008-08-10 20:48:55 +00:00
|
|
|
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
|
|
|
|
P_TraceBleed (damage, self->target, self);
|
|
|
|
if ((player = self->target->player) != NULL &&
|
|
|
|
player->mo == self->target)
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // Squish the player
|
|
|
|
player->deltaviewheight = -16*FRACUNIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
mo = P_SpawnMissile (self, self->target, PClass::FindClass("MinotaurFX2"));
|
2006-02-24 04:48:15 +00:00
|
|
|
if (mo != NULL)
|
|
|
|
{
|
|
|
|
S_Sound (mo, CHAN_WEAPON, "minotaur/attack1", 1, ATTN_NORM);
|
|
|
|
}
|
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
if (pr_minotauratk3() < 192 && self->special2 == 0)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-11 10:02:24 +00:00
|
|
|
self->SetState (self->FindState ("HammerLoop"));
|
2008-08-10 20:48:55 +00:00
|
|
|
self->special2 = 1;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MntrFloorFire
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MntrFloorFire)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
AActor *mo;
|
|
|
|
fixed_t x, y;
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
self->z = self->floorz;
|
|
|
|
x = self->x + (pr_fire.Random2 () << 10);
|
|
|
|
y = self->y + (pr_fire.Random2 () << 10);
|
2008-08-04 19:25:13 +00:00
|
|
|
mo = Spawn("MinotaurFX3", x, y, ONFLOORZ, ALLOW_REPLACE);
|
2008-08-10 20:48:55 +00:00
|
|
|
mo->target = self->target;
|
2006-02-24 04:48:15 +00:00
|
|
|
mo->momx = 1; // Force block checking
|
|
|
|
P_CheckMissileSpawn (mo);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// FUNC P_MinotaurSlam
|
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void P_MinotaurSlam (AActor *source, AActor *target)
|
|
|
|
{
|
|
|
|
angle_t angle;
|
|
|
|
fixed_t thrust;
|
|
|
|
int damage;
|
|
|
|
|
|
|
|
angle = R_PointToAngle2 (source->x, source->y, target->x, target->y);
|
|
|
|
angle >>= ANGLETOFINESHIFT;
|
|
|
|
thrust = 16*FRACUNIT+(pr_minotaurslam()<<10);
|
|
|
|
target->momx += FixedMul (thrust, finecosine[angle]);
|
|
|
|
target->momy += FixedMul (thrust, finesine[angle]);
|
|
|
|
damage = pr_minotaurslam.HitDice (static_cast<AMinotaur *>(source) ? 4 : 6);
|
2006-10-31 14:53:21 +00:00
|
|
|
P_DamageMobj (target, NULL, NULL, damage, NAME_Melee);
|
2006-02-24 04:48:15 +00:00
|
|
|
P_TraceBleed (damage, target, angle, 0);
|
|
|
|
if (target->player)
|
|
|
|
{
|
|
|
|
target->reactiontime = 14+(pr_minotaurslam()&7);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Minotaur variables
|
|
|
|
//
|
|
|
|
// special1 charge duration countdown
|
|
|
|
// special2 internal to minotaur AI
|
|
|
|
// StartTime minotaur start time
|
|
|
|
// tracer pointer to player that spawned it
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// A_MinotaurRoam
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurRoam)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
AMinotaurFriend *self1 = static_cast<AMinotaurFriend *> (self);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// In case pain caused him to skip his fade in.
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->RenderStyle = STYLE_Normal;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self1->StartTime >= 0 && (level.maptime - self1->StartTime) >= MAULATORTICS)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
P_DamageMobj (self1, NULL, NULL, 1000000, NAME_None);
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pr_minotaurroam() < 30)
|
2008-08-10 22:48:37 +00:00
|
|
|
CALL_ACTION(A_MinotaurLook, self1); // adjust to closest target
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (pr_minotaurroam() < 6)
|
|
|
|
{
|
|
|
|
//Choose new direction
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->movedir = pr_minotaurroam() % 8;
|
|
|
|
FaceMovementDirection (self1);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!P_Move(self1))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
// Turn
|
|
|
|
if (pr_minotaurroam() & 1)
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->movedir = (++self1->movedir)%8;
|
2006-02-24 04:48:15 +00:00
|
|
|
else
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->movedir = (self1->movedir+7)%8;
|
|
|
|
FaceMovementDirection (self1);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// PROC A_MinotaurLook
|
|
|
|
//
|
|
|
|
// Look for enemy of player
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define MINOTAUR_LOOK_DIST (16*54*FRACUNIT)
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurLook)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend)))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 22:48:37 +00:00
|
|
|
CALL_ACTION(A_Look, self);
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AActor *mo = NULL;
|
|
|
|
player_t *player;
|
|
|
|
fixed_t dist;
|
|
|
|
int i;
|
2008-08-10 20:48:55 +00:00
|
|
|
AActor *master = self->tracer;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
self->target = NULL;
|
2006-02-24 04:48:15 +00:00
|
|
|
if (deathmatch) // Quick search for players
|
|
|
|
{
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
if (!playeringame[i]) continue;
|
|
|
|
player = &players[i];
|
|
|
|
mo = player->mo;
|
|
|
|
if (mo == master) continue;
|
|
|
|
if (mo->health <= 0) continue;
|
2008-08-10 20:48:55 +00:00
|
|
|
dist = P_AproxDistance(self->x - mo->x, self->y - mo->y);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (dist > MINOTAUR_LOOK_DIST) continue;
|
2008-08-10 20:48:55 +00:00
|
|
|
self->target = mo;
|
2006-02-24 04:48:15 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->target) // Near player monster search
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (master && (master->health>0) && (master->player))
|
|
|
|
mo = P_RoughMonsterSearch(master, 20);
|
|
|
|
else
|
2008-08-10 20:48:55 +00:00
|
|
|
mo = P_RoughMonsterSearch(self, 20);
|
|
|
|
self->target = mo;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->target) // Normal monster search
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
FActorIterator iterator (0);
|
|
|
|
|
|
|
|
while ((mo = iterator.Next()) != NULL)
|
|
|
|
{
|
|
|
|
if (!(mo->flags3&MF3_ISMONSTER)) continue;
|
|
|
|
if (mo->health <= 0) continue;
|
|
|
|
if (!(mo->flags&MF_SHOOTABLE)) continue;
|
2008-08-10 20:48:55 +00:00
|
|
|
dist = P_AproxDistance (self->x - mo->x, self->y - mo->y);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (dist > MINOTAUR_LOOK_DIST) continue;
|
2008-08-10 20:48:55 +00:00
|
|
|
if ((mo == master) || (mo == self)) continue;
|
2008-08-06 19:25:59 +00:00
|
|
|
if ((mo->flags5 & MF5_SUMMONEDMONSTER) && (mo->tracer == master)) continue;
|
2008-08-10 20:48:55 +00:00
|
|
|
self->target = mo;
|
2008-08-11 10:02:24 +00:00
|
|
|
break; // Found actor to attack
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self->target)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
self->SetStateNF (self->SeeState);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
self->SetStateNF (self->FindState ("Roam"));
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
DEFINE_ACTION_FUNCTION(AActor, A_MinotaurChase)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self->IsKindOf(RUNTIME_CLASS(AMinotaurFriend)))
|
2006-04-23 20:12:27 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
A_Chase (self);
|
2006-04-23 20:12:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
AMinotaurFriend *self1 = static_cast<AMinotaurFriend *> (self);
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// In case pain caused him to skip his fade in.
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->RenderStyle = STYLE_Normal;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self1->StartTime >= 0 && (level.maptime - self1->StartTime) >= MAULATORTICS)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
P_DamageMobj (self1, NULL, NULL, 1000000, NAME_None);
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pr_minotaurchase() < 30)
|
2008-08-10 22:48:37 +00:00
|
|
|
CALL_ACTION(A_MinotaurLook, self1); // adjust to closest target
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!self1->target || (self1->target->health <= 0) ||
|
|
|
|
!(self1->target->flags&MF_SHOOTABLE))
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // look for a new target
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->SetState (self1->FindState ("Spawn"));
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-10 20:48:55 +00:00
|
|
|
FaceMovementDirection (self1);
|
|
|
|
self1->reactiontime = 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
// Melee attack
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self1->MeleeState && self1->CheckMeleeRange ())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self1->AttackSound)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
S_Sound (self1, CHAN_WEAPON, self1->AttackSound, 1, ATTN_NORM);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->SetState (self1->MeleeState);
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Missile attack
|
2008-08-10 20:48:55 +00:00
|
|
|
if (self1->MissileState && P_CheckMissileRange(self1))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->SetState (self1->MissileState);
|
2006-02-24 04:48:15 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// chase towards target
|
2008-08-10 20:48:55 +00:00
|
|
|
if (!P_Move (self1))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
P_NewChaseDir (self1);
|
|
|
|
FaceMovementDirection (self1);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Active sound
|
|
|
|
if (pr_minotaurchase() < 6)
|
|
|
|
{
|
2008-08-10 20:48:55 +00:00
|
|
|
self1->PlayActiveSound ();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|