mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-04-25 02:52:12 +00:00
1055 lines
20 KiB
C++
1055 lines
20 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "Precompiled.h"
|
|
#include "globaldata.h"
|
|
|
|
#include "doomdef.h"
|
|
#include "d_event.h"
|
|
|
|
|
|
#include "m_random.h"
|
|
#include "p_local.h"
|
|
#include "s_sound.h"
|
|
|
|
// State.
|
|
#include "doomstat.h"
|
|
|
|
// Data.
|
|
#include "sounds.h"
|
|
|
|
#include "p_pspr.h"
|
|
#include "d3xp/Game_local.h"
|
|
|
|
extern bool globalNetworking;
|
|
|
|
|
|
|
|
static const float PISTOL_MAGNITUDE_HIGH = 0.5f;
|
|
static const int PISTOL_DURATION_HIGH = 250;
|
|
static const float PISTOL_MAGNITUDE_LOW = 1.0f;
|
|
static const int PISTOL_DURATION_LOW = 150;
|
|
|
|
static const float SHOTGUN_MAGNITUDE_HIGH = 0.5f;
|
|
static const int SHOTGUN_DURATION_HIGH = 250;
|
|
static const float SHOTGUN_MAGNITUDE_LOW = 1.0f;
|
|
static const int SHOTGUN_DURATION_LOW = 350;
|
|
|
|
static const float CHAINGUN_MAGNITUDE_HIGH = 0.5f;
|
|
static const int CHAINGUN_DURATION_HIGH = 250;
|
|
static const float CHAINGUN_MAGNITUDE_LOW = 1.0f;
|
|
static const int CHAINGUN_DURATION_LOW = 150;
|
|
|
|
static const float PLASMAGUN_MAGNITUDE_HIGH = 0.5f;
|
|
static const int PLASMAGUN_DURATION_HIGH = 250;
|
|
static const float PLASMAGUN_MAGNITUDE_LOW = 1.0f;
|
|
static const int PLASMAGUN_DURATION_LOW = 150;
|
|
|
|
static const float SUPERSHOTGUN_MAGNITUDE_HIGH = 1.0f;
|
|
static const int SUPERSHOTGUN_DURATION_HIGH = 250;
|
|
static const float SUPERSHOTGUN_MAGNITUDE_LOW = 1.0f;
|
|
static const int SUPERSHOTGUN_DURATION_LOW = 350;
|
|
|
|
static const float ROCKET_MAGNITUDE_HIGH = 1.5f;
|
|
static const int ROCKET_DURATION_HIGH = 250;
|
|
static const float ROCKET_MAGNITUDE_LOW = 1.0f;
|
|
static const int ROCKET_DURATION_LOW = 350;
|
|
|
|
static const float BFG_MAGNITUDE_HIGH = 1.5f;
|
|
static const int BFG_DURATION_HIGH = 250;
|
|
static const float BFG_MAGNITUDE_LOW = 1.0f;
|
|
static const int BFG_DURATION_LOW = 400;
|
|
|
|
|
|
static const float SAW_IDL_MAGNITUDE_HIGH = 0.0f;
|
|
static const int SAW_IDL_DURATION_HIGH = 0;
|
|
static const float SAW_IDL_MAGNITUDE_LOW = 0.4f;
|
|
static const int SAW_IDL_DURATION_LOW = 150;
|
|
|
|
static const float SAW_ATK_MAGNITUDE_HIGH = 1.0f;
|
|
static const int SAW_ATK_DURATION_HIGH = 250;
|
|
static const float SAW_ATK_MAGNITUDE_LOW = 0.0f;
|
|
static const int SAW_ATK_DURATION_LOW = 0;
|
|
|
|
// plasma cells for a bfg attack
|
|
|
|
|
|
//
|
|
// P_SetPsprite
|
|
//
|
|
void
|
|
P_SetPsprite
|
|
( player_t* player,
|
|
int position,
|
|
statenum_t stnum )
|
|
{
|
|
pspdef_t* psp;
|
|
const state_t* state;
|
|
|
|
psp = &player->psprites[position];
|
|
|
|
do
|
|
{
|
|
if( !stnum )
|
|
{
|
|
// object removed itself
|
|
psp->state = NULL;
|
|
break;
|
|
}
|
|
|
|
state = &::g->states[stnum];
|
|
psp->state = state;
|
|
psp->tics = state->tics; // could be 0
|
|
|
|
if( state->misc1 )
|
|
{
|
|
// coordinate set
|
|
psp->sx = state->misc1 << FRACBITS;
|
|
psp->sy = state->misc2 << FRACBITS;
|
|
}
|
|
|
|
// Call action routine.
|
|
// Modified handling.
|
|
if( state->action )
|
|
{
|
|
state->action( player, psp );
|
|
if( !psp->state )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
stnum = psp->state->nextstate;
|
|
|
|
}
|
|
while( !psp->tics );
|
|
// an initial state of 0 could cycle through
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// P_CalcSwing
|
|
//
|
|
|
|
void P_CalcSwing( player_t* player )
|
|
{
|
|
fixed_t swing;
|
|
int angle;
|
|
|
|
// OPTIMIZE: tablify this.
|
|
// A LUT would allow for different modes,
|
|
// and add flexibility.
|
|
|
|
swing = player->bob;
|
|
|
|
angle = ( FINEANGLES / 70 *::g->leveltime )&FINEMASK;
|
|
::g->swingx = FixedMul( swing, finesine[angle] );
|
|
|
|
angle = ( FINEANGLES / 70 *::g->leveltime + FINEANGLES / 2 )&FINEMASK;
|
|
::g->swingy = -FixedMul( ::g->swingx, finesine[angle] );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// P_BringUpWeapon
|
|
// Starts bringing the pending weapon up
|
|
// from the bottom of the screen.
|
|
// Uses player
|
|
//
|
|
void P_BringUpWeapon( player_t* player )
|
|
{
|
|
statenum_t newstate;
|
|
|
|
if( player->pendingweapon == wp_nochange )
|
|
{
|
|
player->pendingweapon = player->readyweapon;
|
|
}
|
|
|
|
if( player->pendingweapon == wp_chainsaw && ( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_sawup );
|
|
}
|
|
|
|
newstate = ( statenum_t )( weaponinfo[player->pendingweapon].upstate );
|
|
|
|
player->pendingweapon = wp_nochange;
|
|
player->psprites[ps_weapon].sy = WEAPONBOTTOM;
|
|
|
|
P_SetPsprite( player, ps_weapon, newstate );
|
|
}
|
|
|
|
//
|
|
// P_CheckAmmo
|
|
// Returns true if there is enough ammo to shoot.
|
|
// If not, selects the next weapon to use.
|
|
//
|
|
qboolean P_CheckAmmo( player_t* player )
|
|
{
|
|
ammotype_t ammo;
|
|
int count;
|
|
|
|
ammo = weaponinfo[player->readyweapon].ammo;
|
|
|
|
// Minimal amount for one shot varies.
|
|
if( player->readyweapon == wp_bfg )
|
|
{
|
|
count = BFGCELLS;
|
|
}
|
|
else if( player->readyweapon == wp_supershotgun )
|
|
{
|
|
count = 2; // Double barrel.
|
|
}
|
|
else
|
|
{
|
|
count = 1; // Regular.
|
|
}
|
|
|
|
// Some do not need ammunition anyway.
|
|
// Return if current ammunition sufficient.
|
|
if( ammo == am_noammo || player->ammo[ammo] >= count )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Out of ammo, pick a weapon to change to.
|
|
// Preferences are set here.
|
|
do
|
|
{
|
|
if( player->weaponowned[wp_plasma]
|
|
&& player->ammo[am_cell]
|
|
&& ( ::g->gamemode != shareware ) )
|
|
{
|
|
player->pendingweapon = wp_plasma;
|
|
}
|
|
else if( player->weaponowned[wp_supershotgun]
|
|
&& player->ammo[am_shell] > 2
|
|
&& ( ::g->gamemode == commercial ) )
|
|
{
|
|
player->pendingweapon = wp_supershotgun;
|
|
}
|
|
else if( player->weaponowned[wp_chaingun]
|
|
&& player->ammo[am_clip] )
|
|
{
|
|
player->pendingweapon = wp_chaingun;
|
|
}
|
|
else if( player->weaponowned[wp_shotgun]
|
|
&& player->ammo[am_shell] )
|
|
{
|
|
player->pendingweapon = wp_shotgun;
|
|
}
|
|
else if( player->ammo[am_clip] )
|
|
{
|
|
player->pendingweapon = wp_pistol;
|
|
}
|
|
else if( player->weaponowned[wp_chainsaw] )
|
|
{
|
|
player->pendingweapon = wp_chainsaw;
|
|
}
|
|
else if( player->weaponowned[wp_missile]
|
|
&& player->ammo[am_misl] )
|
|
{
|
|
player->pendingweapon = wp_missile;
|
|
}
|
|
else if( player->weaponowned[wp_bfg]
|
|
&& player->ammo[am_cell] > 40
|
|
&& ( ::g->gamemode != shareware ) )
|
|
{
|
|
player->pendingweapon = wp_bfg;
|
|
}
|
|
else
|
|
{
|
|
// If everything fails.
|
|
player->pendingweapon = wp_fist;
|
|
}
|
|
|
|
}
|
|
while( player->pendingweapon == wp_nochange );
|
|
|
|
// Now set appropriate weapon overlay.
|
|
P_SetPsprite( player,
|
|
ps_weapon,
|
|
( statenum_t )( weaponinfo[player->readyweapon].downstate ) );
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//
|
|
// P_FireWeapon.
|
|
//
|
|
void P_FireWeapon( player_t* player )
|
|
{
|
|
statenum_t newstate;
|
|
|
|
if( !P_CheckAmmo( player ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
P_SetMobjState( player->mo, S_PLAY_ATK1 );
|
|
newstate = ( statenum_t )weaponinfo[player->readyweapon].atkstate;
|
|
P_SetPsprite( player, ps_weapon, newstate );
|
|
P_NoiseAlert( player->mo, player->mo );
|
|
|
|
if( player->readyweapon == wp_chainsaw )
|
|
{
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// P_DropWeapon
|
|
// Player died, so put the weapon away.
|
|
//
|
|
void P_DropWeapon( player_t* player )
|
|
{
|
|
P_SetPsprite( player,
|
|
ps_weapon,
|
|
( statenum_t )weaponinfo[player->readyweapon].downstate );
|
|
}
|
|
|
|
|
|
|
|
extern "C" {
|
|
//
|
|
// A_WeaponReady
|
|
// The player can fire the weapon
|
|
// or change to another weapon at this time.
|
|
// Follows after getting weapon up,
|
|
// or after previous attack/fire sequence.
|
|
//
|
|
void
|
|
A_WeaponReady
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
statenum_t newstate;
|
|
int angle;
|
|
|
|
// get out of attack state
|
|
if( player->mo->state == &::g->states[S_PLAY_ATK1]
|
|
|| player->mo->state == &::g->states[S_PLAY_ATK2] )
|
|
{
|
|
P_SetMobjState( player->mo, S_PLAY );
|
|
}
|
|
|
|
if( player->readyweapon == wp_chainsaw
|
|
&& psp->state == &::g->states[S_SAW] )
|
|
{
|
|
if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_sawidl );
|
|
}
|
|
}
|
|
|
|
// check for change
|
|
// if player is dead, put the weapon away
|
|
if( player->pendingweapon != wp_nochange || !player->health )
|
|
{
|
|
// change weapon
|
|
// (pending weapon should allready be validated)
|
|
newstate = ( statenum_t )weaponinfo[player->readyweapon].downstate;
|
|
P_SetPsprite( player, ps_weapon, newstate );
|
|
return;
|
|
}
|
|
|
|
// check for fire
|
|
// the missile launcher and bfg do not auto fire
|
|
if( player->cmd.buttons & BT_ATTACK )
|
|
{
|
|
if( !player->attackdown
|
|
|| ( player->readyweapon != wp_missile
|
|
&& player->readyweapon != wp_bfg ) )
|
|
{
|
|
player->attackdown = true;
|
|
P_FireWeapon( player );
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
player->attackdown = false;
|
|
}
|
|
|
|
// bob the weapon based on movement speed
|
|
angle = ( 128 *::g->leveltime )&FINEMASK;
|
|
psp->sx = FRACUNIT + FixedMul( player->bob, finecosine[angle] );
|
|
angle &= FINEANGLES / 2 - 1;
|
|
psp->sy = WEAPONTOP + FixedMul( player->bob, finesine[angle] );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// A_ReFire
|
|
// The player can re-fire the weapon
|
|
// without lowering it entirely.
|
|
//
|
|
void A_ReFire
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
|
|
// check for fire
|
|
// (if a weaponchange is pending, let it go through instead)
|
|
if( ( player->cmd.buttons & BT_ATTACK )
|
|
&& player->pendingweapon == wp_nochange
|
|
&& player->health )
|
|
{
|
|
player->refire++;
|
|
P_FireWeapon( player );
|
|
}
|
|
else
|
|
{
|
|
player->refire = 0;
|
|
P_CheckAmmo( player );
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
A_CheckReload
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
P_CheckAmmo( player );
|
|
#if 0
|
|
if( player->ammo[am_shell] < 2 )
|
|
{
|
|
P_SetPsprite( player, ps_weapon, S_DSNR1 );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// A_Lower
|
|
// Lowers current weapon,
|
|
// and changes weapon at bottom.
|
|
//
|
|
void
|
|
A_Lower
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
psp->sy += LOWERSPEED;
|
|
|
|
// Is already down.
|
|
if( psp->sy < WEAPONBOTTOM )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Player is dead.
|
|
if( player->playerstate == PST_DEAD )
|
|
{
|
|
psp->sy = WEAPONBOTTOM;
|
|
|
|
// don't bring weapon back up
|
|
return;
|
|
}
|
|
|
|
// The old weapon has been lowered off the screen,
|
|
// so change the weapon and start raising it
|
|
if( !player->health )
|
|
{
|
|
// Player is dead, so keep the weapon off screen.
|
|
P_SetPsprite( player, ps_weapon, S_NULL );
|
|
return;
|
|
}
|
|
|
|
player->readyweapon = player->pendingweapon;
|
|
|
|
P_BringUpWeapon( player );
|
|
}
|
|
|
|
|
|
//
|
|
// A_Raise
|
|
//
|
|
void
|
|
A_Raise
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
statenum_t newstate;
|
|
|
|
psp->sy -= RAISESPEED;
|
|
|
|
if( psp->sy > WEAPONTOP )
|
|
{
|
|
return;
|
|
}
|
|
|
|
psp->sy = WEAPONTOP;
|
|
|
|
// The weapon has been raised all the way,
|
|
// so change to the ready state.
|
|
newstate = ( statenum_t )weaponinfo[player->readyweapon].readystate;
|
|
|
|
P_SetPsprite( player, ps_weapon, newstate );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// A_GunFlash
|
|
//
|
|
void
|
|
A_GunFlash
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
P_SetMobjState( player->mo, S_PLAY_ATK2 );
|
|
P_SetPsprite( player, ps_flash, ( statenum_t )weaponinfo[player->readyweapon].flashstate );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WEAPON ATTACKS
|
|
//
|
|
|
|
|
|
//
|
|
// A_Punch
|
|
//
|
|
void
|
|
A_Punch
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
int slope;
|
|
|
|
damage = ( P_Random() % 10 + 1 ) << 1;
|
|
|
|
if( player->powers[pw_strength] )
|
|
{
|
|
damage *= 10;
|
|
}
|
|
|
|
angle = player->mo->angle;
|
|
angle += ( P_Random() - P_Random() ) << 18;
|
|
slope = P_AimLineAttack( player->mo, angle, MELEERANGE );
|
|
P_LineAttack( player->mo, angle, MELEERANGE, slope, damage );
|
|
|
|
// turn to face target
|
|
if( ::g->linetarget )
|
|
{
|
|
S_StartSound( player->mo, sfx_punch );
|
|
player->mo->angle = R_PointToAngle2( player->mo->x,
|
|
player->mo->y,
|
|
::g->linetarget->x,
|
|
::g->linetarget->y );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// A_Saw
|
|
//
|
|
void
|
|
A_Saw
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
int slope;
|
|
|
|
damage = 2 * ( P_Random() % 10 + 1 );
|
|
angle = player->mo->angle;
|
|
angle += ( P_Random() - P_Random() ) << 18;
|
|
|
|
// use meleerange + 1 se the puff doesn't skip the flash
|
|
slope = P_AimLineAttack( player->mo, angle, MELEERANGE + 1 );
|
|
P_LineAttack( player->mo, angle, MELEERANGE + 1, slope, damage );
|
|
|
|
if( !::g->linetarget )
|
|
{
|
|
if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_sawful );
|
|
}
|
|
return;
|
|
}
|
|
if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_sawhit );
|
|
}
|
|
|
|
// turn to face target
|
|
angle = R_PointToAngle2( player->mo->x, player->mo->y,
|
|
::g->linetarget->x, ::g->linetarget->y );
|
|
if( angle - player->mo->angle > ANG180 )
|
|
{
|
|
if( angle - player->mo->angle < UINT_MAX - ANG90 / 20 + 1 ) // SRS - make uint math explicit
|
|
{
|
|
player->mo->angle = angle + ANG90 / 21;
|
|
}
|
|
else
|
|
{
|
|
player->mo->angle -= ANG90 / 20;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( angle - player->mo->angle > ANG90 / 20 )
|
|
{
|
|
player->mo->angle = angle - ANG90 / 21;
|
|
}
|
|
else
|
|
{
|
|
player->mo->angle += ANG90 / 20;
|
|
}
|
|
}
|
|
player->mo->flags |= MF_JUSTATTACKED;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// A_FireMissile
|
|
//
|
|
void
|
|
A_FireMissile
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
if( ( player->cheats & CF_INFAMMO ) == false )
|
|
{
|
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
|
}
|
|
P_SpawnPlayerMissile( player->mo, MT_ROCKET );
|
|
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// A_FireBFG
|
|
//
|
|
void
|
|
A_FireBFG
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
if( ( player->cheats & CF_INFAMMO ) == false )
|
|
{
|
|
player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS;
|
|
}
|
|
|
|
P_SpawnPlayerMissile( player->mo, MT_BFG );
|
|
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// A_FirePlasma
|
|
//
|
|
void
|
|
A_FirePlasma
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
if( ( player->cheats & CF_INFAMMO ) == false )
|
|
{
|
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
|
}
|
|
|
|
P_SetPsprite( player,
|
|
ps_flash,
|
|
( statenum_t )( weaponinfo[player->readyweapon].flashstate + ( P_Random() & 1 ) ) );
|
|
|
|
P_SpawnPlayerMissile( player->mo, MT_PLASMA );
|
|
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// P_BulletSlope
|
|
// Sets a slope so a near miss is at aproximately
|
|
// the height of the intended target
|
|
//
|
|
|
|
|
|
void P_BulletSlope( mobj_t* mo )
|
|
{
|
|
angle_t an;
|
|
|
|
// see which target is to be aimed at
|
|
an = mo->angle;
|
|
::g->bulletslope = P_AimLineAttack( mo, an, 16 * 64 * FRACUNIT );
|
|
|
|
if( !::g->linetarget )
|
|
{
|
|
an += 1 << 26;
|
|
::g->bulletslope = P_AimLineAttack( mo, an, 16 * 64 * FRACUNIT );
|
|
if( !::g->linetarget )
|
|
{
|
|
an -= 2 << 26;
|
|
::g->bulletslope = P_AimLineAttack( mo, an, 16 * 64 * FRACUNIT );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// P_GunShot
|
|
//
|
|
void
|
|
P_GunShot
|
|
( mobj_t* mo,
|
|
qboolean accurate )
|
|
{
|
|
angle_t angle;
|
|
int damage;
|
|
|
|
damage = 5 * ( P_Random() % 3 + 1 );
|
|
angle = mo->angle;
|
|
|
|
if( !accurate )
|
|
{
|
|
angle += ( P_Random() - P_Random() ) << 18;
|
|
}
|
|
|
|
P_LineAttack( mo, angle, MISSILERANGE, ::g->bulletslope, damage );
|
|
}
|
|
|
|
|
|
//
|
|
// A_FirePistol
|
|
//
|
|
void
|
|
A_FirePistol
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_pistol );
|
|
}
|
|
|
|
P_SetMobjState( player->mo, S_PLAY_ATK2 );
|
|
if( ( player->cheats & CF_INFAMMO ) == false )
|
|
{
|
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
|
}
|
|
|
|
P_SetPsprite( player,
|
|
ps_flash,
|
|
( statenum_t )weaponinfo[player->readyweapon].flashstate );
|
|
|
|
P_BulletSlope( player->mo );
|
|
P_GunShot( player->mo, !player->refire );
|
|
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// A_FireShotgun
|
|
//
|
|
void
|
|
A_FireShotgun
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
int i;
|
|
|
|
if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_shotgn );
|
|
}
|
|
P_SetMobjState( player->mo, S_PLAY_ATK2 );
|
|
|
|
if( ( player->cheats & CF_INFAMMO ) == false )
|
|
{
|
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
|
}
|
|
|
|
P_SetPsprite( player,
|
|
ps_flash,
|
|
( statenum_t )weaponinfo[player->readyweapon].flashstate );
|
|
|
|
P_BulletSlope( player->mo );
|
|
|
|
for( i = 0 ; i < 7 ; i++ )
|
|
{
|
|
P_GunShot( player->mo, false );
|
|
}
|
|
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// A_FireShotgun2
|
|
//
|
|
void
|
|
A_FireShotgun2
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
int i;
|
|
angle_t angle;
|
|
int damage;
|
|
|
|
|
|
if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_dshtgn );
|
|
}
|
|
P_SetMobjState( player->mo, S_PLAY_ATK2 );
|
|
|
|
if( ( player->cheats & CF_INFAMMO ) == false )
|
|
{
|
|
player->ammo[weaponinfo[player->readyweapon].ammo] -= 2;
|
|
}
|
|
|
|
P_SetPsprite( player,
|
|
ps_flash,
|
|
( statenum_t )weaponinfo[player->readyweapon].flashstate );
|
|
|
|
P_BulletSlope( player->mo );
|
|
|
|
for( i = 0 ; i < 20 ; i++ )
|
|
{
|
|
damage = 5 * ( P_Random() % 3 + 1 );
|
|
angle = player->mo->angle;
|
|
angle += ( P_Random() - P_Random() ) << 19;
|
|
P_LineAttack( player->mo,
|
|
angle,
|
|
MISSILERANGE,
|
|
::g->bulletslope + ( ( P_Random() - P_Random() ) << 5 ), damage );
|
|
}
|
|
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// A_FireCGun
|
|
//
|
|
void
|
|
A_FireCGun
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
if( globalNetworking || ( player == &::g->players[::g->consoleplayer] ) )
|
|
{
|
|
S_StartSound( player->mo, sfx_pistol );
|
|
}
|
|
|
|
if( !player->ammo[weaponinfo[player->readyweapon].ammo] )
|
|
{
|
|
return;
|
|
}
|
|
|
|
P_SetMobjState( player->mo, S_PLAY_ATK2 );
|
|
if( ( player->cheats & CF_INFAMMO ) == false )
|
|
{
|
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
|
}
|
|
P_SetPsprite( player,
|
|
ps_flash,
|
|
|
|
( statenum_t )(
|
|
weaponinfo[player->readyweapon].flashstate
|
|
+ psp->state
|
|
- &::g->states[S_CHAIN1] ) );
|
|
|
|
P_BulletSlope( player->mo );
|
|
|
|
P_GunShot( player->mo, !player->refire );
|
|
|
|
if( ::g->plyr == player )
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// ?
|
|
//
|
|
void A_Light0( player_t* player, pspdef_t* psp )
|
|
{
|
|
player->extralight = 0;
|
|
}
|
|
|
|
void A_Light1( player_t* player, pspdef_t* psp )
|
|
{
|
|
player->extralight = 1;
|
|
}
|
|
|
|
void A_Light2( player_t* player, pspdef_t* psp )
|
|
{
|
|
player->extralight = 2;
|
|
}
|
|
|
|
|
|
//
|
|
// A_BFGSpray
|
|
// Spawn a BFG explosion on every monster in view
|
|
//
|
|
void A_BFGSpray( mobj_t* mo, void* )
|
|
{
|
|
int i;
|
|
int j;
|
|
int damage;
|
|
angle_t an;
|
|
|
|
// offset angles from its attack angle
|
|
for( i = 0 ; i < 40 ; i++ )
|
|
{
|
|
an = mo->angle - ANG90 / 2 + ANG90 / 40 * i;
|
|
|
|
// mo->target is the originator (player)
|
|
// of the missile
|
|
P_AimLineAttack( mo->target, an, 16 * 64 * FRACUNIT );
|
|
|
|
if( !::g->linetarget )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
P_SpawnMobj( ::g->linetarget->x,
|
|
::g->linetarget->y,
|
|
::g->linetarget->z + ( ::g->linetarget->height >> 2 ),
|
|
MT_EXTRABFG );
|
|
|
|
damage = 0;
|
|
for( j = 0; j < 15; j++ )
|
|
{
|
|
damage += ( P_Random() & 7 ) + 1;
|
|
}
|
|
|
|
P_DamageMobj( ::g->linetarget, mo->target, mo->target, damage );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// A_BFGsound
|
|
//
|
|
void
|
|
A_BFGsound
|
|
( player_t* player,
|
|
pspdef_t* psp )
|
|
{
|
|
S_StartSound( player->mo, sfx_bfg );
|
|
}
|
|
|
|
}; // extern "C"
|
|
|
|
|
|
//
|
|
// 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 gun
|
|
player->pendingweapon = player->readyweapon;
|
|
P_BringUpWeapon( player );
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// P_MovePsprites
|
|
// Called every tic by player thinking routine.
|
|
//
|
|
void P_MovePsprites( player_t* player )
|
|
{
|
|
int i;
|
|
pspdef_t* psp;
|
|
const state_t* state;
|
|
|
|
psp = &player->psprites[0];
|
|
for( i = 0 ; i < NUMPSPRITES ; i++, psp++ )
|
|
{
|
|
// a null state means not active
|
|
if( ( state = psp->state ) )
|
|
{
|
|
// drop tic count and possibly change state
|
|
|
|
// a -1 tic count never changes
|
|
if( psp->tics != -1 )
|
|
{
|
|
psp->tics--;
|
|
if( !psp->tics )
|
|
{
|
|
P_SetPsprite( player, i, psp->state->nextstate );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
|
|
player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
|
|
}
|
|
|