CGDoom/cgdoom/p_pspr.c

810 lines
17 KiB
C

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Weapon sprite animation, weapon objects.
// Action functions for weapons.
//
//-----------------------------------------------------------------------------
//static const char
#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"
#define LOWERSPEED FRACUNIT*6
#define RAISESPEED FRACUNIT*6
#define WEAPONBOTTOM 128*FRACUNIT
#define WEAPONTOP 32*FRACUNIT
// plasma cells for a bfg attack
#define BFGCELLS 40
void A_PSetState(int act, player_t* playr, pspdef_t* pspr );
//
// 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 = (state_t*)&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);
A_PSetState(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
//
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;
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.
//
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 = 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]
&& (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,(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, (statenum_t)S_PLAY_ATK1);
newstate = (statenum_t)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,(statenum_t)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, (statenum_t)S_PLAY);
}
// 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*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 )
{
psp= 0; //Shuts up GCC
// 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 )
{
psp= 0; //Shuts up GCC
P_CheckAmmo (player);
}
//
// 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 )
{
psp= 0; //Shuts up GCC
P_SetMobjState (player->mo, (statenum_t)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;
psp= 0; //Shuts up GCC
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)
{
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;
psp= 0; //Shuts up GCC
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)
return;
// 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)(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;
}
//
// A_FireMissile
//
void A_FireMissile ( player_t* player, pspdef_t* psp )
{
psp= 0; //Shuts up GCC
player->ammo[weaponinfo[player->readyweapon].ammo]--;
P_SpawnPlayerMissile (player->mo, MT_ROCKET);
}
//
// A_FireBFG
//
void A_FireBFG ( player_t* player, pspdef_t* psp )
{
psp= 0; //Shuts up GCC
player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS;
P_SpawnPlayerMissile (player->mo, MT_BFG);
}
//
// A_FirePlasma
//
void A_FirePlasma ( player_t* player, pspdef_t* psp )
{
psp= 0; //Shuts up GCC
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);
}
//
// 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 )
{
psp= 0; //Shuts up GCC
P_SetMobjState (player->mo, (statenum_t)S_PLAY_ATK2);
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, (boolean)!player->refire);
}
//
// A_FireShotgun
//
void A_FireShotgun ( player_t* player, pspdef_t* psp )
{
int i;
psp= 0; //Shuts up GCC
P_SetMobjState (player->mo, (statenum_t)S_PLAY_ATK2);
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);
}
//
// A_FireShotgun2
//
void A_FireShotgun2 ( player_t* player, pspdef_t* psp )
{
int i;
angle_t angle;
int damage;
psp= 0; //Shuts up GCC
P_SetMobjState (player->mo, (statenum_t)S_PLAY_ATK2);
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,
bulletslope + ((P_Random()-P_Random())<<5), damage);
}
}
//
// A_FireCGun
//
void A_FireCGun ( player_t* player, pspdef_t* psp )
{
if (!player->ammo[weaponinfo[player->readyweapon].ammo])
return;
P_SetMobjState (player->mo, (statenum_t)S_PLAY_ATK2);
player->ammo[weaponinfo[player->readyweapon].ammo]--;
P_SetPsprite (player,ps_flash,(statenum_t)(weaponinfo[player->readyweapon].flashstate+ psp->state- &states[S_CHAIN1]) );
P_BulletSlope (player->mo);
P_GunShot (player->mo, (boolean)!player->refire);
}
//
// ?
//
void A_Light0 (player_t *player)
{
player->extralight = 0;
}
void A_Light1 (player_t *player)
{
player->extralight = 1;
}
void A_Light2 (player_t *player)
{
player->extralight = 2;
}
//
// A_BFGsound
//
void A_BFGsound ( player_t* player)
{
//No sound!
player= 0; //Shut up, GCC
}
void A_OpenShotgun2 ( player_t* player, pspdef_t* psp )
{
player= 0; //Shut up, GCC
psp= 0;
}
void A_LoadShotgun2 ( player_t* player, pspdef_t* psp )
{
player= 0; //Shut up, GCC
psp= 0;
}
//void A_ReFire ( player_t* player, pspdef_t* psp )
void A_CloseShotgun2 ( player_t* player, pspdef_t* psp )
{
player= 0; //Shut up, GCC
psp= 0;
//A_ReFire(player,psp);
}
//
// 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) != NULL)
{
// 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, (statenum_t)psp->state->nextstate);
}
}
}
player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
}
void A_PSetState(int act, player_t* playr, pspdef_t* pspr )
{
switch (act)
{
case TA_Light0:A_Light0(playr);break;
case TA_WeaponReady:A_WeaponReady(playr, pspr);break;
case TA_Lower:A_Lower(playr, pspr);break;
case TA_Raise:A_Raise(playr, pspr);break;
case TA_Punch:A_Punch(playr, pspr);break;
case TA_ReFire:A_ReFire(playr, pspr);break;
case TA_FirePistol:A_FirePistol(playr, pspr);break;
case TA_Light1:A_Light1(playr);break;
case TA_FireShotgun:A_FireShotgun(playr, pspr);break;
case TA_Light2:A_Light2(playr);break;
case TA_FireShotgun2:A_FireShotgun2(playr, pspr);break;
case TA_CheckReload:A_CheckReload(playr, pspr);break;
case TA_OpenShotgun2:A_OpenShotgun2(playr, pspr);break;
case TA_LoadShotgun2:A_LoadShotgun2(playr, pspr);break;
case TA_CloseShotgun2:A_CloseShotgun2(playr, pspr);break;
case TA_FireCGun:A_FireCGun(playr, pspr);break;
case TA_GunFlash:A_GunFlash(playr, pspr);break;
case TA_FireMissile:A_FireMissile(playr, pspr);break;
case TA_Saw:A_Saw(playr, pspr);break;
case TA_FirePlasma:A_FirePlasma(playr, pspr);break;
case TA_BFGsound:A_BFGsound(playr);break;
case TA_FireBFG:A_FireBFG(playr, pspr);break;
default:
break;
}
}