mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-17 07:51:06 +00:00
bb617dfbfd
longer determined by game. Instead there's a new flag, MF5_BLOODSPLATTER which is deciding what to do. To keep backwards compatibility this flag is unset for projectiles in Doom and Strife and set for them in Heretic and Hexen. The same applies to DECORATE but of course the flag can be manipulated here. - BLODxx sprites are now globally renamed to BLUDxx when not playing Doom. This allows using the same states in every game, including the Raven-specific blood actors. - Gave the bullet puff and the axe blood masses of 5 so that the make small splashes. - Added A_Light(value) code pointer for DECORATE to generalize the weapon light effect. - Added 'noskillmenu' option to MAPINFO episode definitions. This is for WADs that want to implement a skill selection level. - Added APROP_ChaseGoal and APROP_Frightened actor properties for ACS. - Added MF5_CHASEGOAL flag that makes monsters to go after their goal even if they have a valid target. - Fixed some issues with the changes to P_NewChaseDir I made to include MBF's dropoff logic. - Added a PowerFrightener powerup class. It seemed like such a waste to have this cool feature but no means to use it in a decent fashion. - Fixed: S_Init and S_ParseSndInfo should call atterm only once but not each time they are called. SVN r112 (trunk)
420 lines
12 KiB
C++
420 lines
12 KiB
C++
#include "actor.h"
|
|
#include "gi.h"
|
|
#include "m_random.h"
|
|
#include "s_sound.h"
|
|
#include "d_player.h"
|
|
#include "a_action.h"
|
|
#include "p_local.h"
|
|
#include "p_enemy.h"
|
|
#include "a_action.h"
|
|
#include "a_hexenglobal.h"
|
|
|
|
static FRandom pr_fpatk ("FPunchAttack");
|
|
|
|
void A_FSwordFlames (AActor *);
|
|
|
|
// The fighter --------------------------------------------------------------
|
|
|
|
FState AFighterPlayer::States[] =
|
|
{
|
|
#define S_FPLAY 0
|
|
S_NORMAL (PLAY, 'A', -1, NULL , NULL),
|
|
|
|
#define S_FPLAY_RUN (S_FPLAY+1)
|
|
S_NORMAL (PLAY, 'A', 4, NULL , &States[S_FPLAY_RUN+1]),
|
|
S_NORMAL (PLAY, 'B', 4, NULL , &States[S_FPLAY_RUN+2]),
|
|
S_NORMAL (PLAY, 'C', 4, NULL , &States[S_FPLAY_RUN+3]),
|
|
S_NORMAL (PLAY, 'D', 4, NULL , &States[S_FPLAY_RUN+0]),
|
|
|
|
#define S_FPLAY_ATK (S_FPLAY_RUN+4)
|
|
S_NORMAL (PLAY, 'E', 8, NULL , &States[S_FPLAY_ATK+1]),
|
|
S_NORMAL (PLAY, 'F', 8, NULL , &States[S_FPLAY]),
|
|
|
|
#define S_FPLAY_PAIN (S_FPLAY_ATK+2)
|
|
S_NORMAL (PLAY, 'G', 4, NULL , &States[S_FPLAY_PAIN+1]),
|
|
S_NORMAL (PLAY, 'G', 4, A_Pain , &States[S_FPLAY]),
|
|
|
|
#define S_FPLAY_DIE (S_FPLAY_PAIN+2)
|
|
S_NORMAL (PLAY, 'H', 6, NULL , &States[S_FPLAY_DIE+1]),
|
|
S_NORMAL (PLAY, 'I', 6, A_PlayerScream , &States[S_FPLAY_DIE+2]),
|
|
S_NORMAL (PLAY, 'J', 6, NULL , &States[S_FPLAY_DIE+3]),
|
|
S_NORMAL (PLAY, 'K', 6, NULL , &States[S_FPLAY_DIE+4]),
|
|
S_NORMAL (PLAY, 'L', 6, A_NoBlocking , &States[S_FPLAY_DIE+5]),
|
|
S_NORMAL (PLAY, 'M', 6, NULL , &States[S_FPLAY_DIE+6]),
|
|
S_NORMAL (PLAY, 'N', -1, NULL/*A_AddPlayerCorpse*/, NULL),
|
|
|
|
#define S_FPLAY_XDIE (S_FPLAY_DIE+7)
|
|
S_NORMAL (PLAY, 'O', 5, A_PlayerScream , &States[S_FPLAY_XDIE+1]),
|
|
S_NORMAL (PLAY, 'P', 5, A_SkullPop , &States[S_FPLAY_XDIE+2]),
|
|
S_NORMAL (PLAY, 'R', 5, A_NoBlocking , &States[S_FPLAY_XDIE+3]),
|
|
S_NORMAL (PLAY, 'S', 5, NULL , &States[S_FPLAY_XDIE+4]),
|
|
S_NORMAL (PLAY, 'T', 5, NULL , &States[S_FPLAY_XDIE+5]),
|
|
S_NORMAL (PLAY, 'U', 5, NULL , &States[S_FPLAY_XDIE+6]),
|
|
S_NORMAL (PLAY, 'V', 5, NULL , &States[S_FPLAY_XDIE+7]),
|
|
S_NORMAL (PLAY, 'W', -1, NULL/*A_AddPlayerCorpse*/, NULL),
|
|
|
|
#define S_FPLAY_ICE (S_FPLAY_XDIE+8)
|
|
S_NORMAL (PLAY, 'X', 5, A_FreezeDeath , &States[S_FPLAY_ICE+1]),
|
|
S_NORMAL (PLAY, 'X', 1, A_FreezeDeathChunks , &States[S_FPLAY_ICE+1]),
|
|
|
|
#define S_PLAY_FDTH (S_FPLAY_ICE+2)
|
|
S_BRIGHT (FDTH, 'G', 5, NULL , &States[S_PLAY_FDTH+1]),
|
|
S_BRIGHT (FDTH, 'H', 4, A_PlayerScream , &States[S_PLAY_FDTH+2]),
|
|
S_BRIGHT (FDTH, 'I', 5, NULL , &States[S_PLAY_FDTH+3]),
|
|
S_BRIGHT (FDTH, 'J', 4, NULL , &States[S_PLAY_FDTH+4]),
|
|
S_BRIGHT (FDTH, 'K', 5, NULL , &States[S_PLAY_FDTH+5]),
|
|
S_BRIGHT (FDTH, 'L', 4, NULL , &States[S_PLAY_FDTH+6]),
|
|
S_BRIGHT (FDTH, 'M', 5, NULL , &States[S_PLAY_FDTH+7]),
|
|
S_BRIGHT (FDTH, 'N', 4, NULL , &States[S_PLAY_FDTH+8]),
|
|
S_BRIGHT (FDTH, 'O', 5, NULL , &States[S_PLAY_FDTH+9]),
|
|
S_BRIGHT (FDTH, 'P', 4, NULL , &States[S_PLAY_FDTH+10]),
|
|
S_BRIGHT (FDTH, 'Q', 5, NULL , &States[S_PLAY_FDTH+11]),
|
|
S_BRIGHT (FDTH, 'R', 4, NULL , &States[S_PLAY_FDTH+12]),
|
|
S_BRIGHT (FDTH, 'S', 5, A_NoBlocking , &States[S_PLAY_FDTH+13]),
|
|
S_BRIGHT (FDTH, 'T', 4, NULL , &States[S_PLAY_FDTH+14]),
|
|
S_BRIGHT (FDTH, 'U', 5, NULL , &States[S_PLAY_FDTH+15]),
|
|
S_BRIGHT (FDTH, 'V', 4, NULL , &States[S_PLAY_FDTH+16]),
|
|
S_NORMAL (ACLO, 'E', 35, A_CheckBurnGone , &States[S_PLAY_FDTH+16]),
|
|
S_NORMAL (ACLO, 'E', 8, NULL , NULL),
|
|
|
|
#define S_PLAY_F_FDTH (S_PLAY_FDTH+18)
|
|
S_BRIGHT (FDTH, 'A', 5, A_FireScream , &States[S_PLAY_F_FDTH+1]),
|
|
S_BRIGHT (FDTH, 'B', 4, NULL , &States[S_PLAY_FDTH]),
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AFighterPlayer, Hexen, -1, 0)
|
|
PROP_SpawnHealth (100)
|
|
PROP_RadiusFixed (16)
|
|
PROP_HeightFixed (64)
|
|
PROP_Mass (100)
|
|
PROP_PainChance (255)
|
|
PROP_SpeedFixed (1)
|
|
PROP_Flags (MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY)
|
|
PROP_Flags2 (MF2_WINDTHRUST|MF2_FLOORCLIP|MF2_SLIDE|MF2_PASSMOBJ|MF2_TELESTOMP|MF2_PUSHWALL)
|
|
PROP_Flags3 (MF3_NOBLOCKMONST)
|
|
PROP_Flags4 (MF4_NOSKIN)
|
|
|
|
PROP_SpawnState (S_FPLAY)
|
|
PROP_SeeState (S_FPLAY_RUN)
|
|
PROP_PainState (S_FPLAY_PAIN)
|
|
PROP_MissileState (S_FPLAY_ATK)
|
|
PROP_DeathState (S_FPLAY_DIE)
|
|
PROP_XDeathState (S_FPLAY_XDIE)
|
|
PROP_BDeathState (S_PLAY_F_FDTH)
|
|
PROP_IDeathState (S_FPLAY_ICE)
|
|
|
|
PROP_PainSound ("PlayerFighterPain")
|
|
END_DEFAULTS
|
|
|
|
const char *AFighterPlayer::GetSoundClass ()
|
|
{
|
|
if (player == NULL || player->userinfo.skin == 0)
|
|
{
|
|
return "fighter";
|
|
}
|
|
return Super::GetSoundClass ();
|
|
}
|
|
void AFighterPlayer::PlayAttacking2 ()
|
|
{
|
|
SetState (MissileState);
|
|
}
|
|
|
|
void AFighterPlayer::GiveDefaultInventory ()
|
|
{
|
|
player->health = GetDefault()->health;
|
|
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *>
|
|
(GiveInventoryType (PClass::FindClass ("FWeapFist")));
|
|
|
|
GiveInventoryType (RUNTIME_CLASS(AHexenArmor));
|
|
AHexenArmor *armor = FindInventory<AHexenArmor>();
|
|
armor->Slots[4] = 15*FRACUNIT;
|
|
armor->SlotsIncrement[0] = 25*FRACUNIT;
|
|
armor->SlotsIncrement[1] = 20*FRACUNIT;
|
|
armor->SlotsIncrement[2] = 15*FRACUNIT;
|
|
armor->SlotsIncrement[3] = 5*FRACUNIT;
|
|
}
|
|
|
|
// --- Fighter Weapons ------------------------------------------------------
|
|
|
|
//============================================================================
|
|
//
|
|
// AdjustPlayerAngle
|
|
//
|
|
//============================================================================
|
|
|
|
#define MAX_ANGLE_ADJUST (5*ANGLE_1)
|
|
|
|
void AdjustPlayerAngle (AActor *pmo)
|
|
{
|
|
angle_t angle;
|
|
int difference;
|
|
|
|
angle = R_PointToAngle2 (pmo->x, pmo->y, linetarget->x, linetarget->y);
|
|
difference = (int)angle - (int)pmo->angle;
|
|
if (abs(difference) > MAX_ANGLE_ADJUST)
|
|
{
|
|
if (difference > 0)
|
|
{
|
|
pmo->angle += MAX_ANGLE_ADJUST;
|
|
}
|
|
else
|
|
{
|
|
pmo->angle -= MAX_ANGLE_ADJUST;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pmo->angle = angle;
|
|
}
|
|
}
|
|
|
|
// Fist (first weapon) ------------------------------------------------------
|
|
|
|
void A_FPunchAttack (AActor *actor);
|
|
|
|
class AFWeapFist : public AFighterWeapon
|
|
{
|
|
DECLARE_ACTOR (AFWeapFist, AFighterWeapon)
|
|
};
|
|
|
|
FState AFWeapFist::States[] =
|
|
{
|
|
#define S_PUNCHREADY 0
|
|
S_NORMAL (FPCH, 'A', 1, A_WeaponReady , &States[S_PUNCHREADY]),
|
|
|
|
#define S_PUNCHDOWN (S_PUNCHREADY+1)
|
|
S_NORMAL (FPCH, 'A', 1, A_Lower , &States[S_PUNCHDOWN]),
|
|
|
|
#define S_PUNCHUP (S_PUNCHDOWN+1)
|
|
S_NORMAL (FPCH, 'A', 1, A_Raise , &States[S_PUNCHUP]),
|
|
|
|
#define S_PUNCHATK1 (S_PUNCHUP+1)
|
|
S_NORMAL2(FPCH, 'B', 5, NULL , &States[S_PUNCHATK1+1], 5, 40),
|
|
S_NORMAL2(FPCH, 'C', 4, NULL , &States[S_PUNCHATK1+2], 5, 40),
|
|
S_NORMAL2(FPCH, 'D', 4, A_FPunchAttack , &States[S_PUNCHATK1+3], 5, 40),
|
|
S_NORMAL2(FPCH, 'C', 4, NULL , &States[S_PUNCHATK1+4], 5, 40),
|
|
S_NORMAL2(FPCH, 'B', 5, A_ReFire , &States[S_PUNCHREADY], 5, 40),
|
|
|
|
#define S_PUNCHATK2 (S_PUNCHATK1+5)
|
|
S_NORMAL2(FPCH, 'D', 4, NULL , &States[S_PUNCHATK2+1], 5, 40),
|
|
S_NORMAL2(FPCH, 'E', 4, NULL , &States[S_PUNCHATK2+2], 5, 40),
|
|
S_NORMAL2(FPCH, 'E', 1, NULL , &States[S_PUNCHATK2+3], 15, 50),
|
|
S_NORMAL2(FPCH, 'E', 1, NULL , &States[S_PUNCHATK2+4], 25, 60),
|
|
S_NORMAL2(FPCH, 'E', 1, NULL , &States[S_PUNCHATK2+5], 35, 70),
|
|
S_NORMAL2(FPCH, 'E', 1, NULL , &States[S_PUNCHATK2+6], 45, 80),
|
|
S_NORMAL2(FPCH, 'E', 1, NULL , &States[S_PUNCHATK2+7], 55, 90),
|
|
S_NORMAL2(FPCH, 'E', 1, NULL , &States[S_PUNCHATK2+8], 65, 100),
|
|
S_NORMAL2(FPCH, 'E', 10, NULL , &States[S_PUNCHREADY], 0, 150)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (AFWeapFist, Hexen, -1, 0)
|
|
PROP_Flags5 (MF5_BLOODSPLATTER)
|
|
PROP_Weapon_SelectionOrder (3400)
|
|
PROP_Weapon_Flags (WIF_BOT_MELEE)
|
|
PROP_Weapon_UpState (S_PUNCHUP)
|
|
PROP_Weapon_DownState (S_PUNCHDOWN)
|
|
PROP_Weapon_ReadyState (S_PUNCHREADY)
|
|
PROP_Weapon_AtkState (S_PUNCHATK1)
|
|
PROP_Weapon_HoldAtkState (S_PUNCHATK1)
|
|
PROP_Weapon_Kickback (150)
|
|
END_DEFAULTS
|
|
|
|
// Punch puff ---------------------------------------------------------------
|
|
|
|
class APunchPuff : public AActor
|
|
{
|
|
DECLARE_ACTOR (APunchPuff, AActor)
|
|
public:
|
|
void BeginPlay ();
|
|
};
|
|
|
|
FState APunchPuff::States[] =
|
|
{
|
|
S_NORMAL (FHFX, 'S', 4, NULL , &States[1]),
|
|
S_NORMAL (FHFX, 'T', 4, NULL , &States[2]),
|
|
S_NORMAL (FHFX, 'U', 4, NULL , &States[3]),
|
|
S_NORMAL (FHFX, 'V', 4, NULL , &States[4]),
|
|
S_NORMAL (FHFX, 'W', 4, NULL , NULL)
|
|
};
|
|
|
|
IMPLEMENT_ACTOR (APunchPuff, Hexen, -1, 0)
|
|
PROP_Flags (MF_NOBLOCKMAP|MF_NOGRAVITY)
|
|
PROP_Flags3 (MF3_PUFFONACTORS)
|
|
PROP_RenderStyle (STYLE_Translucent)
|
|
PROP_Alpha (HX_SHADOW)
|
|
|
|
PROP_SpawnState (0)
|
|
|
|
PROP_SeeSound ("FighterPunchHitThing")
|
|
PROP_AttackSound ("FighterPunchHitWall")
|
|
PROP_ActiveSound ("FighterPunchMiss")
|
|
END_DEFAULTS
|
|
|
|
void APunchPuff::BeginPlay ()
|
|
{
|
|
Super::BeginPlay ();
|
|
momz = FRACUNIT;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// A_FPunchAttack
|
|
//
|
|
//============================================================================
|
|
|
|
void A_FPunchAttack (AActor *actor)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
int slope;
|
|
fixed_t power;
|
|
int i;
|
|
player_t *player;
|
|
const PClass *pufftype;
|
|
|
|
if (NULL == (player = actor->player))
|
|
{
|
|
return;
|
|
}
|
|
APlayerPawn *pmo = player->mo;
|
|
|
|
damage = 40+(pr_fpatk()&15);
|
|
power = 2*FRACUNIT;
|
|
pufftype = RUNTIME_CLASS(APunchPuff);
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
angle = pmo->angle + i*(ANG45/16);
|
|
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE);
|
|
if (linetarget)
|
|
{
|
|
pmo->special1++;
|
|
if (pmo->special1 == 3)
|
|
{
|
|
damage <<= 1;
|
|
power = 6*FRACUNIT;
|
|
pufftype = RUNTIME_CLASS(AHammerPuff);
|
|
}
|
|
P_LineAttack (pmo, angle, 2*MELEERANGE, slope, damage, MOD_HIT, pufftype);
|
|
if (linetarget->flags3&MF3_ISMONSTER || linetarget->player)
|
|
{
|
|
P_ThrustMobj (linetarget, angle, power);
|
|
}
|
|
AdjustPlayerAngle (pmo);
|
|
goto punchdone;
|
|
}
|
|
angle = pmo->angle-i * (ANG45/16);
|
|
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE);
|
|
if (linetarget)
|
|
{
|
|
pmo->special1++;
|
|
if (pmo->special1 == 3)
|
|
{
|
|
damage <<= 1;
|
|
power = 6*FRACUNIT;
|
|
pufftype = RUNTIME_CLASS(AHammerPuff);
|
|
}
|
|
P_LineAttack (pmo, angle, 2*MELEERANGE, slope, damage, MOD_HIT, pufftype);
|
|
if (linetarget->flags3&MF3_ISMONSTER || linetarget->player)
|
|
{
|
|
P_ThrustMobj (linetarget, angle, power);
|
|
}
|
|
AdjustPlayerAngle (pmo);
|
|
goto punchdone;
|
|
}
|
|
}
|
|
// didn't find any creatures, so try to strike any walls
|
|
pmo->special1 = 0;
|
|
|
|
angle = pmo->angle;
|
|
slope = P_AimLineAttack (pmo, angle, MELEERANGE);
|
|
P_LineAttack (pmo, angle, MELEERANGE, slope, damage, MOD_HIT, pufftype);
|
|
|
|
punchdone:
|
|
if (pmo->special1 == 3)
|
|
{
|
|
pmo->special1 = 0;
|
|
P_SetPsprite (player, ps_weapon, &AFWeapFist::States[S_PUNCHATK2]);
|
|
S_Sound (pmo, CHAN_VOICE, "*fistgrunt", 1, ATTN_NORM);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void AFighterPlayer::TweakSpeeds (int &forward, int &side)
|
|
{
|
|
if ((unsigned int)(forward + 0x31ff) < 0x63ff)
|
|
{
|
|
forward = forward * 0x1d / 0x19;
|
|
}
|
|
else
|
|
{
|
|
forward = forward * 0x3c / 0x32;
|
|
}
|
|
if ((unsigned int)(side + 0x27ff) < 0x4fff)
|
|
{
|
|
side = side * 0x1b / 0x18;
|
|
}
|
|
else
|
|
{ // The fighter is a very fast strafer when running!
|
|
side = side * 0x3b / 0x28;
|
|
}
|
|
Super::TweakSpeeds (forward, side);
|
|
}
|
|
|
|
fixed_t AFighterPlayer::GetJumpZ ()
|
|
{
|
|
return FRACUNIT*39/4; // ~9.75
|
|
}
|
|
|
|
// Radius armor boost
|
|
bool AFighterPlayer::DoHealingRadius (APlayerPawn *other)
|
|
{
|
|
bool gotSome = false;
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
AHexenArmor *armor = Spawn<AHexenArmor> (0,0,0);
|
|
armor->health = i;
|
|
armor->Amount = 1;
|
|
if (!armor->TryPickup (player->mo))
|
|
{
|
|
armor->Destroy ();
|
|
}
|
|
else
|
|
{
|
|
gotSome = true;
|
|
}
|
|
}
|
|
if (gotSome)
|
|
{
|
|
S_Sound (other, CHAN_AUTO, "MysticIncant", 1, ATTN_NORM);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Fighter Weapon Base Class ------------------------------------------------
|
|
|
|
IMPLEMENT_ABSTRACT_ACTOR (AFighterWeapon)
|
|
|
|
bool AFighterWeapon::TryPickup (AActor *toucher)
|
|
{
|
|
// The Doom and Hexen players are not excluded from pickup in case
|
|
// somebody wants to use these weapons with either of those games.
|
|
if (toucher->IsKindOf (RUNTIME_CLASS(AClericPlayer)) ||
|
|
toucher->IsKindOf (RUNTIME_CLASS(AMagePlayer)))
|
|
{ // Wrong class, but try to pick up for mana
|
|
if (ShouldStay())
|
|
{ // Can't pick up weapons for other classes in coop netplay
|
|
return false;
|
|
}
|
|
|
|
bool gaveSome = (NULL != AddAmmo (toucher, AmmoType1, AmmoGive1));
|
|
gaveSome |= (NULL != AddAmmo (toucher, AmmoType2, AmmoGive2));
|
|
if (gaveSome)
|
|
{
|
|
GoAwayAndDie ();
|
|
}
|
|
return gaveSome;
|
|
}
|
|
return Super::TryPickup (toucher);
|
|
}
|