mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-16 17:21:24 +00:00
0715b7dfc6
* savegames stored an index in the switch table and performed no validation when loading a savegame. * setting of a random switch animation duration was broken. * separated the 2 values stored in the Time variable into 2 separate variables. * defining a switch with one texture already belonging to another switch could leave broken definitions in the switch table. - added function for serializing switch and door animation pointers. - bumped min. savegame versions due to changes to DButtonThinker and removed all current savegame compatibility code. SVN r3030 (trunk)
867 lines
21 KiB
C++
867 lines
21 KiB
C++
|
|
//**************************************************************************
|
|
//**
|
|
//** p_pspr.c : Heretic 2 : Raven Software, Corp.
|
|
//**
|
|
//** $RCSfile: p_pspr.c,v $
|
|
//** $Revision: 1.105 $
|
|
//** $Date: 96/01/06 03:23:35 $
|
|
//** $Author: bgokey $
|
|
//**
|
|
//**************************************************************************
|
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "doomdef.h"
|
|
#include "d_event.h"
|
|
#include "c_cvars.h"
|
|
#include "m_random.h"
|
|
#include "p_local.h"
|
|
#include "s_sound.h"
|
|
#include "doomstat.h"
|
|
#include "gi.h"
|
|
#include "p_pspr.h"
|
|
#include "templates.h"
|
|
#include "thingdef/thingdef.h"
|
|
#include "g_level.h"
|
|
|
|
// MACROS ------------------------------------------------------------------
|
|
|
|
#define LOWERSPEED FRACUNIT*6
|
|
#define RAISESPEED FRACUNIT*6
|
|
|
|
// TYPES -------------------------------------------------------------------
|
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
// [SO] 1=Weapons states are all 1 tick
|
|
// 2=states with a function 1 tick, others 0 ticks.
|
|
CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO);
|
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
|
|
|
static FRandom pr_wpnreadysnd ("WpnReadySnd");
|
|
static FRandom pr_gunshot ("GunShot");
|
|
|
|
// CODE --------------------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC P_SetPsprite
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void P_SetPsprite (player_t *player, int position, FState *state, bool nofunction)
|
|
{
|
|
pspdef_t *psp;
|
|
|
|
if (position == ps_weapon && !nofunction)
|
|
{ // A_WeaponReady will re-set these as needed
|
|
player->cheats &= ~(CF_WEAPONREADY | CF_WEAPONREADYALT | CF_WEAPONBOBBING | CF_WEAPONSWITCHOK);
|
|
}
|
|
|
|
psp = &player->psprites[position];
|
|
do
|
|
{
|
|
if (state == NULL)
|
|
{ // Object removed itself.
|
|
psp->state = NULL;
|
|
break;
|
|
}
|
|
psp->state = state;
|
|
|
|
if (state->sprite != SPR_FIXED)
|
|
{ // okay to change sprite and/or frame
|
|
if (!state->GetSameFrame())
|
|
{ // okay to change frame
|
|
psp->frame = state->GetFrame();
|
|
}
|
|
if (state->sprite != SPR_NOCHANGE)
|
|
{ // okay to change sprite
|
|
psp->sprite = state->sprite;
|
|
}
|
|
}
|
|
|
|
|
|
if (sv_fastweapons >= 2 && position == ps_weapon)
|
|
psp->tics = state->ActionFunc == NULL? 0 : 1;
|
|
else if (sv_fastweapons)
|
|
psp->tics = 1; // great for producing decals :)
|
|
else
|
|
psp->tics = state->GetTics(); // could be 0
|
|
|
|
if (state->GetMisc1())
|
|
{ // Set coordinates.
|
|
psp->sx = state->GetMisc1()<<FRACBITS;
|
|
}
|
|
if (state->GetMisc2())
|
|
{
|
|
psp->sy = state->GetMisc2()<<FRACBITS;
|
|
}
|
|
|
|
if (!nofunction && player->mo != NULL)
|
|
{
|
|
if (state->CallAction(player->mo, player->ReadyWeapon))
|
|
{
|
|
if (!psp->state)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
state = psp->state->GetNextState();
|
|
} while (!psp->tics); // An initial state of 0 could cycle through.
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC P_BringUpWeapon
|
|
//
|
|
// Starts bringing the pending weapon up from the bottom of the screen.
|
|
// This is only called to start the rising, not throughout it.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void P_BringUpWeapon (player_t *player)
|
|
{
|
|
FState *newstate;
|
|
AWeapon *weapon;
|
|
|
|
if (player->PendingWeapon == WP_NOCHANGE)
|
|
{
|
|
player->psprites[ps_weapon].sy = WEAPONTOP;
|
|
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
|
|
return;
|
|
}
|
|
|
|
weapon = player->PendingWeapon;
|
|
|
|
// If the player has a tome of power, use this weapon's powered up
|
|
// version, if one is available.
|
|
if (weapon != NULL &&
|
|
weapon->SisterWeapon &&
|
|
weapon->SisterWeapon->WeaponFlags & WIF_POWERED_UP &&
|
|
player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2)))
|
|
{
|
|
weapon = weapon->SisterWeapon;
|
|
}
|
|
|
|
if (weapon != NULL)
|
|
{
|
|
if (weapon->UpSound)
|
|
{
|
|
S_Sound (player->mo, CHAN_WEAPON, weapon->UpSound, 1, ATTN_NORM);
|
|
}
|
|
newstate = weapon->GetUpState ();
|
|
player->refire = 0;
|
|
}
|
|
else
|
|
{
|
|
newstate = NULL;
|
|
}
|
|
player->PendingWeapon = WP_NOCHANGE;
|
|
player->ReadyWeapon = weapon;
|
|
player->psprites[ps_weapon].sy = player->cheats & CF_INSTANTWEAPSWITCH
|
|
? WEAPONTOP : WEAPONBOTTOM;
|
|
P_SetPsprite (player, ps_weapon, newstate);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC P_FireWeapon
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void P_FireWeapon (player_t *player, FState *state)
|
|
{
|
|
AWeapon *weapon;
|
|
|
|
// [SO] 9/2/02: People were able to do an awful lot of damage
|
|
// when they were observers...
|
|
if (!player->isbot && bot_observer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
weapon = player->ReadyWeapon;
|
|
if (weapon == NULL || !weapon->CheckAmmo (AWeapon::PrimaryFire, true))
|
|
{
|
|
return;
|
|
}
|
|
|
|
player->mo->PlayAttacking ();
|
|
weapon->bAltFire = false;
|
|
if (state == NULL)
|
|
{
|
|
state = weapon->GetAtkState(!!player->refire);
|
|
}
|
|
P_SetPsprite (player, ps_weapon, state);
|
|
if (!(weapon->WeaponFlags & WIF_NOALERT))
|
|
{
|
|
P_NoiseAlert (player->mo, player->mo, false);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC P_FireWeaponAlt
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void P_FireWeaponAlt (player_t *player, FState *state)
|
|
{
|
|
AWeapon *weapon;
|
|
|
|
// [SO] 9/2/02: People were able to do an awful lot of damage
|
|
// when they were observers...
|
|
if (!player->isbot && bot_observer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
weapon = player->ReadyWeapon;
|
|
if (weapon == NULL || weapon->FindState(NAME_AltFire) == NULL || !weapon->CheckAmmo (AWeapon::AltFire, true))
|
|
{
|
|
return;
|
|
}
|
|
|
|
player->mo->PlayAttacking ();
|
|
weapon->bAltFire = true;
|
|
|
|
if (state == NULL)
|
|
{
|
|
state = weapon->GetAltAtkState(!!player->refire);
|
|
}
|
|
|
|
P_SetPsprite (player, ps_weapon, state);
|
|
if (!(weapon->WeaponFlags & WIF_NOALERT))
|
|
{
|
|
P_NoiseAlert (player->mo, player->mo, false);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC P_DropWeapon
|
|
//
|
|
// The player died, so put the weapon away.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void P_DropWeapon (player_t *player)
|
|
{
|
|
if (player->ReadyWeapon != NULL)
|
|
{
|
|
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetDownState());
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// P_BobWeapon
|
|
//
|
|
// [RH] Moved this out of A_WeaponReady so that the weapon can bob every
|
|
// tic and not just when A_WeaponReady is called. Not all weapons execute
|
|
// A_WeaponReady every tic, and it looks bad if they don't bob smoothly.
|
|
//
|
|
//============================================================================
|
|
|
|
void P_BobWeapon (player_t *player, pspdef_t *psp, fixed_t *x, fixed_t *y)
|
|
{
|
|
static fixed_t curbob;
|
|
|
|
AWeapon *weapon;
|
|
fixed_t bobtarget;
|
|
|
|
weapon = player->ReadyWeapon;
|
|
|
|
if (weapon == NULL || weapon->WeaponFlags & WIF_DONTBOB)
|
|
{
|
|
*x = *y = 0;
|
|
return;
|
|
}
|
|
|
|
// Bob the weapon based on movement speed.
|
|
int angle = (128*35/TICRATE*level.time)&FINEMASK;
|
|
|
|
// [RH] Smooth transitions between bobbing and not-bobbing frames.
|
|
// This also fixes the bug where you can "stick" a weapon off-center by
|
|
// shooting it when it's at the peak of its swing.
|
|
bobtarget = (player->cheats & CF_WEAPONBOBBING) ? player->bob : 0;
|
|
if (curbob != bobtarget)
|
|
{
|
|
if (abs (bobtarget - curbob) <= 1*FRACUNIT)
|
|
{
|
|
curbob = bobtarget;
|
|
}
|
|
else
|
|
{
|
|
fixed_t zoom = MAX<fixed_t> (1*FRACUNIT, abs (curbob - bobtarget) / 40);
|
|
if (curbob > bobtarget)
|
|
{
|
|
curbob -= zoom;
|
|
}
|
|
else
|
|
{
|
|
curbob += zoom;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (curbob != 0)
|
|
{
|
|
*x = FixedMul(player->bob, finecosine[angle]);
|
|
*y = FixedMul(player->bob, finesine[angle & (FINEANGLES/2-1)]);
|
|
}
|
|
else
|
|
{
|
|
*x = 0;
|
|
*y = 0;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// PROC A_WeaponReady
|
|
//
|
|
// Readies a weapon for firing or bobbing with its three ancillary functions,
|
|
// DoReadyWeaponToSwitch(), DoReadyWeaponToFire() and DoReadyWeaponToBob().
|
|
//
|
|
//============================================================================
|
|
|
|
void DoReadyWeaponToSwitch (AActor * self)
|
|
{
|
|
// Prepare for switching action.
|
|
player_t *player;
|
|
if (self && (player = self->player))
|
|
player->cheats |= CF_WEAPONSWITCHOK;
|
|
}
|
|
|
|
void DoReadyWeaponToFire (AActor * self, bool prim, bool alt)
|
|
{
|
|
player_t *player;
|
|
AWeapon *weapon;
|
|
|
|
if (!self || !(player = self->player) || !(weapon = player->ReadyWeapon))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Change player from attack state
|
|
if (self->InStateSequence(self->state, self->MissileState) ||
|
|
self->InStateSequence(self->state, self->MeleeState))
|
|
{
|
|
static_cast<APlayerPawn *>(self)->PlayIdle ();
|
|
}
|
|
|
|
// Play ready sound, if any.
|
|
if (weapon->ReadySound && player->psprites[ps_weapon].state == weapon->FindState(NAME_Ready))
|
|
{
|
|
if (!(weapon->WeaponFlags & WIF_READYSNDHALF) || pr_wpnreadysnd() < 128)
|
|
{
|
|
S_Sound (self, CHAN_WEAPON, weapon->ReadySound, 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
// Prepare for firing action.
|
|
player->cheats |= ((prim ? CF_WEAPONREADY : 0) | (alt ? CF_WEAPONREADYALT : 0));
|
|
return;
|
|
}
|
|
|
|
void DoReadyWeaponToBob (AActor * self)
|
|
{
|
|
if (self && self->player && self->player->ReadyWeapon)
|
|
{
|
|
// Prepare for bobbing action.
|
|
self->player->cheats |= CF_WEAPONBOBBING;
|
|
self->player->psprites[ps_weapon].sx = 0;
|
|
self->player->psprites[ps_weapon].sy = WEAPONTOP;
|
|
}
|
|
}
|
|
|
|
// This function replaces calls to A_WeaponReady in other codepointers.
|
|
void DoReadyWeapon(AActor * self)
|
|
{
|
|
DoReadyWeaponToBob(self);
|
|
DoReadyWeaponToFire(self);
|
|
DoReadyWeaponToSwitch(self);
|
|
}
|
|
|
|
enum EWRF_Options
|
|
{
|
|
WRF_NoBob = 1,
|
|
WRF_NoFire = 12,
|
|
WRF_NoSwitch = 2,
|
|
WRF_NoPrimary = 4,
|
|
WRF_NoSecondary = 8,
|
|
};
|
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady)
|
|
{
|
|
ACTION_PARAM_START(1);
|
|
ACTION_PARAM_INT(paramflags, 0);
|
|
|
|
if (!(paramflags & WRF_NoSwitch)) DoReadyWeaponToSwitch(self);
|
|
if ((paramflags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(self,
|
|
(!(paramflags & WRF_NoPrimary)), (!(paramflags & WRF_NoSecondary)));
|
|
if (!(paramflags & WRF_NoBob)) DoReadyWeaponToBob(self);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC P_CheckWeaponFire
|
|
//
|
|
// The player can fire the weapon.
|
|
// [RH] This was in A_WeaponReady before, but that only works well when the
|
|
// weapon's ready frames have a one tic delay.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void P_CheckWeaponFire (player_t *player)
|
|
{
|
|
AWeapon *weapon = player->ReadyWeapon;
|
|
|
|
if (weapon == NULL)
|
|
return;
|
|
|
|
// Check for fire. Some weapons do not auto fire.
|
|
if ((player->cheats & CF_WEAPONREADY) && (player->cmd.ucmd.buttons & BT_ATTACK))
|
|
{
|
|
if (!player->attackdown || !(weapon->WeaponFlags & WIF_NOAUTOFIRE))
|
|
{
|
|
player->attackdown = true;
|
|
P_FireWeapon (player, NULL);
|
|
return;
|
|
}
|
|
}
|
|
else if ((player->cheats & CF_WEAPONREADYALT) && (player->cmd.ucmd.buttons & BT_ALTATTACK))
|
|
{
|
|
if (!player->attackdown || !(weapon->WeaponFlags & WIF_NOAUTOFIRE))
|
|
{
|
|
player->attackdown = true;
|
|
P_FireWeaponAlt (player, NULL);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
player->attackdown = false;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC P_CheckWeaponSwitch
|
|
//
|
|
// The player can change to another weapon at this time.
|
|
// [GZ] This was cut from P_CheckWeaponFire.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void P_CheckWeaponSwitch (player_t *player)
|
|
{
|
|
AWeapon *weapon;
|
|
|
|
if (!player || !(weapon = player->ReadyWeapon))
|
|
return;
|
|
|
|
// Put the weapon away if the player has a pending weapon or has died.
|
|
if ((player->morphTics == 0 && player->PendingWeapon != WP_NOCHANGE) || player->health <= 0)
|
|
{
|
|
P_SetPsprite (player, ps_weapon, weapon->GetDownState());
|
|
return;
|
|
}
|
|
else if (player->morphTics != 0)
|
|
{
|
|
// morphed classes cannot change weapons so don't even try again.
|
|
player->PendingWeapon = WP_NOCHANGE;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC A_ReFire
|
|
//
|
|
// The player can re-fire the weapon without lowering it entirely.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_ReFire)
|
|
{
|
|
ACTION_PARAM_START(1)
|
|
ACTION_PARAM_STATE(state, 0);
|
|
|
|
A_ReFire(self, state);
|
|
}
|
|
|
|
void A_ReFire(AActor *self, FState *state)
|
|
{
|
|
player_t *player = self->player;
|
|
|
|
if (NULL == player)
|
|
{
|
|
return;
|
|
}
|
|
if ((player->cmd.ucmd.buttons&BT_ATTACK)
|
|
&& !player->ReadyWeapon->bAltFire
|
|
&& player->PendingWeapon == WP_NOCHANGE && player->health)
|
|
{
|
|
player->refire++;
|
|
P_FireWeapon (player, state);
|
|
}
|
|
else if ((player->cmd.ucmd.buttons&BT_ALTATTACK)
|
|
&& player->ReadyWeapon->bAltFire
|
|
&& player->PendingWeapon == WP_NOCHANGE && player->health)
|
|
{
|
|
player->refire++;
|
|
P_FireWeaponAlt (player, state);
|
|
}
|
|
else
|
|
{
|
|
player->refire = 0;
|
|
player->ReadyWeapon->CheckAmmo (player->ReadyWeapon->bAltFire
|
|
? AWeapon::AltFire : AWeapon::PrimaryFire, true);
|
|
}
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AInventory, A_ClearReFire)
|
|
{
|
|
player_t *player = self->player;
|
|
|
|
if (NULL != player)
|
|
{
|
|
player->refire = 0;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC A_CheckReload
|
|
//
|
|
// Present in Doom, but unused. Also present in Strife, and actually used.
|
|
// This and what I call A_XBowReFire are actually the same thing in Strife,
|
|
// not two separate functions as I have them here.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AInventory, A_CheckReload)
|
|
{
|
|
if (self->player != NULL)
|
|
{
|
|
self->player->ReadyWeapon->CheckAmmo (
|
|
self->player->ReadyWeapon->bAltFire ? AWeapon::AltFire
|
|
: AWeapon::PrimaryFire, true);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC A_Lower
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AInventory, A_Lower)
|
|
{
|
|
player_t *player = self->player;
|
|
pspdef_t *psp;
|
|
|
|
if (NULL == player)
|
|
{
|
|
return;
|
|
}
|
|
psp = &player->psprites[ps_weapon];
|
|
if (player->morphTics || player->cheats & CF_INSTANTWEAPSWITCH)
|
|
{
|
|
psp->sy = WEAPONBOTTOM;
|
|
}
|
|
else
|
|
{
|
|
psp->sy += LOWERSPEED;
|
|
}
|
|
if (psp->sy < WEAPONBOTTOM)
|
|
{ // Not lowered all the way yet
|
|
return;
|
|
}
|
|
if (player->playerstate == PST_DEAD)
|
|
{ // Player is dead, so don't bring up a pending weapon
|
|
psp->sy = WEAPONBOTTOM;
|
|
return;
|
|
}
|
|
if (player->health <= 0)
|
|
{ // Player is dead, so keep the weapon off screen
|
|
P_SetPsprite (player, ps_weapon, NULL);
|
|
return;
|
|
}
|
|
/* if (player->PendingWeapon != WP_NOCHANGE)
|
|
{ // [RH] Make sure we're actually changing weapons.
|
|
player->ReadyWeapon = player->PendingWeapon;
|
|
} */
|
|
// [RH] Clear the flash state. Only needed for Strife.
|
|
P_SetPsprite (player, ps_flash, NULL);
|
|
P_BringUpWeapon (player);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC A_Raise
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
DEFINE_ACTION_FUNCTION(AInventory, A_Raise)
|
|
{
|
|
if (self == NULL)
|
|
{
|
|
return;
|
|
}
|
|
player_t *player = self->player;
|
|
pspdef_t *psp;
|
|
|
|
if (NULL == player)
|
|
{
|
|
return;
|
|
}
|
|
if (player->PendingWeapon != WP_NOCHANGE)
|
|
{
|
|
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetDownState());
|
|
return;
|
|
}
|
|
psp = &player->psprites[ps_weapon];
|
|
psp->sy -= RAISESPEED;
|
|
if (psp->sy > WEAPONTOP)
|
|
{ // Not raised all the way yet
|
|
return;
|
|
}
|
|
psp->sy = WEAPONTOP;
|
|
if (player->ReadyWeapon != NULL)
|
|
{
|
|
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
|
|
}
|
|
else
|
|
{
|
|
player->psprites[ps_weapon].state = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// A_GunFlash
|
|
//
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_GunFlash)
|
|
{
|
|
ACTION_PARAM_START(1)
|
|
ACTION_PARAM_STATE(flash, 0);
|
|
|
|
player_t *player = self->player;
|
|
|
|
if (NULL == player)
|
|
{
|
|
return;
|
|
}
|
|
player->mo->PlayAttacking2 ();
|
|
|
|
if (flash == NULL)
|
|
{
|
|
if (player->ReadyWeapon->bAltFire) flash = player->ReadyWeapon->FindState(NAME_AltFlash);
|
|
if (flash == NULL) flash = player->ReadyWeapon->FindState(NAME_Flash);
|
|
}
|
|
P_SetPsprite (player, ps_flash, flash);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WEAPON ATTACKS
|
|
//
|
|
|
|
//
|
|
// P_BulletSlope
|
|
// Sets a slope so a near miss is at aproximately
|
|
// the height of the intended target
|
|
//
|
|
|
|
angle_t P_BulletSlope (AActor *mo, AActor **pLineTarget)
|
|
{
|
|
static const int angdiff[3] = { -1<<26, 1<<26, 0 };
|
|
int i;
|
|
angle_t an;
|
|
angle_t pitch;
|
|
AActor *linetarget;
|
|
|
|
// see which target is to be aimed at
|
|
i = 2;
|
|
do
|
|
{
|
|
an = mo->angle + angdiff[i];
|
|
pitch = P_AimLineAttack (mo, an, 16*64*FRACUNIT, &linetarget);
|
|
|
|
if (mo->player != NULL &&
|
|
level.IsFreelookAllowed() &&
|
|
mo->player->userinfo.GetAimDist() <= ANGLE_1/2)
|
|
{
|
|
break;
|
|
}
|
|
} while (linetarget == NULL && --i >= 0);
|
|
if (pLineTarget != NULL)
|
|
{
|
|
*pLineTarget = linetarget;
|
|
}
|
|
return pitch;
|
|
}
|
|
|
|
|
|
//
|
|
// P_GunShot
|
|
//
|
|
void P_GunShot (AActor *mo, bool accurate, const PClass *pufftype, angle_t pitch)
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
|
|
damage = 5*(pr_gunshot()%3+1);
|
|
angle = mo->angle;
|
|
|
|
if (!accurate)
|
|
{
|
|
angle += pr_gunshot.Random2 () << 18;
|
|
}
|
|
|
|
P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_None, pufftype);
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AInventory, A_Light0)
|
|
{
|
|
if (self->player != NULL)
|
|
{
|
|
self->player->extralight = 0;
|
|
}
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AInventory, A_Light1)
|
|
{
|
|
if (self->player != NULL)
|
|
{
|
|
self->player->extralight = 1;
|
|
}
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(AInventory, A_Light2)
|
|
{
|
|
if (self->player != NULL)
|
|
{
|
|
self->player->extralight = 2;
|
|
}
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_Light)
|
|
{
|
|
ACTION_PARAM_START(1);
|
|
ACTION_PARAM_INT(light, 0);
|
|
|
|
if (self->player != NULL)
|
|
{
|
|
self->player->extralight = clamp<int>(light, 0, 20);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// PROC P_SetupPsprites
|
|
//
|
|
// Called at start of level for each player
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
void P_SetupPsprites(player_t *player)
|
|
{
|
|
int i;
|
|
|
|
// Remove all psprites
|
|
for (i = 0; i < NUMPSPRITES; i++)
|
|
{
|
|
player->psprites[i].state = NULL;
|
|
}
|
|
// Spawn the ready weapon
|
|
player->PendingWeapon = player->ReadyWeapon;
|
|
P_BringUpWeapon (player);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// PROC P_MovePsprites
|
|
//
|
|
// Called every tic by player thinking routine
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
void P_MovePsprites (player_t *player)
|
|
{
|
|
int i;
|
|
pspdef_t *psp;
|
|
FState *state;
|
|
|
|
// [RH] If you don't have a weapon, then the psprites should be NULL.
|
|
if (player->ReadyWeapon == NULL && (player->health > 0 || player->mo->DamageType != NAME_Fire))
|
|
{
|
|
P_SetPsprite (player, ps_weapon, NULL);
|
|
P_SetPsprite (player, ps_flash, NULL);
|
|
if (player->PendingWeapon != WP_NOCHANGE)
|
|
{
|
|
P_BringUpWeapon (player);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psp = &player->psprites[0];
|
|
for (i = 0; i < NUMPSPRITES; i++, psp++)
|
|
{
|
|
if ((state = psp->state) != NULL) // a null state means not active
|
|
{
|
|
// drop tic count and possibly change state
|
|
if (psp->tics != -1) // a -1 tic count never changes
|
|
{
|
|
psp->tics--;
|
|
|
|
// [BC] Apply double firing speed.
|
|
if ( psp->tics && ( player->cheats & CF_DOUBLEFIRINGSPEED ))
|
|
psp->tics--;
|
|
|
|
if(!psp->tics)
|
|
{
|
|
P_SetPsprite (player, i, psp->state->GetNextState());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
|
|
player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
|
|
if (player->cheats & CF_WEAPONSWITCHOK)
|
|
{
|
|
P_CheckWeaponSwitch (player);
|
|
}
|
|
if (player->cheats & (CF_WEAPONREADY | CF_WEAPONREADYALT))
|
|
{
|
|
P_CheckWeaponFire (player);
|
|
}
|
|
}
|
|
}
|
|
|
|
FArchive &operator<< (FArchive &arc, pspdef_t &def)
|
|
{
|
|
arc << def.state << def.tics << def.sx << def.sy
|
|
<< def.sprite << def.frame;
|
|
return arc;
|
|
}
|