// P_pspr.c #include "doomdef.h" #include "p_local.h" #define LOWERSPEED FRACUNIT*6 #define RAISESPEED FRACUNIT*6 #define WEAPONBOTTOM 128*FRACUNIT #define WEAPONTOP 32*FRACUNIT #define BFGCELLS 40 // plasma cells for a bfg attack /* ================== = = 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) { psp->state = NULL; break; // object removed itself } 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 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 = ================= */ fixed_t swingx, swingy; void P_CalcSwing (player_t *player) { fixed_t swing; int angle; // OPTIMIZE: tablify this 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]); } /* =============================================================================== PSPRITE ACTIONS =============================================================================== */ weaponinfo_t weaponinfo[NUMWEAPONS] = { { // fist /* ammo */ am_noammo, /* upstate */ S_PUNCHUP, /* downstate */ S_PUNCHDOWN, /* readystate */ S_PUNCH, /* atkstate */ S_PUNCH1, /* flashstate */ S_NULL }, { // pistol /* ammo */ am_clip, /* upstate */ S_PISTOLUP, /* downstate */ S_PISTOLDOWN, /* readystate */ S_PISTOL, /* atkstate */ S_PISTOL1, /* flashstate */ S_PISTOLFLASH }, { // shotgun /* ammo */ am_shell, /* upstate */ S_SGUNUP, /* downstate */ S_SGUNDOWN, /* readystate */ S_SGUN, /* atkstate */ S_SGUN1, /* flashstate */ S_SGUNFLASH1 }, { // chaingun /* ammo */ am_clip, /* upstate */ S_CHAINUP, /* downstate */ S_CHAINDOWN, /* readystate */ S_CHAIN, /* atkstate */ S_CHAIN1, /* flashstate */ S_CHAINFLASH1 }, { // missile /* ammo */ am_misl, /* upstate */ S_MISSILEUP, /* downstate */ S_MISSILEDOWN, /* readystate */ S_MISSILE, /* atkstate */ S_MISSILE1, /* flashstate */ S_MISSILEFLASH1 }, { // plasma /* ammo */ am_cell, /* upstate */ S_PLASMAUP, /* downstate */ S_PLASMADOWN, /* readystate */ S_PLASMA, /* atkstate */ S_PLASMA1, /* flashstate */ S_PLASMAFLASH1 }, { // bfg /* ammo */ am_cell, /* upstate */ S_BFGUP, /* downstate */ S_BFGDOWN, /* readystate */ S_BFG, /* atkstate */ S_BFG1, /* flashstate */ S_BFGFLASH1 }, { // saw /* ammo */ am_noammo, /* upstate */ S_SAWUP, /* downstate */ S_SAWDOWN, /* readystate */ S_SAW, /* atkstate */ S_SAW1, /* flashstate */ S_NULL }, { // super shotgun /* ammo */ am_shell, /* upstate */ S_DSGUNUP, /* downstate */ S_DSGUNDOWN, /* readystate */ S_DSGUN, /* atkstate */ S_DSGUN1, /* flashstate */ S_DSGUNFLASH1 }, }; /* ================ = = 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; if (player->readyweapon == wp_bfg) count = BFGCELLS; else if (player->readyweapon == wp_supershotgun) count = 2; else count = 1; if (ammo == am_noammo || player->ammo[ammo] >= count) return true; // out of ammo, pick a weapon to change to do { if (player->weaponowned[wp_plasma] && player->ammo[am_cell] && !shareware) player->pendingweapon = wp_plasma; else if (player->weaponowned[wp_supershotgun] && player->ammo[am_shell]>2 && 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 && !shareware) player->pendingweapon = wp_bfg; else player->pendingweapon = wp_fist; } while (player->pendingweapon == wp_nochange); 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 = ================= */ 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 = ================= */ void A_Lower (player_t *player, pspdef_t *psp) { psp->sy += LOWERSPEED; if (psp->sy < WEAPONBOTTOM ) return; 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); } /* ================ = = 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 (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) { 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]--; 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); player->ammo[weaponinfo[player->readyweapon].ammo]--; 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); player->ammo[weaponinfo[player->readyweapon].ammo]--; 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); player->ammo[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); player->ammo[weaponinfo[player->readyweapon].ammo]--; 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, j, 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 ; ipsprites[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 ; istate) // 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--; 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; }