gzdoom/src/g_heretic/a_chicken.cpp

539 lines
15 KiB
C++
Raw Normal View History

#include "actor.h"
#include "gi.h"
#include "m_random.h"
#include "s_sound.h"
#include "d_player.h"
#include "a_action.h"
#include "a_pickups.h"
#include "p_local.h"
#include "a_hereticglobal.h"
#include "a_sharedglobal.h"
#include "p_enemy.h"
#include "d_event.h"
#include "ravenshared.h"
#include "gstrings.h"
static FRandom pr_chickenplayerthink ("ChickenPlayerThink");
static FRandom pr_chicattack ("ChicAttack");
static FRandom pr_feathers ("Feathers");
static FRandom pr_beakatkpl1 ("BeakAtkPL1");
static FRandom pr_beakatkpl2 ("BeakAtkPL2");
void A_BeakRaise (AActor *);
void A_BeakAttackPL1 (AActor *);
void A_BeakAttackPL2 (AActor *);
void A_Feathers (AActor *);
void A_ChicLook (AActor *);
void A_ChicChase (AActor *);
void A_ChicPain (AActor *);
void A_ChicAttack (AActor *);
void P_UpdateBeak (AActor *);
// Beak puff ----------------------------------------------------------------
class ABeakPuff : public AStaffPuff
{
DECLARE_STATELESS_ACTOR (ABeakPuff, AStaffPuff)
public:
void BeginPlay ();
};
IMPLEMENT_STATELESS_ACTOR (ABeakPuff, Heretic, -1, 0)
PROP_Mass (5)
PROP_RenderStyle (STYLE_Translucent)
PROP_Alpha (HR_SHADOW)
PROP_AttackSound ("chicken/attack")
END_DEFAULTS
void ABeakPuff::BeginPlay ()
{
Super::BeginPlay ();
momz = FRACUNIT;
}
// Beak ---------------------------------------------------------------------
class ABeak : public AWeapon
{
DECLARE_ACTOR (ABeak, AWeapon)
};
class ABeakPowered : public ABeak
{
DECLARE_STATELESS_ACTOR (ABeakPowered, ABeak)
};
FState ABeak::States[] =
{
#define S_BEAKREADY 0
S_NORMAL (BEAK, 'A', 1, A_WeaponReady , &States[S_BEAKREADY]),
#define S_BEAKDOWN (S_BEAKREADY+1)
S_NORMAL (BEAK, 'A', 1, A_Lower , &States[S_BEAKDOWN]),
#define S_BEAKUP (S_BEAKDOWN+1)
S_NORMAL (BEAK, 'A', 1, A_BeakRaise , &States[S_BEAKUP]),
#define S_BEAKATK1 (S_BEAKUP+1)
S_NORMAL (BEAK, 'A', 18, A_BeakAttackPL1 , &States[S_BEAKREADY]),
#define S_BEAKATK2 (S_BEAKATK1+1)
S_NORMAL (BEAK, 'A', 12, A_BeakAttackPL2 , &States[S_BEAKREADY])
};
IMPLEMENT_ACTOR (ABeak, Heretic, -1, 0)
PROP_Weapon_SelectionOrder (10000)
PROP_Weapon_Flags (WIF_DONTBOB|WIF_BOT_MELEE)
PROP_Weapon_UpState (S_BEAKUP)
PROP_Weapon_DownState (S_BEAKDOWN)
PROP_Weapon_ReadyState (S_BEAKREADY)
PROP_Weapon_AtkState (S_BEAKATK1)
PROP_Weapon_HoldAtkState (S_BEAKATK1)
PROP_Weapon_YAdjust (15)
PROP_Weapon_SisterType ("BeakPowered")
END_DEFAULTS
IMPLEMENT_STATELESS_ACTOR (ABeakPowered, Heretic, -1, 0)
PROP_Weapon_Flags (WIF_DONTBOB|WIF_BOT_MELEE|WIF_POWERED_UP)
PROP_Weapon_AtkState (S_BEAKATK2)
PROP_Weapon_HoldAtkState (S_BEAKATK2)
PROP_Weapon_SisterType ("Beak")
END_DEFAULTS
// Chicken player -----------------------------------------------------------
class AChickenPlayer : public APlayerPawn
{
DECLARE_ACTOR (AChickenPlayer, APlayerPawn)
public:
void MorphPlayerThink ();
void ActivateMorphWeapon ();
};
FState AChickenPlayer::States[] =
{
#define S_CHICPLAY 0
S_NORMAL (CHKN, 'A', -1, NULL , NULL),
#define S_CHICPLAY_RUN (S_CHICPLAY+1)
S_NORMAL (CHKN, 'A', 3, NULL , &States[S_CHICPLAY_RUN+1]),
S_NORMAL (CHKN, 'B', 3, NULL , &States[S_CHICPLAY_RUN+2]),
S_NORMAL (CHKN, 'A', 3, NULL , &States[S_CHICPLAY_RUN+3]),
S_NORMAL (CHKN, 'B', 3, NULL , &States[S_CHICPLAY_RUN+0]),
#define S_CHICPLAY_ATK (S_CHICPLAY_RUN+4)
S_NORMAL (CHKN, 'C', 12, NULL , &States[S_CHICPLAY]),
#define S_CHICPLAY_PAIN (S_CHICPLAY_ATK+1)
S_NORMAL (CHKN, 'D', 4, A_Feathers , &States[S_CHICPLAY_PAIN+1]),
S_NORMAL (CHKN, 'C', 4, A_Pain , &States[S_CHICPLAY]),
#define S_CHICPLAY_DIE (S_CHICPLAY_PAIN+2)
S_NORMAL (CHKN, 'E', 6, A_Scream , &States[S_CHICPLAY_DIE+1]),
S_NORMAL (CHKN, 'F', 6, A_Feathers , &States[S_CHICPLAY_DIE+2]),
S_NORMAL (CHKN, 'G', 6, NULL , &States[S_CHICPLAY_DIE+3]),
S_NORMAL (CHKN, 'H', 6, A_NoBlocking , &States[S_CHICPLAY_DIE+4]),
S_NORMAL (CHKN, 'I', 6, NULL , &States[S_CHICPLAY_DIE+5]),
S_NORMAL (CHKN, 'J', 6, NULL , &States[S_CHICPLAY_DIE+6]),
S_NORMAL (CHKN, 'K', 6, NULL , &States[S_CHICPLAY_DIE+7]),
S_NORMAL (CHKN, 'L', -1, NULL , NULL)
};
IMPLEMENT_ACTOR (AChickenPlayer, Heretic, -1, 0)
PROP_SpawnHealth (30)
PROP_RadiusFixed (16)
PROP_HeightFixed (24)
PROP_PainChance (255)
PROP_SpeedFixed (1)
PROP_Flags (MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_NOTDMATCH|MF_FRIENDLY)
PROP_Flags2 (MF2_WINDTHRUST|MF2_SLIDE|MF2_PASSMOBJ|MF2_PUSHWALL|MF2_FLOORCLIP|MF2_LOGRAV|MF2_TELESTOMP)
PROP_Flags3 (MF3_NOBLOCKMONST)
PROP_Flags4 (MF4_NOSKIN)
PROP_SpawnState (S_CHICPLAY)
PROP_SeeState (S_CHICPLAY_RUN)
PROP_PainState (S_CHICPLAY_PAIN)
PROP_MissileState (S_CHICPLAY_ATK)
PROP_DeathState (S_CHICPLAY_DIE)
// [GRB]
PROP_PlayerPawn_JumpZ (FRACUNIT)
PROP_PlayerPawn_ViewHeight (21*FRACUNIT)
PROP_PlayerPawn_ForwardMove1 (FRACUNIT * 2500 / 2048)
PROP_PlayerPawn_ForwardMove2 (FRACUNIT * 2500 / 2048)
PROP_PlayerPawn_SideMove1 (FRACUNIT * 2500 / 2048)
PROP_PlayerPawn_SideMove2 (FRACUNIT * 2500 / 2048)
PROP_PainSound ("chicken/pain")
PROP_DeathSound ("chicken/death")
END_DEFAULTS
void AChickenPlayer::MorphPlayerThink ()
{
if (health > 0)
{ // Handle beak movement
P_UpdateBeak (this);
}
if (player->morphTics & 15)
{
return;
}
if (!(momx | momy) && pr_chickenplayerthink () < 160)
{ // Twitch view angle
angle += pr_chickenplayerthink.Random2 () << 19;
}
if ((z <= floorz) && (pr_chickenplayerthink() < 32))
{ // Jump and noise
momz += JumpZ;
if (PainState != NULL)
{
SetState (PainState);
}
}
if (pr_chickenplayerthink () < 48)
{ // Just noise
S_Sound (this, CHAN_VOICE, "chicken/active", 1, ATTN_NORM);
}
}
void AChickenPlayer::ActivateMorphWeapon ()
{
player->PendingWeapon = WP_NOCHANGE;
player->psprites[ps_weapon].sy = WEAPONTOP;
player->ReadyWeapon = player->mo->FindInventory<ABeak> ();
if (player->ReadyWeapon == NULL)
{
player->ReadyWeapon = static_cast<AWeapon *>(player->mo->GiveInventoryType (RUNTIME_CLASS(ABeak)));
}
if (player->ReadyWeapon != NULL)
{
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
}
}
// Chicken (non-player) -----------------------------------------------------
class AChicken : public AActor
{
DECLARE_ACTOR (AChicken, AActor)
public:
void Destroy ();
void Die (AActor *source, AActor *inflictor);
};
FState AChicken::States[] =
{
#define S_CHICKEN_LOOK 0
S_NORMAL (CHKN, 'A', 10, A_ChicLook , &States[S_CHICKEN_LOOK+1]),
S_NORMAL (CHKN, 'B', 10, A_ChicLook , &States[S_CHICKEN_LOOK+0]),
#define S_CHICKEN_WALK (S_CHICKEN_LOOK+2)
S_NORMAL (CHKN, 'A', 3, A_ChicChase , &States[S_CHICKEN_WALK+1]),
S_NORMAL (CHKN, 'B', 3, A_ChicChase , &States[S_CHICKEN_WALK+0]),
#define S_CHICKEN_PAIN (S_CHICKEN_WALK+2)
S_NORMAL (CHKN, 'D', 5, A_Feathers , &States[S_CHICKEN_PAIN+1]),
S_NORMAL (CHKN, 'C', 5, A_ChicPain , &States[S_CHICKEN_WALK+0]),
#define S_CHICKEN_ATK (S_CHICKEN_PAIN+2)
S_NORMAL (CHKN, 'A', 8, A_FaceTarget , &States[S_CHICKEN_ATK+1]),
S_NORMAL (CHKN, 'C', 10, A_ChicAttack , &States[S_CHICKEN_WALK+0]),
#define S_CHICKEN_DIE (S_CHICKEN_ATK+2)
S_NORMAL (CHKN, 'E', 6, A_Scream , &States[S_CHICKEN_DIE+1]),
S_NORMAL (CHKN, 'F', 6, A_Feathers , &States[S_CHICKEN_DIE+2]),
S_NORMAL (CHKN, 'G', 6, NULL , &States[S_CHICKEN_DIE+3]),
S_NORMAL (CHKN, 'H', 6, A_NoBlocking , &States[S_CHICKEN_DIE+4]),
S_NORMAL (CHKN, 'I', 6, NULL , &States[S_CHICKEN_DIE+5]),
S_NORMAL (CHKN, 'J', 6, NULL , &States[S_CHICKEN_DIE+6]),
S_NORMAL (CHKN, 'K', 6, NULL , &States[S_CHICKEN_DIE+7]),
S_NORMAL (CHKN, 'L', -1, NULL , NULL)
};
IMPLEMENT_ACTOR (AChicken, Heretic, -1, 122)
PROP_SpawnHealth (10)
PROP_RadiusFixed (9)
PROP_HeightFixed (22)
PROP_Mass (40)
PROP_SpeedFixed (4)
PROP_PainChance (200)
PROP_Flags (MF_SOLID|MF_SHOOTABLE)
PROP_Flags2 (MF2_MCROSS|MF2_WINDTHRUST|MF2_FLOORCLIP|MF2_PASSMOBJ|MF2_PUSHWALL)
PROP_Flags3 (MF3_DONTMORPH|MF3_ISMONSTER)
PROP_SpawnState (S_CHICKEN_LOOK)
PROP_SeeState (S_CHICKEN_WALK)
PROP_PainState (S_CHICKEN_PAIN)
PROP_MeleeState (S_CHICKEN_ATK)
PROP_DeathState (S_CHICKEN_DIE)
PROP_SeeSound ("chicken/pain")
PROP_AttackSound ("chicken/attack")
PROP_PainSound ("chicken/pain")
PROP_DeathSound ("chicken/death")
PROP_ActiveSound ("chicken/active")
PROP_Obituary("$OB_CHICKEN")
END_DEFAULTS
void AChicken::Destroy ()
{
if (tracer != NULL)
{
tracer->Destroy ();
}
Super::Destroy ();
}
void AChicken::Die (AActor *source, AActor *inflictor)
{
Super::Die (source, inflictor);
if (tracer != NULL && (tracer->flags & MF_UNMORPHED))
{
tracer->Die (source, inflictor);
}
}
// Feather ------------------------------------------------------------------
class AFeather : public AActor
{
DECLARE_ACTOR (AFeather, AActor)
};
FState AFeather::States[] =
{
#define S_FEATHER 0
S_NORMAL (CHKN, 'M', 3, NULL , &States[S_FEATHER+1]),
S_NORMAL (CHKN, 'N', 3, NULL , &States[S_FEATHER+2]),
S_NORMAL (CHKN, 'O', 3, NULL , &States[S_FEATHER+3]),
S_NORMAL (CHKN, 'P', 3, NULL , &States[S_FEATHER+4]),
S_NORMAL (CHKN, 'Q', 3, NULL , &States[S_FEATHER+5]),
S_NORMAL (CHKN, 'P', 3, NULL , &States[S_FEATHER+6]),
S_NORMAL (CHKN, 'O', 3, NULL , &States[S_FEATHER+7]),
S_NORMAL (CHKN, 'N', 3, NULL , &States[S_FEATHER+0]),
#define S_FEATHERX (S_FEATHER+8)
S_NORMAL (CHKN, 'N', 6, NULL , NULL)
};
IMPLEMENT_ACTOR (AFeather, Heretic, -1, 121)
PROP_RadiusFixed (2)
PROP_HeightFixed (4)
PROP_Flags (MF_MISSILE|MF_DROPOFF)
PROP_Flags2 (MF2_NOTELEPORT|MF2_LOGRAV|MF2_CANNOTPUSH|MF2_WINDTHRUST)
PROP_Flags3 (MF3_DONTSPLASH)
PROP_SpawnState (S_FEATHER)
PROP_DeathState (S_FEATHERX)
END_DEFAULTS
//----------------------------------------------------------------------------
//
// PROC A_ChicAttack
//
//----------------------------------------------------------------------------
void A_ChicAttack (AActor *actor)
{
if (P_UpdateMorphedMonster(actor, 18))
{
return;
}
if (!actor->target)
{
return;
}
if (actor->CheckMeleeRange())
{
int damage = 1 + (pr_chicattack() & 1);
P_DamageMobj (actor->target, actor, actor, damage, MOD_HIT);
P_TraceBleed (damage, actor->target, actor);
}
}
//----------------------------------------------------------------------------
//
// PROC A_ChicLook
//
//----------------------------------------------------------------------------
void A_ChicLook (AActor *actor)
{
if (P_UpdateMorphedMonster (actor, 10))
{
return;
}
A_Look (actor);
}
//----------------------------------------------------------------------------
//
// PROC A_ChicChase
//
//----------------------------------------------------------------------------
void A_ChicChase (AActor *actor)
{
if (P_UpdateMorphedMonster (actor, 3))
{
return;
}
A_Chase (actor);
}
//----------------------------------------------------------------------------
//
// PROC A_ChicPain
//
//----------------------------------------------------------------------------
void A_ChicPain (AActor *actor)
{
if (P_UpdateMorphedMonster (actor, 10))
{
return;
}
S_SoundID (actor, CHAN_BODY, actor->PainSound, 1, ATTN_NORM);
}
//----------------------------------------------------------------------------
//
// PROC A_Feathers
//
//----------------------------------------------------------------------------
void A_Feathers (AActor *actor)
{
int i;
int count;
AActor *mo;
if (actor->health > 0)
{ // Pain
count = pr_feathers() < 32 ? 2 : 1;
}
else
{ // Death
count = 5 + (pr_feathers()&3);
}
for (i = 0; i < count; i++)
{
mo = Spawn<AFeather> (actor->x, actor->y, actor->z+20*FRACUNIT);
mo->target = actor;
mo->momx = pr_feathers.Random2() << 8;
mo->momy = pr_feathers.Random2() << 8;
mo->momz = FRACUNIT + (pr_feathers() << 9);
mo->SetState (&AFeather::States[S_FEATHER+pr_feathers()&7]);
}
}
//---------------------------------------------------------------------------
//
// PROC P_UpdateBeak
//
//---------------------------------------------------------------------------
void P_UpdateBeak (AActor *actor)
{
if (actor->player != NULL)
{
actor->player->psprites[ps_weapon].sy = WEAPONTOP +
(actor->player->chickenPeck << (FRACBITS-1));
}
}
//---------------------------------------------------------------------------
//
// PROC A_BeakRaise
//
//---------------------------------------------------------------------------
void A_BeakRaise (AActor *actor)
{
player_t *player;
if (NULL == (player = actor->player))
{
return;
}
player->psprites[ps_weapon].sy = WEAPONTOP;
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
}
//----------------------------------------------------------------------------
//
// PROC P_PlayPeck
//
//----------------------------------------------------------------------------
void P_PlayPeck (AActor *chicken)
{
S_Sound (chicken, CHAN_VOICE, "chicken/peck", 1, ATTN_NORM);
}
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL1
//
//----------------------------------------------------------------------------
void A_BeakAttackPL1 (AActor *actor)
{
angle_t angle;
int damage;
int slope;
player_t *player;
if (NULL == (player = actor->player))
{
return;
}
damage = 1 + (pr_beakatkpl1()&3);
angle = player->mo->angle;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, MOD_HIT, RUNTIME_CLASS(ABeakPuff));
if (linetarget)
{
player->mo->angle = R_PointToAngle2 (player->mo->x,
player->mo->y, linetarget->x, linetarget->y);
}
P_PlayPeck (player->mo);
player->chickenPeck = 12;
player->psprites[ps_weapon].tics -= pr_beakatkpl1() & 7;
}
//----------------------------------------------------------------------------
//
// PROC A_BeakAttackPL2
//
//----------------------------------------------------------------------------
void A_BeakAttackPL2 (AActor *actor)
{
angle_t angle;
int damage;
int slope;
player_t *player;
if (NULL == (player = actor->player))
{
return;
}
damage = pr_beakatkpl2.HitDice (4);
angle = player->mo->angle;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, MOD_HIT, RUNTIME_CLASS(ABeakPuff));
if (linetarget)
{
player->mo->angle = R_PointToAngle2 (player->mo->x,
player->mo->y, linetarget->x, linetarget->y);
}
P_PlayPeck (player->mo);
player->chickenPeck = 12;
player->psprites[ps_weapon].tics -= pr_beakatkpl2()&3;
}