1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
20 // Weapon sprite animation, weapon objects.
21 // Action functions for weapons.
23 //-----------------------------------------------------------------------------
26 rcsid[] = "$Id: p_pspr.c,v 1.5 1997/02/03 22:45:12 b1 Exp $";
44 #define LOWERSPEED FRACUNIT*6
45 #define RAISESPEED FRACUNIT*6
47 #define WEAPONBOTTOM 128*FRACUNIT
48 #define WEAPONTOP 32*FRACUNIT
51 // plasma cells for a bfg attack
67 psp = &player->psprites[position];
73 // object removed itself
78 state = &states[stnum];
80 psp->tics = state->tics; // could be 0
85 psp->sx = state->misc1 << FRACBITS;
86 psp->sy = state->misc2 << FRACBITS;
89 // Call action routine.
93 state->action(player, psp);
98 stnum = psp->state->nextstate;
100 } while (!psp->tics);
101 // an initial state of 0 could cycle through
112 void P_CalcSwing (player_t* player)
117 // OPTIMIZE: tablify this.
118 // A LUT would allow for different modes,
119 // and add flexibility.
123 angle = (FINEANGLES/70*leveltime)&FINEMASK;
124 swingx = FixedMul ( swing, finesine[angle]);
126 angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK;
127 swingy = -FixedMul ( swingx, finesine[angle]);
134 // Starts bringing the pending weapon up
135 // from the bottom of the screen.
138 void P_BringUpWeapon (player_t* player)
142 if (player->pendingweapon == wp_nochange)
143 player->pendingweapon = player->readyweapon;
145 if (player->pendingweapon == wp_chainsaw)
146 S_StartSound (player->mo, sfx_sawup);
148 newstate = weaponinfo[player->pendingweapon].upstate;
150 player->pendingweapon = wp_nochange;
151 player->psprites[ps_weapon].sy = WEAPONBOTTOM;
153 P_SetPsprite (player, ps_weapon, newstate);
158 // Returns true if there is enough ammo to shoot.
159 // If not, selects the next weapon to use.
161 boolean P_CheckAmmo (player_t* player)
166 ammo = weaponinfo[player->readyweapon].ammo;
168 // Minimal amount for one shot varies.
169 if (player->readyweapon == wp_bfg)
171 else if (player->readyweapon == wp_supershotgun)
172 count = 2; // Double barrel.
174 count = 1; // Regular.
176 // Some do not need ammunition anyway.
177 // Return if current ammunition sufficient.
178 if (ammo == am_noammo || player->ammo[ammo] >= count)
181 // Out of ammo, pick a weapon to change to.
182 // Preferences are set here.
185 if (player->weaponowned[wp_plasma]
186 && player->ammo[am_cell]
187 && (gamemode != shareware) )
189 player->pendingweapon = wp_plasma;
191 else if (player->weaponowned[wp_supershotgun]
192 && player->ammo[am_shell]>2
193 && (gamemode == commercial) )
195 player->pendingweapon = wp_supershotgun;
197 else if (player->weaponowned[wp_chaingun]
198 && player->ammo[am_clip])
200 player->pendingweapon = wp_chaingun;
202 else if (player->weaponowned[wp_shotgun]
203 && player->ammo[am_shell])
205 player->pendingweapon = wp_shotgun;
207 else if (player->ammo[am_clip])
209 player->pendingweapon = wp_pistol;
211 else if (player->weaponowned[wp_chainsaw])
213 player->pendingweapon = wp_chainsaw;
215 else if (player->weaponowned[wp_missile]
216 && player->ammo[am_misl])
218 player->pendingweapon = wp_missile;
220 else if (player->weaponowned[wp_bfg]
221 && player->ammo[am_cell]>40
222 && (gamemode != shareware) )
224 player->pendingweapon = wp_bfg;
228 // If everything fails.
229 player->pendingweapon = wp_fist;
232 } while (player->pendingweapon == wp_nochange);
234 // Now set appropriate weapon overlay.
235 P_SetPsprite (player,
237 weaponinfo[player->readyweapon].downstate);
246 void P_FireWeapon (player_t* player)
250 if (!P_CheckAmmo (player))
253 P_SetMobjState (player->mo, S_PLAY_ATK1);
254 newstate = weaponinfo[player->readyweapon].atkstate;
255 P_SetPsprite (player, ps_weapon, newstate);
256 P_NoiseAlert (player->mo, player->mo);
263 // Player died, so put the weapon away.
265 void P_DropWeapon (player_t* player)
267 P_SetPsprite (player,
269 weaponinfo[player->readyweapon].downstate);
276 // The player can fire the weapon
277 // or change to another weapon at this time.
278 // Follows after getting weapon up,
279 // or after previous attack/fire sequence.
282 A_WeaponReady(void *_player, void *_psp)
284 player_t *player = (player_t*)_player;
285 pspdef_t *psp = (pspdef_t*)_psp;
289 // get out of attack state
290 if (player->mo->state == &states[S_PLAY_ATK1]
291 || player->mo->state == &states[S_PLAY_ATK2] )
293 P_SetMobjState (player->mo, S_PLAY);
296 if (player->readyweapon == wp_chainsaw
297 && psp->state == &states[S_SAW])
299 S_StartSound (player->mo, sfx_sawidl);
303 // if player is dead, put the weapon away
304 if (player->pendingweapon != wp_nochange || !player->health)
307 // (pending weapon should allready be validated)
308 newstate = weaponinfo[player->readyweapon].downstate;
309 P_SetPsprite (player, ps_weapon, newstate);
314 // the missile launcher and bfg do not auto fire
315 if (player->cmd.buttons & BT_ATTACK)
317 if ( !player->attackdown
318 || (player->readyweapon != wp_missile
319 && player->readyweapon != wp_bfg) )
321 player->attackdown = true;
322 P_FireWeapon (player);
327 player->attackdown = false;
329 // bob the weapon based on movement speed
330 angle = (128*leveltime)&FINEMASK;
331 psp->sx = FRACUNIT + FixedMul (player->bob, finecosine[angle]);
332 angle &= FINEANGLES/2-1;
333 psp->sy = WEAPONTOP + FixedMul (player->bob, finesine[angle]);
340 // The player can re-fire the weapon
341 // without lowering it entirely.
343 void A_ReFire(void *_player, void* /*psp*/)
345 player_t *player = (player_t*)_player;
348 // (if a weaponchange is pending, let it go through instead)
349 if ( (player->cmd.buttons & BT_ATTACK)
350 && player->pendingweapon == wp_nochange
354 P_FireWeapon (player);
359 P_CheckAmmo (player);
365 A_CheckReload(void *_player, void * /*psp*/)
367 P_CheckAmmo ((player_t*)_player);
374 // Lowers current weapon,
375 // and changes weapon at bottom.
378 A_Lower(void *_player, void *_psp)
380 player_t *player = (player_t*)_player;
381 pspdef_t *psp = (pspdef_t*)_psp;
383 psp->sy += LOWERSPEED;
386 if (psp->sy < WEAPONBOTTOM )
390 if (player->playerstate == PST_DEAD)
392 psp->sy = WEAPONBOTTOM;
394 // don't bring weapon back up
398 // The old weapon has been lowered off the screen,
399 // so change the weapon and start raising it
402 // Player is dead, so keep the weapon off screen.
403 P_SetPsprite (player, ps_weapon, S_NULL);
407 player->readyweapon = player->pendingweapon;
409 P_BringUpWeapon (player);
417 A_Raise(void *_player, void *_psp)
419 player_t *player = (player_t*)_player;
420 pspdef_t *psp = (pspdef_t*)_psp;
423 psp->sy -= RAISESPEED;
425 if (psp->sy > WEAPONTOP )
430 // The weapon has been raised all the way,
431 // so change to the ready state.
432 newstate = weaponinfo[player->readyweapon].readystate;
434 P_SetPsprite (player, ps_weapon, newstate);
443 A_GunFlash(void *_player, void * /*psp*/)
445 player_t *player = (player_t*)_player;
446 P_SetMobjState (player->mo, S_PLAY_ATK2);
447 P_SetPsprite (player,ps_flash,weaponinfo[player->readyweapon].flashstate);
461 A_Punch(void *_player, void* /*psp*/)
463 player_t *player = (player_t*)_player;
468 damage = (P_Random ()%10+1)<<1;
470 if (player->powers[pw_strength])
473 angle = player->mo->angle;
474 angle += (P_Random()-P_Random())<<18;
475 slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
476 P_LineAttack (player->mo, angle, MELEERANGE, slope, damage);
478 // turn to face target
481 S_StartSound (player->mo, sfx_punch);
482 player->mo->angle = R_PointToAngle2 (player->mo->x,
494 A_Saw(void *_player, void * /*psp*/)
496 player_t *player = (player_t*)_player;
501 damage = 2*(P_Random ()%10+1);
502 angle = player->mo->angle;
503 angle += (P_Random()-P_Random())<<18;
505 // use meleerange + 1 se the puff doesn't skip the flash
506 slope = P_AimLineAttack (player->mo, angle, MELEERANGE+1);
507 P_LineAttack (player->mo, angle, MELEERANGE+1, slope, damage);
511 S_StartSound (player->mo, sfx_sawful);
514 S_StartSound (player->mo, sfx_sawhit);
516 // turn to face target
517 angle = R_PointToAngle2 (player->mo->x, player->mo->y,
518 linetarget->x, linetarget->y);
519 if (angle - player->mo->angle > ANG180)
521 if (angle - player->mo->angle < (unsigned int)(-ANG90/20))
522 player->mo->angle = angle + ANG90/21;
524 player->mo->angle -= ANG90/20;
528 if (angle - player->mo->angle > ANG90/20)
529 player->mo->angle = angle - ANG90/21;
531 player->mo->angle += ANG90/20;
533 player->mo->flags |= MF_JUSTATTACKED;
542 A_FireMissile(void *_player, void* /*psp*/)
544 player_t *player = (player_t*)_player;
545 player->ammo[weaponinfo[player->readyweapon].ammo]--;
546 P_SpawnPlayerMissile (player->mo, MT_ROCKET);
554 A_FireBFG(void *_player, void * /*psp*/)
556 player_t *player = (player_t*)_player;
557 player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS;
558 P_SpawnPlayerMissile (player->mo, MT_BFG);
567 A_FirePlasma(void *_player, void * /*psp*/)
569 player_t *player = (player_t*)_player;
570 player->ammo[weaponinfo[player->readyweapon].ammo]--;
572 P_SetPsprite (player,
574 weaponinfo[player->readyweapon].flashstate+(P_Random ()&1) );
576 P_SpawnPlayerMissile (player->mo, MT_PLASMA);
583 // Sets a slope so a near miss is at aproximately
584 // the height of the intended target
589 void P_BulletSlope (mobj_t *mo)
593 // see which target is to be aimed at
595 bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
600 bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
604 bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
621 damage = 5*(P_Random ()%3+1);
625 angle += (P_Random()-P_Random())<<18;
627 P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage);
635 A_FirePistol(void *_player, void* /*psp*/)
637 player_t *player = (player_t*)_player;
639 S_StartSound (player->mo, sfx_pistol);
641 P_SetMobjState (player->mo, S_PLAY_ATK2);
642 player->ammo[weaponinfo[player->readyweapon].ammo]--;
644 P_SetPsprite (player,
646 weaponinfo[player->readyweapon].flashstate);
648 P_BulletSlope (player->mo);
649 P_GunShot (player->mo, !player->refire);
657 A_FireShotgun(void *_player, void* /*psp*/)
659 player_t *player = (player_t*)_player;
662 S_StartSound (player->mo, sfx_shotgn);
663 P_SetMobjState (player->mo, S_PLAY_ATK2);
665 player->ammo[weaponinfo[player->readyweapon].ammo]--;
667 P_SetPsprite (player,
669 weaponinfo[player->readyweapon].flashstate);
671 P_BulletSlope (player->mo);
673 for (i=0 ; i<7 ; i++)
674 P_GunShot (player->mo, false);
683 A_FireShotgun2(void *_player, void* /*psp*/)
685 player_t *player = (player_t*)_player;
691 S_StartSound (player->mo, sfx_dshtgn);
692 P_SetMobjState (player->mo, S_PLAY_ATK2);
694 player->ammo[weaponinfo[player->readyweapon].ammo]-=2;
696 P_SetPsprite (player,
698 weaponinfo[player->readyweapon].flashstate);
700 P_BulletSlope (player->mo);
702 for (i=0 ; i<20 ; i++)
704 damage = 5*(P_Random ()%3+1);
705 angle = player->mo->angle;
706 angle += (P_Random()-P_Random())<<19;
707 P_LineAttack (player->mo,
710 bulletslope + ((P_Random()-P_Random())<<5), damage);
719 A_FireCGun(void *_player, void *_psp)
721 player_t *player = (player_t*)_player;
722 pspdef_t *psp = (pspdef_t*)_psp;
724 S_StartSound (player->mo, sfx_pistol);
726 if (!player->ammo[weaponinfo[player->readyweapon].ammo])
729 P_SetMobjState (player->mo, S_PLAY_ATK2);
730 player->ammo[weaponinfo[player->readyweapon].ammo]--;
732 P_SetPsprite (player,
734 weaponinfo[player->readyweapon].flashstate
736 - &states[S_CHAIN1] );
738 P_BulletSlope (player->mo);
740 P_GunShot (player->mo, !player->refire);
748 void A_Light0 (void *_player, void* /*psp*/)
750 ((player_t*)_player)->extralight = 0;
753 void A_Light1 (void *_player, void* /*psp*/)
755 ((player_t*)_player)->extralight = 1;
758 void A_Light2 (void *_player, void* /*psp*/)
760 ((player_t*)_player)->extralight = 2;
766 // Spawn a BFG explosion on every monster in view
768 void A_BFGSpray (void *_mo, void*)
770 mobj_t *mo = (mobj_t*)_mo;
776 // offset angles from its attack angle
777 for (i=0 ; i<40 ; i++)
779 an = mo->angle - ANG90/2 + ANG90/40*i;
781 // mo->target is the originator (player)
783 P_AimLineAttack (mo->target, an, 16*64*FRACUNIT);
788 P_SpawnMobj (linetarget->x,
790 linetarget->z + (linetarget->height>>2),
795 damage += (P_Random()&7) + 1;
797 P_DamageMobj (linetarget, mo->target,mo->target, damage);
806 A_BFGsound(void *_player, void * /*psp*/)
808 S_StartSound (((player_t*)_player)->mo, sfx_bfg);
815 // Called at start of level for each player.
817 void P_SetupPsprites (player_t* player)
821 // remove all psprites
822 for (i=0 ; i<NUMPSPRITES ; i++)
823 player->psprites[i].state = NULL;
826 player->pendingweapon = player->readyweapon;
827 P_BringUpWeapon (player);
835 // Called every tic by player thinking routine.
837 void P_MovePsprites (player_t* player)
842 psp = &player->psprites[0];
843 for (i=0 ; i<NUMPSPRITES ; i++, psp++)
845 // a null state means not active
848 // drop tic count and possibly change state
850 // a -1 tic count never changes
855 P_SetPsprite (player, i, psp->state->nextstate);
860 player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
861 player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;