1 /**\file
2  *\section License
3  * License: GPL
4  * Online License Link: http://www.gnu.org/licenses/gpl.html
5  *
6  *\author Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
7  *\author Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
8  *\author Copyright © 2006 Jamie Jones <yagisan@dengine.net>
9  *\author Copyright © 2003-2005 Samuel Villarreal <svkaiser@gmail.com>
10  *\author Copyright © 1993-1996 by id Software, Inc.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin St, Fifth Floor,
25  * Boston, MA  02110-1301  USA
26  */
27 
28 /**
29  * p_pspr.c: Weapon sprite animation, weapon objects.
30  *
31  * Action functions for weapons.
32  */
33 
34 #ifdef MSVC
35 // Sumtin' 'ere messes with poor ol' MSVC's head...
36 #  pragma optimize("g",off)
37 #endif
38 
39 // HEADER FILES ------------------------------------------------------------
40 
41 #include <math.h>
42 #include <stdio.h>
43 #include <string.h>
44 
45 #include "common.h"
46 
47 #include "d_net.h"
48 #include "player.h"
49 #include "p_map.h"
50 #include "p_tick.h"
51 #include "p_inventory.h"
52 
53 // MACROS ------------------------------------------------------------------
54 
55 #define LOWERSPEED      6
56 #define RAISESPEED      6
57 #define WEAPONTOP       32
58 
59 // TYPES -------------------------------------------------------------------
60 
61 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
62 
63 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
64 
65 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
66 
67 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
68 
69 // PUBLIC DATA DEFINITIONS -------------------------------------------------
70 
71 // PRIVATE DATA DEFINITIONS ------------------------------------------------
72 
73 //static float swing[2];
74 static float bulletSlope;
75 
76 // CODE --------------------------------------------------------------------
77 
R_GetWeaponBob(int player,float * x,float * y)78 void R_GetWeaponBob(int player, float* x, float* y)
79 {
80     if(x)
81     {
82         *x = 1 + (cfg.common.bobWeapon * players[player].bob) *
83             FIX2FLT(finecosine[(128 * mapTime) & FINEMASK]);
84     }
85 
86     if(y)
87     {
88         *y = 32 + (cfg.common.bobWeapon * players[player].bob) *
89             FIX2FLT(finesine[(128 * mapTime) & FINEMASK & (FINEANGLES / 2 - 1)]);
90     }
91 }
92 
P_SetPsprite(player_t * player,int position,statenum_t stnum)93 void P_SetPsprite(player_t *player, int position, statenum_t stnum)
94 {
95     pspdef_t           *psp;
96     state_t            *state;
97 
98     psp = &player->pSprites[position];
99 
100     do
101     {
102         if(!stnum)
103         {
104             // Object removed itself.
105             psp->state = NULL;
106             break;
107         }
108 
109         state = &STATES[stnum];
110         psp->state = state;
111         psp->tics = state->tics; // Could be 0.
112 
113         Player_NotifyPSpriteChange(player, position);
114 
115         if(state->misc[0])
116         {
117             // Coordinate set.
118             psp->pos[VX] = (float) state->misc[0];
119             psp->pos[VY] = (float) state->misc[1];
120         }
121 
122         // Call action routine.
123         // Modified handling.
124         if(state->action)
125         {
126             // Custom parameters in the action function are passed to libdoomsday this way.
127             P_SetCurrentActionState((int) stnum);
128 
129             state->action(player, psp);
130             if(!psp->state)
131                 break;
132         }
133 
134         stnum = psp->state->nextState;
135 
136     } while(!psp->tics);
137     // An initial state of 0 could cycle through.
138 }
139 
140 /**
141  * Starts bringing the pending weapon up from the bottom of the screen.
142  */
P_BringUpWeapon(player_t * player)143 void P_BringUpWeapon(player_t* player)
144 {
145     weapontype_t const oldPendingWeapon = player->pendingWeapon;
146 
147     weaponmodeinfo_t* wminfo = NULL;
148     weapontype_t raiseWeapon;
149 
150     if(!player) return;
151 
152     if(player->plr->flags & DDPF_UNDEFINED_WEAPON)
153     {
154         // We'll do this when the server informs us about the client's current weapon.
155         return;
156     }
157 
158     raiseWeapon = player->pendingWeapon;
159     if(raiseWeapon == WT_NOCHANGE)
160         raiseWeapon = player->readyWeapon;
161 
162     player->pendingWeapon = WT_NOCHANGE;
163     player->pSprites[ps_weapon].pos[VY] = WEAPONBOTTOM;
164 
165     if(!VALID_WEAPONTYPE(raiseWeapon))
166     {
167         return;
168     }
169 
170     wminfo = WEAPON_INFO(raiseWeapon, player->class_, 0);
171 
172     App_Log(DE2_MAP_XVERBOSE, "P_BringUpWeapon: Player %i, pending weapon was %i, weapon pspr to %i",
173             (int)(player - players), oldPendingWeapon, wminfo->states[WSN_UP]);
174 
175     if(wminfo->raiseSound)
176         S_StartSoundEx(wminfo->raiseSound, player->plr->mo);
177 
178     P_SetPsprite(player, ps_weapon, wminfo->states[WSN_UP]);
179 }
180 
P_FireWeapon(player_t * player)181 void P_FireWeapon(player_t *player)
182 {
183     statenum_t          newstate;
184 
185     if(!P_CheckAmmo(player))
186         return;
187 
188     // Psprite state.
189     player->plr->pSprites[0].state = DDPSP_FIRE;
190 
191     P_MobjChangeState(player->plr->mo, PCLASS_INFO(player->class_)->attackState);
192     newstate = weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_ATTACK];
193     P_SetPsprite(player, ps_weapon, newstate);
194     P_NoiseAlert(player->plr->mo, player->plr->mo);
195 }
196 
197 /**
198  * Player died, so put the weapon away.
199  */
P_DropWeapon(player_t * player)200 void P_DropWeapon(player_t *player)
201 {
202     P_SetPsprite(player, ps_weapon, weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_DOWN]);
203 }
204 
205 /**
206  * The player can fire the weapon or change to another weapon at this time.
207  * Follows after getting weapon up, or after previous attack/fire sequence.
208  */
A_WeaponReady(player_t * player,pspdef_t * psp)209 void C_DECL A_WeaponReady(player_t* player, pspdef_t* psp)
210 {
211     weaponmodeinfo_t*   wminfo;
212 
213     // Enable the pspr Y offset (might be disabled in A_Lower).
214     DD_SetInteger(DD_WEAPON_OFFSET_SCALE_Y, 1000);
215 
216     // Get out of attack state.
217     if(player->plr->mo->state == &STATES[PCLASS_INFO(player->class_)->attackState] ||
218        player->plr->mo->state == &STATES[PCLASS_INFO(player->class_)->attackEndState])
219     {
220         P_MobjChangeState(player->plr->mo, PCLASS_INFO(player->class_)->normalState);
221     }
222 
223     if(player->readyWeapon != WT_NOCHANGE)
224     {
225         wminfo = WEAPON_INFO(player->readyWeapon, player->class_, 0);
226 
227         // A weaponready sound?
228         if(psp->state == &STATES[wminfo->states[WSN_READY]] && wminfo->readySound)
229             S_StartSound(wminfo->readySound, player->plr->mo);
230 
231         // Check for change. If player is dead, put the weapon away.
232         if(player->pendingWeapon != WT_NOCHANGE || !player->health)
233         {   //  (pending weapon should allready be validated)
234             P_SetPsprite(player, ps_weapon, wminfo->states[WSN_DOWN]);
235             return;
236         }
237     }
238 
239     // Check for autofire.
240     if(player->brain.attack)
241     {
242         wminfo = WEAPON_INFO(player->readyWeapon, player->class_, 0);
243 
244         if(!player->attackDown || wminfo->autoFire)
245         {
246             player->attackDown = true;
247             P_FireWeapon(player);
248             return;
249         }
250     }
251     else
252         player->attackDown = false;
253 
254     // Bob the weapon based on movement speed.
255     R_GetWeaponBob(player - players, &psp->pos[0], &psp->pos[1]);
256 
257     // Psprite state.
258     player->plr->pSprites[0].state = DDPSP_BOBBING;
259 }
260 
261 /**
262  * The player can re-fire the weapon without lowering it entirely.
263  */
A_ReFire(player_t * player,pspdef_t * psp)264 void C_DECL A_ReFire(player_t *player, pspdef_t *psp)
265 {
266     // Check for fire (if a weaponchange is pending, let it go through
267     // instead).
268     if((player->brain.attack) &&
269        player->pendingWeapon == WT_NOCHANGE && player->health)
270     {
271         player->refire++;
272         P_FireWeapon(player);
273     }
274     else
275     {
276         player->refire = 0;
277         P_CheckAmmo(player);
278     }
279 }
280 
A_CheckReload(player_t * player,pspdef_t * psp)281 void C_DECL A_CheckReload(player_t *player, pspdef_t *psp)
282 {
283     P_CheckAmmo(player);
284 }
285 
286 /**
287  * Lowers current weapon, and changes weapon at bottom.
288  */
A_Lower(player_t * player,pspdef_t * psp)289 void C_DECL A_Lower(player_t *player, pspdef_t *psp)
290 {
291     psp->pos[VY] += LOWERSPEED;
292 
293     // Psprite state.
294     player->plr->pSprites[0].state = DDPSP_DOWN;
295 
296     // Should we disable the lowering?
297     if(!cfg.bobWeaponLower ||
298        weaponInfo[player->readyWeapon][player->class_].mode[0].staticSwitch)
299     {
300         DD_SetInteger(DD_WEAPON_OFFSET_SCALE_Y, 0);
301     }
302 
303     // Is already down.
304     if(psp->pos[VY] < WEAPONBOTTOM)
305         return;
306 
307     // Player is dead.
308     if(player->playerState == PST_DEAD)
309     {
310         psp->pos[VY] = WEAPONBOTTOM;
311 
312         // Don't bring weapon back up.
313         return;
314     }
315 
316     if(player->readyWeapon == WT_SIXTH) // jd64
317         P_SetPsprite(player, ps_flash, S_NULL);
318 
319     // The old weapon has been lowered off the screen, so change the weapon
320     // and start raising it.
321     if(!player->health)
322     {
323         // Player is dead, so keep the weapon off screen.
324         P_SetPsprite(player, ps_weapon, S_NULL);
325         return;
326     }
327 
328     player->readyWeapon = player->pendingWeapon;
329     player->update |= PSF_READY_WEAPON;
330 
331     // Should we suddenly lower the weapon?
332     if(cfg.bobWeaponLower && !weaponInfo[player->readyWeapon][player->class_].mode[0].staticSwitch)
333     {
334         DD_SetInteger(DD_WEAPON_OFFSET_SCALE_Y, 1000);
335     }
336 
337     P_BringUpWeapon(player);
338 }
339 
A_Raise(player_t * player,pspdef_t * psp)340 void C_DECL A_Raise(player_t *player, pspdef_t *psp)
341 {
342     statenum_t          newstate;
343 
344     // Psprite state.
345     player->plr->pSprites[0].state = DDPSP_UP;
346 
347     // Should we disable the lowering?
348     if(!cfg.bobWeaponLower || weaponInfo[player->readyWeapon][player->class_].mode[0].staticSwitch)
349     {
350         DD_SetInteger(DD_WEAPON_OFFSET_SCALE_Y, 0);
351     }
352 
353     P_SetPsprite(player, ps_flash, S_NULL);
354     psp->pos[VY] -= RAISESPEED;
355 
356     if(psp->pos[VY] > WEAPONTOP)
357         return;
358 
359     // Enable the pspr Y offset once again.
360     DD_SetInteger(DD_WEAPON_OFFSET_SCALE_Y, 1000);
361 
362     psp->pos[VY] = WEAPONTOP;
363 
364     // The weapon has been raised all the way, so change to the ready state.
365     newstate = weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_READY];
366 
367     P_SetPsprite(player, ps_weapon, newstate);
368 }
369 
A_PlasmaShock(player_t * pl,pspdef_t * psp)370 void C_DECL A_PlasmaShock(player_t* pl, pspdef_t* psp)
371 {
372     S_StartSound(SFX_PSIDL, pl->plr->mo);
373     P_SetPsprite(pl, ps_flash, S_PLASMASHOCK1);
374 }
375 
A_GunFlash(player_t * player,pspdef_t * psp)376 void C_DECL A_GunFlash(player_t *player, pspdef_t *psp)
377 {
378     P_MobjChangeState(player->plr->mo, PCLASS_INFO(player->class_)->attackEndState);
379     P_SetPsprite(player, ps_flash, weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_FLASH]);
380 }
381 
A_Punch(player_t * player,pspdef_t * psp)382 void C_DECL A_Punch(player_t *player, pspdef_t *psp)
383 {
384     angle_t angle;
385     int damage;
386     float slope;
387 
388     P_ShotAmmo(player);
389     player->update |= PSF_AMMO;
390 
391     if(IS_CLIENT) return;
392 
393     damage = (P_Random() % 10 + 1) * 2;
394     if(player->powers[PT_STRENGTH])
395         damage *= 10;
396 
397     angle = player->plr->mo->angle;
398     angle += (P_Random() - P_Random()) << 18;
399     slope = P_AimLineAttack(player->plr->mo, angle, PLRMELEERANGE);
400     P_LineAttack(player->plr->mo, angle, PLRMELEERANGE, slope, damage, MT_PUFF);
401 
402     // Turn to face target.
403     if(lineTarget)
404     {
405         S_StartSound(SFX_PUNCH, player->plr->mo);
406 
407         player->plr->mo->angle = M_PointToAngle2(player->plr->mo->origin, lineTarget->origin);
408         player->plr->flags |= DDPF_FIXANGLES;
409     }
410 }
411 
A_Saw(player_t * player,pspdef_t * psp)412 void C_DECL A_Saw(player_t *player, pspdef_t *psp)
413 {
414     angle_t angle;
415     int damage;
416     float slope;
417 
418     DENG_UNUSED(psp);
419 
420     P_ShotAmmo(player);
421     player->update |= PSF_AMMO;
422 
423     if(IS_CLIENT) return;
424 
425     damage = (float) (P_Random() % 10 + 1) * 2;
426     angle = player->plr->mo->angle;
427     angle += (P_Random() - P_Random()) << 18;
428 
429     // Use meleerange + 1 so the puff doesn't skip the flash.
430     slope = P_AimLineAttack(player->plr->mo, angle, PLRMELEERANGE + 1);
431     P_LineAttack(player->plr->mo, angle, PLRMELEERANGE + 1, slope, damage, MT_PUFF);
432 
433     if(!lineTarget)
434     {
435         S_StartSound(SFX_SAWFUL, player->plr->mo);
436         return;
437     }
438 
439     S_StartSound(SFX_SAWHIT, player->plr->mo);
440 
441     // Turn to face target.
442     angle = M_PointToAngle2(player->plr->mo->origin, lineTarget->origin);
443     if(angle - player->plr->mo->angle > ANG180)
444     {
445         if(angle - player->plr->mo->angle < -ANG90 / 32) // jd64 was "/ 20"
446             player->plr->mo->angle = angle + ANG90 / 32; // jd64 was "/ 21"
447         else
448             player->plr->mo->angle -= ANG90 / 20;
449     }
450     else
451     {
452         if(angle - player->plr->mo->angle > ANG90 / 32) // jd64 was "/ 20"
453             player->plr->mo->angle = angle - ANG90 / 32; // jd64 was "/ 21"
454         else
455             player->plr->mo->angle += ANG90 / 20;
456     }
457     player->plr->mo->flags |= MF_JUSTATTACKED;
458 }
459 
A_FireMissile(player_t * player,pspdef_t * psp)460 void C_DECL A_FireMissile(player_t *player, pspdef_t *psp)
461 {
462     P_ShotAmmo(player);
463     player->update |= PSF_AMMO;
464 
465     if(IS_CLIENT)
466         return;
467 
468     P_SpawnMissile(MT_ROCKET, player->plr->mo, NULL);
469 
470     // jd64 >
471     if(cfg.weaponRecoil)
472     {
473         angle_t         angle = player->plr->mo->angle + ANG180;
474         uint            an = angle >> ANGLETOFINESHIFT;
475 
476         player->plr->mo->mom[MX] += 4 * FIX2FLT(finecosine[an]);
477         player->plr->mo->mom[MY] += 4 * FIX2FLT(finesine[an]);
478     }
479     // < d64tc
480 }
481 
A_FireBFG(player_t * player,pspdef_t * psp)482 void C_DECL A_FireBFG(player_t* player, pspdef_t* psp)
483 {
484     P_ShotAmmo(player);
485     player->update |= PSF_AMMO;
486 
487     if(IS_CLIENT)
488         return;
489 
490     P_SpawnMissile(MT_BFG, player->plr->mo, NULL);
491 }
492 
A_FirePlasma(player_t * player,pspdef_t * psp)493 void C_DECL A_FirePlasma(player_t* player, pspdef_t* psp)
494 {
495     P_ShotAmmo(player);
496 
497     P_SetPsprite(player, ps_flash,
498                  weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_FLASH] +
499                  (P_Random() & 1));
500 
501     //P_SetPsprite(player, ps_flash, S_NULL); // jd64 wha?
502 
503     player->update |= PSF_AMMO;
504     if(IS_CLIENT)
505         return;
506 
507     P_SpawnMissile(MT_PLASMA, player->plr->mo, NULL);
508 }
509 
510 /**
511  * d64tc
512  */
A_FireSingleLaser(player_t * player,pspdef_t * psp)513 void C_DECL A_FireSingleLaser(player_t *player, pspdef_t *psp)
514 {
515     int                 plrNum = player - players;
516     mobj_t*             pmo;
517     short               laserPower;
518 
519     P_ShotAmmo(player);
520     P_SetPsprite(player, ps_flash,
521                  weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_FLASH]);
522     player->update |= PSF_AMMO;
523     if(IS_CLIENT)
524         return;
525 
526     pmo = player->plr->mo;
527 
528     laserPower = 0;
529     if(P_InventoryCount(plrNum, IIT_DEMONKEY1))
530         laserPower++;
531     if(P_InventoryCount(plrNum, IIT_DEMONKEY2))
532         laserPower++;
533     if(P_InventoryCount(plrNum, IIT_DEMONKEY3))
534         laserPower++;
535 
536     switch(laserPower)
537     {
538     case 0:
539         P_SpawnMissile(MT_LASERSHOTWEAK, player->plr->mo, NULL);
540         break;
541 
542     case 1:
543         P_SpawnMissile(MT_LASERSHOT, player->plr->mo, NULL);
544         break;
545 
546     case 2:
547         P_SPMAngle(MT_LASERSHOT, pmo, pmo->angle - (ANG45 / 8));
548         P_SPMAngle(MT_LASERSHOT, pmo, pmo->angle + (ANG45 / 8));
549         break;
550 
551     case 3:
552         P_SpawnMissile(MT_LASERSHOT, pmo, NULL);
553         P_SPMAngle(MT_LASERSHOT, pmo, pmo->angle - (ANG45 / 6));
554         P_SPMAngle(MT_LASERSHOT, pmo, pmo->angle + (ANG45 / 6));
555         break;
556     }
557 }
558 
559 /**
560  * d64tc
561  */
fireDoubleLaser(player_t * player,pspdef_t * psp,angle_t angleDelta)562 static void fireDoubleLaser(player_t* player, pspdef_t* psp,
563                             angle_t angleDelta)
564 {
565     mobj_t*             pmo;
566 
567     P_ShotAmmo(player);
568 
569     pmo = player->plr->mo;
570     player->update |= PSF_AMMO;
571 
572     if(IS_CLIENT)
573         return;
574 
575     P_SpawnMissile(MT_LASERSHOT, pmo, NULL);
576     P_SPMAngle(MT_LASERSHOT, pmo, pmo->angle - angleDelta);
577     P_SPMAngle(MT_LASERSHOT, pmo, pmo->angle + angleDelta);
578 }
579 
580 /**
581  * d64tc
582  */
A_FireDoubleLaser(player_t * player,pspdef_t * psp)583 void C_DECL A_FireDoubleLaser(player_t *player, pspdef_t *psp)
584 {
585     fireDoubleLaser(player, psp, ANG45 / 8);
586 }
587 
588 /**
589  * d64tc
590  */
A_FireDoubleLaser1(player_t * player,pspdef_t * psp)591 void C_DECL A_FireDoubleLaser1(player_t *player, pspdef_t *psp)
592 {
593     fireDoubleLaser(player, psp, ANG45 / 4);
594 }
595 
596 /**
597  * d64tc
598  */
A_FireDoubleLaser2(player_t * player,pspdef_t * psp)599 void C_DECL A_FireDoubleLaser2(player_t *player, pspdef_t *psp)
600 {
601     fireDoubleLaser(player, psp, ANG45 / 3);
602 }
603 
604 /**
605  * Sets a slope so a near miss is at aproximately the height of the
606  * intended target.
607  */
P_BulletSlope(mobj_t * mo)608 void P_BulletSlope(mobj_t *mo)
609 {
610     angle_t             angle;
611 
612     // See which target is to be aimed at.
613     angle = mo->angle;
614     bulletSlope = P_AimLineAttack(mo, angle, 16 * 64);
615     if(!cfg.common.noAutoAim)
616     {
617         if(!lineTarget)
618         {
619             angle += 1 << 26;
620             bulletSlope = P_AimLineAttack(mo, angle, 16 * 64);
621 
622             if(!lineTarget)
623             {
624                 angle -= 2 << 26;
625                 bulletSlope = P_AimLineAttack(mo, angle, 16 * 64);
626             }
627 
628             if(!lineTarget)
629             {
630                 angle += 2 << 26;
631                 bulletSlope =
632                     tan(LOOKDIR2RAD(mo->dPlayer->lookDir)) / 1.2;
633             }
634         }
635     }
636 }
637 
P_GunShot(mobj_t * mo,dd_bool accurate)638 void P_GunShot(mobj_t *mo, dd_bool accurate)
639 {
640     angle_t angle;
641     int damage;
642 
643     damage = 5 * (P_Random() % 3 + 1);
644 
645     angle  = mo->angle;
646     if(!accurate)
647     {
648         angle += (P_Random() - P_Random()) << 18;
649     }
650 
651     P_LineAttack(mo, angle, MISSILERANGE, bulletSlope, damage, MT_PUFF);
652 }
653 
A_FirePistol(player_t * player,pspdef_t * psp)654 void C_DECL A_FirePistol(player_t *player, pspdef_t *psp)
655 {
656     S_StartSound(SFX_PISTOL, player->plr->mo);
657 
658     P_MobjChangeState(player->plr->mo, PCLASS_INFO(player->class_)->attackEndState);
659 
660     P_ShotAmmo(player);
661 
662     P_SetPsprite(player, ps_flash, weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_FLASH]);
663 
664     player->update |= PSF_AMMO;
665     if(IS_CLIENT)
666         return;
667 
668     P_BulletSlope(player->plr->mo);
669     P_GunShot(player->plr->mo, !player->refire);
670 }
671 
A_FireShotgun(player_t * player,pspdef_t * psp)672 void C_DECL A_FireShotgun(player_t *player, pspdef_t *psp)
673 {
674     int                 i;
675 
676     S_StartSound(SFX_SHOTGN, player->plr->mo);
677     P_MobjChangeState(player->plr->mo, PCLASS_INFO(player->class_)->attackEndState);
678 
679     P_ShotAmmo(player);
680 
681     P_SetPsprite(player, ps_flash, weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_FLASH]);
682 
683     player->update |= PSF_AMMO;
684     if(IS_CLIENT)
685         return;
686 
687     P_BulletSlope(player->plr->mo);
688 
689     for(i = 0; i < 7; ++i)
690         P_GunShot(player->plr->mo, false);
691 }
692 
A_FireShotgun2(player_t * player,pspdef_t * psp)693 void C_DECL A_FireShotgun2(player_t *player, pspdef_t *psp)
694 {
695     int i;
696     angle_t angle;
697     int damage;
698 
699     S_StartSound(SFX_DSHTGN, player->plr->mo);
700     P_MobjChangeState(player->plr->mo, PCLASS_INFO(player->class_)->attackEndState);
701 
702     P_ShotAmmo(player);
703 
704     P_SetPsprite(player, ps_flash, weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_FLASH]);
705 
706     player->update |= PSF_AMMO;
707     if(IS_CLIENT)
708         return;
709 
710     P_BulletSlope(player->plr->mo);
711 
712     // jd64 >
713     if(cfg.weaponRecoil)
714     {
715         uint an;
716 
717         player->plr->mo->angle += ANG90/90;
718         an = (player->plr->mo->angle + ANG180) >> ANGLETOFINESHIFT;
719 
720         player->plr->mo->mom[MX] += 4 * FIX2FLT(finecosine[an]);
721         player->plr->mo->mom[MY] += 4 * FIX2FLT(finesine[an]);
722     }
723     // < d64tc
724 
725     for(i = 0; i < 20; ++i)
726     {
727         damage = 5 * (P_Random() % 3 + 1);
728         angle = player->plr->mo->angle;
729         angle += (P_Random() - P_Random()) << 19;
730 
731         P_LineAttack(player->plr->mo, angle, MISSILERANGE,
732                      bulletSlope + FIX2FLT((P_Random() - P_Random()) << 5),
733                      damage, MT_PUFF);
734     }
735 }
736 
A_OpenShotgun2(player_t * player,pspdef_t * psp)737 void C_DECL A_OpenShotgun2(player_t *player, pspdef_t *psp)
738 {
739     S_StartSound(SFX_DBOPN, player->plr->mo);
740 }
741 
A_LoadShotgun2(player_t * player,pspdef_t * psp)742 void C_DECL A_LoadShotgun2(player_t *player, pspdef_t *psp)
743 {
744     S_StartSound(SFX_DBLOAD, player->plr->mo);
745 }
746 
A_FireCGun(player_t * player,pspdef_t * psp)747 void C_DECL A_FireCGun(player_t *player, pspdef_t *psp)
748 {
749     S_StartSound(SFX_PISTOL, player->plr->mo);
750 
751     P_MobjChangeState(player->plr->mo, PCLASS_INFO(player->class_)->attackEndState);
752 
753     P_ShotAmmo(player);
754 
755     P_SetPsprite(player, ps_flash,
756                  weaponInfo[player->readyWeapon][player->class_].mode[0].states[WSN_FLASH] + psp->state -
757                  &STATES[S_CHAIN1]);
758 
759     player->update |= PSF_AMMO;
760     if(IS_CLIENT)
761         return;
762 
763     psp->pos[VY] = WEAPONTOP + FIX2FLT((P_Random() & 8) - 2); // jd64
764 
765     P_BulletSlope(player->plr->mo);
766 
767     // jd64 >
768     if(cfg.weaponRecoil)
769     {   // Nice little recoil effect.
770         player->plr->mo->angle += ANG90/256;
771     }
772     // < d64tc
773 
774     P_GunShot(player->plr->mo, !player->refire);
775 }
776 
A_Light0(player_t * player,pspdef_t * psp)777 void C_DECL A_Light0(player_t *player, pspdef_t *psp)
778 {
779     player->plr->extraLight = 0;
780 }
781 
A_Light1(player_t * player,pspdef_t * psp)782 void C_DECL A_Light1(player_t *player, pspdef_t *psp)
783 {
784     player->plr->extraLight = 1;
785 }
786 
A_Light2(player_t * player,pspdef_t * psp)787 void C_DECL A_Light2(player_t *player, pspdef_t *psp)
788 {
789     player->plr->extraLight = 2;
790 }
791 
792 /**
793  * Spawn a BFG explosion on every monster in view.
794  */
A_BFGSpray(mobj_t * mo)795 void C_DECL A_BFGSpray(mobj_t* mo)
796 {
797     int i, j, damage;
798     angle_t angle;
799 
800     // Offset angles from its attack angle.
801     for(i = 0; i < 40; ++i)
802     {
803         angle = mo->angle - ANG90 / 2 + ANG90 / 40 * i;
804 
805         // mo->target is the originator (player) of the missile.
806         P_AimLineAttack(mo->target, angle, 16 * 64);
807 
808         if(!lineTarget) continue;
809 
810         P_SpawnMobjXYZ(MT_EXTRABFG, lineTarget->origin[VX], lineTarget->origin[VY],
811                                     lineTarget->origin[VZ] + lineTarget->height / 4,
812                                     angle + ANG180, 0);
813 
814         damage = 0;
815         for(j = 0; j < 15; ++j)
816         {
817             damage += (P_Random() & 7) + 1;
818         }
819 
820         P_DamageMobj(lineTarget, mo->target, mo->target, damage, false);
821     }
822 }
823 
A_BFGsound(player_t * player,pspdef_t * psp)824 void C_DECL A_BFGsound(player_t* player, pspdef_t* psp)
825 {
826     S_StartSound(SFX_BFG, player->plr->mo);
827 }
828 
829 /**
830  * Called at start of level for each player.
831  */
P_SetupPsprites(player_t * player)832 void P_SetupPsprites(player_t* player)
833 {
834     int i;
835 
836     // Remove all psprites.
837     for(i = 0; i < NUMPSPRITES; ++i)
838         player->pSprites[i].state = NULL;
839 
840     // Spawn the gun.
841     if(player->pendingWeapon == WT_NOCHANGE)
842         player->pendingWeapon = player->readyWeapon;
843     P_BringUpWeapon(player);
844 }
845 
846 /**
847  * Called every tic by player thinking routine.
848  */
P_MovePsprites(player_t * player)849 void P_MovePsprites(player_t *player)
850 {
851     int                 i;
852     pspdef_t           *psp;
853     state_t            *state;
854 
855     psp = &player->pSprites[0];
856     for(i = 0; i < NUMPSPRITES; ++i, psp++)
857     {
858         // A null state means not active.
859         state = psp->state;
860         if(state)
861         {
862             // Decrease tic count and possibly change state.
863             // A -1 tic count never changes.
864             if(psp->tics != -1)
865             {
866                 psp->tics--;
867                 if(!psp->tics)
868                     P_SetPsprite(player, i, psp->state->nextState);
869             }
870         }
871     }
872 
873     player->pSprites[ps_flash].pos[VX] = player->pSprites[ps_weapon].pos[VX];
874     player->pSprites[ps_flash].pos[VY] = player->pSprites[ps_weapon].pos[VY];
875 }
876