summaryrefslogtreecommitdiff
path: root/src/strife/p_pspr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/strife/p_pspr.c')
-rw-r--r--src/strife/p_pspr.c896
1 files changed, 896 insertions, 0 deletions
diff --git a/src/strife/p_pspr.c b/src/strife/p_pspr.c
new file mode 100644
index 00000000..fe32c53f
--- /dev/null
+++ b/src/strife/p_pspr.c
@@ -0,0 +1,896 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 2005 Simon Howard
+//
+// 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 "doomdef.h"
+#include "d_event.h"
+
+#include "deh_misc.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"
+
+#define LOWERSPEED FRACUNIT*6
+#define RAISESPEED FRACUNIT*6
+
+#define WEAPONBOTTOM 128*FRACUNIT
+#define WEAPONTOP 32*FRACUNIT
+
+
+
+//
+// P_SetPsprite
+//
+void
+P_SetPsprite
+( player_t* player,
+ int position,
+ statenum_t stnum )
+{
+ pspdef_t* psp;
+ state_t* state;
+
+ psp = &player->psprites[position];
+
+ do
+ {
+ 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.acp2)
+ {
+ state->action.acp2(player, psp);
+ if (!psp->state)
+ break;
+ }
+
+ stnum = psp->state->nextstate;
+
+ } while (!psp->tics);
+ // an initial state of 0 could cycle through
+}
+
+
+
+//
+// P_CalcSwing
+//
+fixed_t swingx;
+fixed_t swingy;
+
+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*leveltime)&FINEMASK;
+ swingx = FixedMul ( swing, finesine[angle]);
+
+ angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK;
+ swingy = -FixedMul ( 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)
+ S_StartSound (player->mo, sfx_sawup);
+
+ newstate = 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.
+//
+boolean 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 = deh_bfg_cells_per_shot;
+ 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]
+ && (gamemode != shareware) )
+ {
+ player->pendingweapon = wp_plasma;
+ }
+ else if (player->weaponowned[wp_supershotgun]
+ && player->ammo[am_shell]>2
+ && (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
+ && (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,
+ 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 = 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 )
+{
+ statenum_t newstate;
+ int angle;
+
+ // 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 allready be validated)
+ 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
+ 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 )
+{
+ 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 = 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,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 (linetarget)
+ {
+ S_StartSound (player->mo, sfx_punch);
+ player->mo->angle = R_PointToAngle2 (player->mo->x,
+ player->mo->y,
+ linetarget->x,
+ 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 (!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 ((signed int) (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;
+}
+
+// Doom does not check the bounds of the ammo array. As a result,
+// it is possible to use an ammo type > 4 that overflows into the
+// maxammo array and affects that instead. Through dehacked, for
+// example, it is possible to make a weapon that decreases the max
+// number of ammo for another weapon. Emulate this.
+
+static void DecreaseAmmo(player_t *player, int ammonum, int amount)
+{
+ if (ammonum < NUMAMMO)
+ {
+ player->ammo[ammonum] -= amount;
+ }
+ else
+ {
+ player->maxammo[ammonum - NUMAMMO] -= amount;
+ }
+}
+
+
+//
+// A_FireMissile
+//
+void
+A_FireMissile
+( player_t* player,
+ pspdef_t* psp )
+{
+ DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);
+ P_SpawnPlayerMissile (player->mo, MT_ROCKET);
+}
+
+
+//
+// A_FireBFG
+//
+void
+A_FireBFG
+( player_t* player,
+ pspdef_t* psp )
+{
+ DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo,
+ deh_bfg_cells_per_shot);
+ P_SpawnPlayerMissile (player->mo, MT_BFG);
+}
+
+
+
+//
+// A_FirePlasma
+//
+void
+A_FirePlasma
+( player_t* player,
+ pspdef_t* psp )
+{
+ DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);
+
+ P_SetPsprite (player,
+ ps_flash,
+ weaponinfo[player->readyweapon].flashstate+(P_Random ()&1) );
+
+ P_SpawnPlayerMissile (player->mo, MT_PLASMA);
+}
+
+
+
+//
+// P_BulletSlope
+// Sets a slope so a near miss is at aproximately
+// the height of the intended target
+//
+fixed_t bulletslope;
+
+
+void P_BulletSlope (mobj_t* mo)
+{
+ angle_t an;
+
+ // see which target is to be aimed at
+ an = mo->angle;
+ bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
+
+ if (!linetarget)
+ {
+ an += 1<<26;
+ bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
+ if (!linetarget)
+ {
+ an -= 2<<26;
+ bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
+ }
+ }
+}
+
+
+//
+// P_GunShot
+//
+void
+P_GunShot
+( mobj_t* mo,
+ boolean 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, 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);
+ DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);
+
+ P_SetPsprite (player,
+ ps_flash,
+ weaponinfo[player->readyweapon].flashstate);
+
+ 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);
+
+ DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);
+
+ P_SetPsprite (player,
+ ps_flash,
+ weaponinfo[player->readyweapon].flashstate);
+
+ 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;
+ angle_t angle;
+ int damage;
+
+
+ S_StartSound (player->mo, sfx_dshtgn);
+ P_SetMobjState (player->mo, S_PLAY_ATK2);
+
+ DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 2);
+
+ P_SetPsprite (player,
+ ps_flash,
+ 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,
+ bulletslope + ((P_Random()-P_Random())<<5), damage);
+ }
+}
+
+
+//
+// A_FireCGun
+//
+void
+A_FireCGun
+( player_t* player,
+ pspdef_t* psp )
+{
+ S_StartSound (player->mo, sfx_pistol);
+
+ if (!player->ammo[weaponinfo[player->readyweapon].ammo])
+ return;
+
+ P_SetMobjState (player->mo, S_PLAY_ATK2);
+ DecreaseAmmo(player, weaponinfo[player->readyweapon].ammo, 1);
+
+ P_SetPsprite (player,
+ ps_flash,
+ weaponinfo[player->readyweapon].flashstate
+ + psp->state
+ - &states[S_CHAIN1] );
+
+ 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;
+ 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 (!linetarget)
+ continue;
+
+ P_SpawnMobj (linetarget->x,
+ linetarget->y,
+ linetarget->z + (linetarget->height>>2),
+ MT_EXTRABFG);
+
+ damage = 0;
+ for (j=0;j<15;j++)
+ damage += (P_Random()&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)
+{
+ int i;
+ pspdef_t* psp;
+ 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;
+}
+
+