#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: fixed_t GetJumpZ () { return FRACUNIT; } void MorphPlayerThink (); void ActivateMorphWeapon (); void TweakSpeeds (int &forward, int &side); }; 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) 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 += GetJumpZ (); 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 (); if (player->ReadyWeapon == NULL) { player->ReadyWeapon = static_cast(player->mo->GiveInventoryType (RUNTIME_CLASS(ABeak))); } if (player->ReadyWeapon != NULL) { P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState()); } } void AChickenPlayer::TweakSpeeds (int &forward, int &side) { forward = forward * 2500 / 2048; side = side * 2500 / 2048; } // Chicken (non-player) ----------------------------------------------------- class AChicken : public AActor { DECLARE_ACTOR (AChicken, AActor) public: void Destroy (); const char *GetObituary (); 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_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") END_DEFAULTS void AChicken::Destroy () { if (tracer != NULL) { tracer->Destroy (); } Super::Destroy (); } const char *AChicken::GetObituary () { return GStrings("OB_CHICKEN"); } 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 (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; }