mirror of
https://github.com/DrBeef/DVR.git
synced 2024-12-21 10:01:47 +00:00
830 lines
21 KiB
C
830 lines
21 KiB
C
|
/* Emacs style mode select -*- C++ -*-
|
||
|
*-----------------------------------------------------------------------------
|
||
|
*
|
||
|
*
|
||
|
* PrBoom: a Doom port merged with LxDoom and LSDLDoom
|
||
|
* based on BOOM, a modified and improved DOOM engine
|
||
|
* Copyright (C) 1999 by
|
||
|
* id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
|
||
|
* Copyright (C) 1999-2000 by
|
||
|
* Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
|
||
|
* Copyright 2005, 2006 by
|
||
|
* Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
|
||
|
*
|
||
|
* This program 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 2
|
||
|
* of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This program 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 this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||
|
* 02111-1307, USA.
|
||
|
*
|
||
|
* DESCRIPTION:
|
||
|
* Weapon sprite animation, weapon objects.
|
||
|
* Action functions for weapons.
|
||
|
*
|
||
|
*-----------------------------------------------------------------------------*/
|
||
|
|
||
|
#include "doomstat.h"
|
||
|
#include "r_main.h"
|
||
|
#include "p_map.h"
|
||
|
#include "p_inter.h"
|
||
|
#include "p_pspr.h"
|
||
|
#include "p_enemy.h"
|
||
|
#include "m_random.h"
|
||
|
#include "s_sound.h"
|
||
|
#include "sounds.h"
|
||
|
#include "d_event.h"
|
||
|
#include "r_demo.h"
|
||
|
|
||
|
#define LOWERSPEED (FRACUNIT*6)
|
||
|
#define RAISESPEED (FRACUNIT*6)
|
||
|
#define WEAPONBOTTOM (FRACUNIT*128)
|
||
|
#define WEAPONTOP (FRACUNIT*32)
|
||
|
|
||
|
#define BFGCELLS bfgcells /* Ty 03/09/98 externalized in p_inter.c */
|
||
|
|
||
|
extern void P_Thrust(player_t *, angle_t, fixed_t);
|
||
|
|
||
|
// The following array holds the recoil values // phares
|
||
|
|
||
|
static const int recoil_values[] = { // phares
|
||
|
10, // wp_fist
|
||
|
10, // wp_pistol
|
||
|
30, // wp_shotgun
|
||
|
10, // wp_chaingun
|
||
|
100,// wp_missile
|
||
|
20, // wp_plasma
|
||
|
100,// wp_bfg
|
||
|
0, // wp_chainsaw
|
||
|
80 // wp_supershotgun
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// P_SetPsprite
|
||
|
//
|
||
|
|
||
|
static void P_SetPsprite(player_t *player, int position, statenum_t stnum)
|
||
|
{
|
||
|
pspdef_t *psp = &player->psprites[position];
|
||
|
|
||
|
do
|
||
|
{
|
||
|
state_t *state;
|
||
|
|
||
|
if (!stnum)
|
||
|
{
|
||
|
// object removed itself
|
||
|
psp->state = NULL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
state = &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_BringUpWeapon
|
||
|
// Starts bringing the pending weapon up
|
||
|
// from the bottom of the screen.
|
||
|
// Uses player
|
||
|
//
|
||
|
|
||
|
static void P_BringUpWeapon(player_t *player)
|
||
|
{
|
||
|
statenum_t newstate;
|
||
|
|
||
|
if (player->pendingweapon == wp_nochange)
|
||
|
player->pendingweapon = player->readyweapon;
|
||
|
|
||
|
if (player->pendingweapon == wp_chainsaw)
|
||
|
S_StartSound (player->mo, sfx_sawup);
|
||
|
|
||
|
newstate = weaponinfo[player->pendingweapon].upstate;
|
||
|
|
||
|
player->pendingweapon = wp_nochange;
|
||
|
// killough 12/98: prevent pistol from starting visibly at bottom of screen:
|
||
|
player->psprites[ps_weapon].sy =
|
||
|
mbf_features ? WEAPONBOTTOM+FRACUNIT*2 : WEAPONBOTTOM;
|
||
|
|
||
|
P_SetPsprite(player, ps_weapon, newstate);
|
||
|
}
|
||
|
|
||
|
// The first set is where the weapon preferences from // killough,
|
||
|
// default.cfg are stored. These values represent the keys used // phares
|
||
|
// in DOOM2 to bring up the weapon, i.e. 6 = plasma gun. These // |
|
||
|
// are NOT the wp_* constants. // V
|
||
|
|
||
|
int weapon_preferences[2][NUMWEAPONS+1] = {
|
||
|
{6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // !compatibility preferences
|
||
|
{6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // compatibility preferences
|
||
|
};
|
||
|
|
||
|
// P_SwitchWeapon checks current ammo levels and gives you the
|
||
|
// most preferred weapon with ammo. It will not pick the currently
|
||
|
// raised weapon. When called from P_CheckAmmo this won't matter,
|
||
|
// because the raised weapon has no ammo anyway. When called from
|
||
|
// G_BuildTiccmd you want to toggle to a different weapon regardless.
|
||
|
|
||
|
int P_SwitchWeapon(player_t *player)
|
||
|
{
|
||
|
int *prefer = weapon_preferences[demo_compatibility!=0]; // killough 3/22/98
|
||
|
int currentweapon = player->readyweapon;
|
||
|
int newweapon = currentweapon;
|
||
|
int i = NUMWEAPONS+1; // killough 5/2/98
|
||
|
|
||
|
// killough 2/8/98: follow preferences and fix BFG/SSG bugs
|
||
|
|
||
|
do
|
||
|
switch (*prefer++)
|
||
|
{
|
||
|
case 1:
|
||
|
if (!player->powers[pw_strength]) // allow chainsaw override
|
||
|
break;
|
||
|
case 0:
|
||
|
newweapon = wp_fist;
|
||
|
break;
|
||
|
case 2:
|
||
|
if (player->ammo[am_clip])
|
||
|
newweapon = wp_pistol;
|
||
|
break;
|
||
|
case 3:
|
||
|
if (player->weaponowned[wp_shotgun] && player->ammo[am_shell])
|
||
|
newweapon = wp_shotgun;
|
||
|
break;
|
||
|
case 4:
|
||
|
if (player->weaponowned[wp_chaingun] && player->ammo[am_clip])
|
||
|
newweapon = wp_chaingun;
|
||
|
break;
|
||
|
case 5:
|
||
|
if (player->weaponowned[wp_missile] && player->ammo[am_misl])
|
||
|
newweapon = wp_missile;
|
||
|
break;
|
||
|
case 6:
|
||
|
if (player->weaponowned[wp_plasma] && player->ammo[am_cell] &&
|
||
|
gamemode != shareware)
|
||
|
newweapon = wp_plasma;
|
||
|
break;
|
||
|
case 7:
|
||
|
if (player->weaponowned[wp_bfg] && gamemode != shareware &&
|
||
|
player->ammo[am_cell] >= (demo_compatibility ? 41 : 40))
|
||
|
newweapon = wp_bfg;
|
||
|
break;
|
||
|
case 8:
|
||
|
if (player->weaponowned[wp_chainsaw])
|
||
|
newweapon = wp_chainsaw;
|
||
|
break;
|
||
|
case 9:
|
||
|
if (player->weaponowned[wp_supershotgun] && gamemode == commercial &&
|
||
|
player->ammo[am_shell] >= (demo_compatibility ? 3 : 2))
|
||
|
newweapon = wp_supershotgun;
|
||
|
break;
|
||
|
}
|
||
|
while (newweapon==currentweapon && --i); // killough 5/2/98
|
||
|
return newweapon;
|
||
|
}
|
||
|
|
||
|
// killough 5/2/98: whether consoleplayer prefers weapon w1 over weapon w2.
|
||
|
int P_WeaponPreferred(int w1, int w2)
|
||
|
{
|
||
|
return
|
||
|
(weapon_preferences[0][0] != ++w2 && (weapon_preferences[0][0] == ++w1 ||
|
||
|
(weapon_preferences[0][1] != w2 && (weapon_preferences[0][1] == w1 ||
|
||
|
(weapon_preferences[0][2] != w2 && (weapon_preferences[0][2] == w1 ||
|
||
|
(weapon_preferences[0][3] != w2 && (weapon_preferences[0][3] == w1 ||
|
||
|
(weapon_preferences[0][4] != w2 && (weapon_preferences[0][4] == w1 ||
|
||
|
(weapon_preferences[0][5] != w2 && (weapon_preferences[0][5] == w1 ||
|
||
|
(weapon_preferences[0][6] != w2 && (weapon_preferences[0][6] == w1 ||
|
||
|
(weapon_preferences[0][7] != w2 && (weapon_preferences[0][7] == w1
|
||
|
))))))))))))))));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// P_CheckAmmo
|
||
|
// Returns true if there is enough ammo to shoot.
|
||
|
// If not, selects the next weapon to use.
|
||
|
// (only in demo_compatibility mode -- killough 3/22/98)
|
||
|
//
|
||
|
|
||
|
boolean P_CheckAmmo(player_t *player)
|
||
|
{
|
||
|
ammotype_t ammo = weaponinfo[player->readyweapon].ammo;
|
||
|
int count = 1; // Regular
|
||
|
|
||
|
if (player->readyweapon == wp_bfg) // Minimal amount for one shot varies.
|
||
|
count = BFGCELLS;
|
||
|
else
|
||
|
if (player->readyweapon == wp_supershotgun) // Double barrel.
|
||
|
count = 2;
|
||
|
|
||
|
// 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.
|
||
|
//
|
||
|
// killough 3/22/98: for old demos we do the switch here and now;
|
||
|
// for Boom games we cannot do this, and have different player
|
||
|
// preferences across demos or networks, so we have to use the
|
||
|
// G_BuildTiccmd() interface instead of making the switch here.
|
||
|
|
||
|
if (demo_compatibility)
|
||
|
{
|
||
|
player->pendingweapon = P_SwitchWeapon(player); // phares
|
||
|
// Now set appropriate weapon overlay.
|
||
|
P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// P_FireWeapon.
|
||
|
//
|
||
|
|
||
|
static void P_FireWeapon(player_t *player)
|
||
|
{
|
||
|
statenum_t newstate;
|
||
|
|
||
|
if (!P_CheckAmmo(player))
|
||
|
return;
|
||
|
|
||
|
P_SetMobjState(player->mo, S_PLAY_ATK1);
|
||
|
newstate = weaponinfo[player->readyweapon].atkstate;
|
||
|
P_SetPsprite(player, ps_weapon, newstate);
|
||
|
P_NoiseAlert(player->mo, player->mo);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// P_DropWeapon
|
||
|
// Player died, so put the weapon away.
|
||
|
//
|
||
|
|
||
|
void P_DropWeapon(player_t *player)
|
||
|
{
|
||
|
P_SetPsprite(player, ps_weapon, weaponinfo[player->readyweapon].downstate);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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)
|
||
|
{
|
||
|
// get out of attack state
|
||
|
if (player->mo->state == &states[S_PLAY_ATK1]
|
||
|
|| player->mo->state == &states[S_PLAY_ATK2] )
|
||
|
P_SetMobjState(player->mo, S_PLAY);
|
||
|
|
||
|
if (player->readyweapon == wp_chainsaw && psp->state == &states[S_SAW])
|
||
|
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 already be validated)
|
||
|
statenum_t newstate = 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
|
||
|
{
|
||
|
int angle = (128*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)
|
||
|
{
|
||
|
if (!P_CheckAmmo(player) && compatibility_level >= prboom_4_compatibility) {
|
||
|
/* cph 2002/08/08 - In old Doom, P_CheckAmmo would start the weapon lowering
|
||
|
* immediately. This was lost in Boom when the weapon switching logic was
|
||
|
* rewritten. But we must tell Doom that we don't need to complete the
|
||
|
* reload frames for the weapon here. G_BuildTiccmd will set ->pendingweapon
|
||
|
* for us later on. */
|
||
|
P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
return; // don't bring weapon back up
|
||
|
}
|
||
|
|
||
|
// 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 = weaponinfo[player->readyweapon].readystate;
|
||
|
|
||
|
P_SetPsprite(player, ps_weapon, newstate);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Weapons now recoil, amount depending on the weapon. // phares
|
||
|
// // |
|
||
|
// The P_SetPsprite call in each of the weapon firing routines // V
|
||
|
// was moved here so the recoil could be synched with the
|
||
|
// muzzle flash, rather than the pressing of the trigger.
|
||
|
// The BFG delay caused this to be necessary.
|
||
|
|
||
|
static void A_FireSomething(player_t* player,int adder)
|
||
|
{
|
||
|
P_SetPsprite(player, ps_flash,
|
||
|
weaponinfo[player->readyweapon].flashstate+adder);
|
||
|
|
||
|
// killough 3/27/98: prevent recoil in no-clipping mode
|
||
|
if (!(player->mo->flags & MF_NOCLIP))
|
||
|
if (!compatibility && weapon_recoil)
|
||
|
P_Thrust(player,
|
||
|
ANG180+player->mo->angle, // ^
|
||
|
2048*recoil_values[player->readyweapon]); // |
|
||
|
} // phares
|
||
|
|
||
|
//
|
||
|
// A_GunFlash
|
||
|
//
|
||
|
|
||
|
void A_GunFlash(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
P_SetMobjState(player->mo, S_PLAY_ATK2);
|
||
|
|
||
|
A_FireSomething(player,0); // phares
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// WEAPON ATTACKS
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// A_Punch
|
||
|
//
|
||
|
|
||
|
void A_Punch(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
angle_t angle;
|
||
|
int t, slope, damage = (P_Random(pr_punch)%10+1)<<1;
|
||
|
|
||
|
if (player->powers[pw_strength])
|
||
|
damage *= 10;
|
||
|
|
||
|
angle = player->mo->angle;
|
||
|
|
||
|
// killough 5/5/98: remove dependence on order of evaluation:
|
||
|
t = P_Random(pr_punchangle);
|
||
|
angle += (t - P_Random(pr_punchangle))<<18;
|
||
|
|
||
|
/* killough 8/2/98: make autoaiming prefer enemies */
|
||
|
if (!mbf_features ||
|
||
|
(slope = P_AimLineAttack(player->mo, angle, MELEERANGE, MF_FRIEND),
|
||
|
!linetarget))
|
||
|
slope = P_AimLineAttack(player->mo, angle, MELEERANGE, 0);
|
||
|
|
||
|
P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
|
||
|
|
||
|
if (!linetarget)
|
||
|
return;
|
||
|
|
||
|
S_StartSound(player->mo, sfx_punch);
|
||
|
|
||
|
// turn to face target
|
||
|
|
||
|
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y,
|
||
|
linetarget->x, linetarget->y);
|
||
|
R_SmoothPlaying_Reset(player); // e6y
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_Saw
|
||
|
//
|
||
|
|
||
|
void A_Saw(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
int slope, damage = 2*(P_Random(pr_saw)%10+1);
|
||
|
angle_t angle = player->mo->angle;
|
||
|
// killough 5/5/98: remove dependence on order of evaluation:
|
||
|
int t = P_Random(pr_saw);
|
||
|
angle += (t - P_Random(pr_saw))<<18;
|
||
|
|
||
|
/* Use meleerange + 1 so that the puff doesn't skip the flash
|
||
|
* killough 8/2/98: make autoaiming prefer enemies */
|
||
|
if (!mbf_features ||
|
||
|
(slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, MF_FRIEND),
|
||
|
!linetarget))
|
||
|
slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, 0);
|
||
|
|
||
|
P_LineAttack(player->mo, angle, MELEERANGE+1, slope, damage);
|
||
|
|
||
|
if (!linetarget)
|
||
|
{
|
||
|
S_StartSound(player->mo, sfx_sawful);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
S_StartSound(player->mo, sfx_sawhit);
|
||
|
|
||
|
// turn to face target
|
||
|
angle = R_PointToAngle2(player->mo->x, player->mo->y,
|
||
|
linetarget->x, linetarget->y);
|
||
|
|
||
|
if (angle - player->mo->angle > ANG180) {
|
||
|
if (angle - player->mo->angle < -ANG90/20)
|
||
|
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;
|
||
|
R_SmoothPlaying_Reset(player); // e6y
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_FireMissile
|
||
|
//
|
||
|
|
||
|
void A_FireMissile(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
||
|
P_SpawnPlayerMissile(player->mo, MT_ROCKET);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_FireBFG
|
||
|
//
|
||
|
|
||
|
void A_FireBFG(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS;
|
||
|
P_SpawnPlayerMissile(player->mo, MT_BFG);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_FirePlasma
|
||
|
//
|
||
|
|
||
|
void A_FirePlasma(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
||
|
|
||
|
A_FireSomething(player,P_Random(pr_plasma)&1); // phares
|
||
|
P_SpawnPlayerMissile(player->mo, MT_PLASMA);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// P_BulletSlope
|
||
|
// Sets a slope so a near miss is at aproximately
|
||
|
// the height of the intended target
|
||
|
//
|
||
|
|
||
|
static fixed_t bulletslope;
|
||
|
|
||
|
static void P_BulletSlope(mobj_t *mo)
|
||
|
{
|
||
|
angle_t an = mo->angle; // see which target is to be aimed at
|
||
|
|
||
|
/* killough 8/2/98: make autoaiming prefer enemies */
|
||
|
uint_64_t mask = mbf_features ? MF_FRIEND : 0;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
bulletslope = P_AimLineAttack(mo, an, 16*64*FRACUNIT, mask);
|
||
|
if (!linetarget)
|
||
|
bulletslope = P_AimLineAttack(mo, an += 1<<26, 16*64*FRACUNIT, mask);
|
||
|
if (!linetarget)
|
||
|
bulletslope = P_AimLineAttack(mo, an -= 2<<26, 16*64*FRACUNIT, mask);
|
||
|
}
|
||
|
while (mask && (mask=0, !linetarget)); /* killough 8/2/98 */
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// P_GunShot
|
||
|
//
|
||
|
|
||
|
static void P_GunShot(mobj_t *mo, boolean accurate)
|
||
|
{
|
||
|
int damage = 5*(P_Random(pr_gunshot)%3+1);
|
||
|
angle_t angle = mo->angle;
|
||
|
|
||
|
if (!accurate)
|
||
|
{ // killough 5/5/98: remove dependence on order of evaluation:
|
||
|
int t = P_Random(pr_misfire);
|
||
|
angle += (t - P_Random(pr_misfire))<<18;
|
||
|
}
|
||
|
|
||
|
P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_FirePistol
|
||
|
//
|
||
|
|
||
|
void A_FirePistol(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
S_StartSound(player->mo, sfx_pistol);
|
||
|
|
||
|
P_SetMobjState(player->mo, S_PLAY_ATK2);
|
||
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
||
|
|
||
|
A_FireSomething(player,0); // phares
|
||
|
P_BulletSlope(player->mo);
|
||
|
P_GunShot(player->mo, !player->refire);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_FireShotgun
|
||
|
//
|
||
|
|
||
|
void A_FireShotgun(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
S_StartSound(player->mo, sfx_shotgn);
|
||
|
P_SetMobjState(player->mo, S_PLAY_ATK2);
|
||
|
|
||
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
||
|
|
||
|
A_FireSomething(player,0); // phares
|
||
|
|
||
|
P_BulletSlope(player->mo);
|
||
|
|
||
|
for (i=0; i<7; i++)
|
||
|
P_GunShot(player->mo, false);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_FireShotgun2
|
||
|
//
|
||
|
|
||
|
void A_FireShotgun2(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
S_StartSound(player->mo, sfx_dshtgn);
|
||
|
P_SetMobjState(player->mo, S_PLAY_ATK2);
|
||
|
player->ammo[weaponinfo[player->readyweapon].ammo] -= 2;
|
||
|
|
||
|
A_FireSomething(player,0); // phares
|
||
|
|
||
|
P_BulletSlope(player->mo);
|
||
|
|
||
|
for (i=0; i<20; i++)
|
||
|
{
|
||
|
int damage = 5*(P_Random(pr_shotgun)%3+1);
|
||
|
angle_t angle = player->mo->angle;
|
||
|
// killough 5/5/98: remove dependence on order of evaluation:
|
||
|
int t = P_Random(pr_shotgun);
|
||
|
angle += (t - P_Random(pr_shotgun))<<19;
|
||
|
t = P_Random(pr_shotgun);
|
||
|
P_LineAttack(player->mo, angle, MISSILERANGE, bulletslope +
|
||
|
((t - P_Random(pr_shotgun))<<5), damage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_FireCGun
|
||
|
//
|
||
|
|
||
|
void A_FireCGun(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
if (player->ammo[weaponinfo[player->readyweapon].ammo] || comp[comp_sound])
|
||
|
S_StartSound(player->mo, sfx_pistol);
|
||
|
|
||
|
if (!player->ammo[weaponinfo[player->readyweapon].ammo])
|
||
|
return;
|
||
|
|
||
|
P_SetMobjState(player->mo, S_PLAY_ATK2);
|
||
|
player->ammo[weaponinfo[player->readyweapon].ammo]--;
|
||
|
|
||
|
A_FireSomething(player,psp->state - &states[S_CHAIN1]); // phares
|
||
|
|
||
|
P_BulletSlope(player->mo);
|
||
|
|
||
|
P_GunShot(player->mo, !player->refire);
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i=0 ; i<40 ; i++) // offset angles from its attack angle
|
||
|
{
|
||
|
int j, damage;
|
||
|
angle_t an = mo->angle - ANG90/2 + ANG90/40*i;
|
||
|
|
||
|
// mo->target is the originator (player) of the missile
|
||
|
|
||
|
// killough 8/2/98: make autoaiming prefer enemies
|
||
|
if (!mbf_features ||
|
||
|
(P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, MF_FRIEND),
|
||
|
!linetarget))
|
||
|
P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, 0);
|
||
|
|
||
|
if (!linetarget)
|
||
|
continue;
|
||
|
|
||
|
P_SpawnMobj(linetarget->x, linetarget->y,
|
||
|
linetarget->z + (linetarget->height>>2), MT_EXTRABFG);
|
||
|
|
||
|
for (damage=j=0; j<15; j++)
|
||
|
damage += (P_Random(pr_bfg)&7) + 1;
|
||
|
|
||
|
P_DamageMobj(linetarget, mo->target, mo->target, damage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// A_BFGsound
|
||
|
//
|
||
|
|
||
|
void A_BFGsound(player_t *player, pspdef_t *psp)
|
||
|
{
|
||
|
S_StartSound(player->mo, sfx_bfg);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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)
|
||
|
{
|
||
|
pspdef_t *psp = player->psprites;
|
||
|
int i;
|
||
|
|
||
|
// a null state means not active
|
||
|
// drop tic count and possibly change state
|
||
|
// a -1 tic count never changes
|
||
|
|
||
|
for (i=0; i<NUMPSPRITES; i++, psp++)
|
||
|
if (psp->state && psp->tics != -1 && !--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;
|
||
|
}
|