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