1
2 //**************************************************************************
3 //**
4 //** p_pspr.c : Heretic 2 : Raven Software, Corp.
5 //**
6 //** $RCSfile: p_pspr.c,v $
7 //** $Revision: 1.105 $
8 //** $Date: 96/01/06 03:23:35 $
9 //** $Author: bgokey $
10 //**
11 //**************************************************************************
12
13 // HEADER FILES ------------------------------------------------------------
14
15 #include <stdlib.h>
16
17 #include "doomdef.h"
18 #include "d_event.h"
19 #include "c_cvars.h"
20 #include "m_random.h"
21 #include "p_enemy.h"
22 #include "p_local.h"
23 #include "s_sound.h"
24 #include "doomstat.h"
25 #include "gi.h"
26 #include "p_pspr.h"
27 #include "templates.h"
28 #include "thingdef/thingdef.h"
29 #include "g_level.h"
30 #include "farchive.h"
31 #include "d_player.h"
32
33
34 // MACROS ------------------------------------------------------------------
35
36 #define LOWERSPEED FRACUNIT*6
37 #define RAISESPEED FRACUNIT*6
38
39 // TYPES -------------------------------------------------------------------
40
41 struct FGenericButtons
42 {
43 int ReadyFlag; // Flag passed to A_WeaponReady
44 int StateFlag; // Flag set in WeaponState
45 int ButtonFlag; // Button to press
46 ENamedName StateName; // Name of the button/state
47 };
48
49 enum EWRF_Options
50 {
51 WRF_NoBob = 1,
52 WRF_NoSwitch = 1 << 1,
53 WRF_NoPrimary = 1 << 2,
54 WRF_NoSecondary = 1 << 3,
55 WRF_NoFire = WRF_NoPrimary | WRF_NoSecondary,
56 WRF_AllowReload = 1 << 4,
57 WRF_AllowZoom = 1 << 5,
58 WRF_DisableSwitch = 1 << 6,
59 WRF_AllowUser1 = 1 << 7,
60 WRF_AllowUser2 = 1 << 8,
61 WRF_AllowUser3 = 1 << 9,
62 WRF_AllowUser4 = 1 << 10,
63 };
64
65 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
66
67 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
68
69 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
70
71 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
72
73 // PUBLIC DATA DEFINITIONS -------------------------------------------------
74
75 // [SO] 1=Weapons states are all 1 tick
76 // 2=states with a function 1 tick, others 0 ticks.
77 CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO);
78
79 // PRIVATE DATA DEFINITIONS ------------------------------------------------
80
81 static FRandom pr_wpnreadysnd ("WpnReadySnd");
82 static FRandom pr_gunshot ("GunShot");
83
84 static const FGenericButtons ButtonChecks[] =
85 {
86 { WRF_AllowZoom, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom },
87 { WRF_AllowReload, WF_WEAPONRELOADOK, BT_RELOAD, NAME_Reload },
88 { WRF_AllowUser1, WF_USER1OK, BT_USER1, NAME_User1 },
89 { WRF_AllowUser2, WF_USER2OK, BT_USER2, NAME_User2 },
90 { WRF_AllowUser3, WF_USER3OK, BT_USER3, NAME_User3 },
91 { WRF_AllowUser4, WF_USER4OK, BT_USER4, NAME_User4 },
92 };
93
94 // CODE --------------------------------------------------------------------
95
96 //---------------------------------------------------------------------------
97 //
98 // PROC P_NewPspriteTick
99 //
100 //---------------------------------------------------------------------------
101
P_NewPspriteTick()102 void P_NewPspriteTick()
103 {
104 // This function should be called after the beginning of a tick, before any possible
105 // prprite-event, or near the end, after any possible psprite event.
106 // Because data is reset for every tick (which it must be) this has no impact on savegames.
107 for (int i = 0; i<MAXPLAYERS; i++)
108 {
109 if (playeringame[i])
110 {
111 pspdef_t *pspdef = players[i].psprites;
112 for (int j = 0;j < NUMPSPRITES; j++)
113 {
114 pspdef[j].processPending = true;
115 }
116 }
117 }
118 }
119
120 //---------------------------------------------------------------------------
121 //
122 // PROC P_SetPsprite
123 //
124 //---------------------------------------------------------------------------
125
P_SetPsprite(player_t * player,int position,FState * state,bool nofunction)126 void P_SetPsprite (player_t *player, int position, FState *state, bool nofunction)
127 {
128 pspdef_t *psp;
129
130 if (position == ps_weapon && !nofunction)
131 { // A_WeaponReady will re-set these as needed
132 player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK |
133 WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK);
134 }
135
136 psp = &player->psprites[position];
137 psp->processPending = false; // Do not subsequently perform periodic processing within the same tick.
138
139 do
140 {
141 if (state == NULL)
142 { // Object removed itself.
143 psp->state = NULL;
144 break;
145 }
146 psp->state = state;
147
148 if (state->sprite != SPR_FIXED)
149 { // okay to change sprite and/or frame
150 if (!state->GetSameFrame())
151 { // okay to change frame
152 psp->frame = state->GetFrame();
153 }
154 if (state->sprite != SPR_NOCHANGE)
155 { // okay to change sprite
156 psp->sprite = state->sprite;
157 }
158 }
159
160
161 if (sv_fastweapons == 2 && position == ps_weapon)
162 psp->tics = state->ActionFunc == NULL ? 0 : 1;
163 else if (sv_fastweapons == 3)
164 psp->tics = (state->GetTics() != 0);
165 else if (sv_fastweapons)
166 psp->tics = 1; // great for producing decals :)
167 else
168 psp->tics = state->GetTics(); // could be 0
169
170 if (state->GetMisc1())
171 { // Set coordinates.
172 psp->sx = state->GetMisc1()<<FRACBITS;
173 }
174 if (state->GetMisc2())
175 {
176 psp->sy = state->GetMisc2()<<FRACBITS;
177 }
178
179 if (!nofunction && player->mo != NULL)
180 {
181 if (state->CallAction(player->mo, player->ReadyWeapon))
182 {
183 if (!psp->state)
184 {
185 break;
186 }
187 }
188 }
189
190 state = psp->state->GetNextState();
191 } while (!psp->tics); // An initial state of 0 could cycle through.
192 }
193
194 //---------------------------------------------------------------------------
195 //
196 // PROC P_BringUpWeapon
197 //
198 // Starts bringing the pending weapon up from the bottom of the screen.
199 // This is only called to start the rising, not throughout it.
200 //
201 //---------------------------------------------------------------------------
202
P_BringUpWeapon(player_t * player)203 void P_BringUpWeapon (player_t *player)
204 {
205 FState *newstate;
206 AWeapon *weapon;
207
208 if (player->PendingWeapon == WP_NOCHANGE)
209 {
210 if (player->ReadyWeapon != NULL)
211 {
212 player->psprites[ps_weapon].sy = WEAPONTOP;
213 P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
214 }
215 return;
216 }
217
218 weapon = player->PendingWeapon;
219
220 // If the player has a tome of power, use this weapon's powered up
221 // version, if one is available.
222 if (weapon != NULL &&
223 weapon->SisterWeapon &&
224 weapon->SisterWeapon->WeaponFlags & WIF_POWERED_UP &&
225 player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true))
226 {
227 weapon = weapon->SisterWeapon;
228 }
229
230 if (weapon != NULL)
231 {
232 if (weapon->UpSound)
233 {
234 S_Sound (player->mo, CHAN_WEAPON, weapon->UpSound, 1, ATTN_NORM);
235 }
236 newstate = weapon->GetUpState ();
237 player->refire = 0;
238 }
239 else
240 {
241 newstate = NULL;
242 }
243 player->PendingWeapon = WP_NOCHANGE;
244 player->ReadyWeapon = weapon;
245 player->psprites[ps_weapon].sy = player->cheats & CF_INSTANTWEAPSWITCH
246 ? WEAPONTOP : WEAPONBOTTOM;
247 // make sure that the previous weapon's flash state is terminated.
248 // When coming here from a weapon drop it may still be active.
249 P_SetPsprite(player, ps_flash, NULL);
250 P_SetPsprite (player, ps_weapon, newstate);
251 player->mo->weaponspecial = 0;
252 }
253
254
255 //---------------------------------------------------------------------------
256 //
257 // PROC P_FireWeapon
258 //
259 //---------------------------------------------------------------------------
260
P_FireWeapon(player_t * player,FState * state)261 void P_FireWeapon (player_t *player, FState *state)
262 {
263 AWeapon *weapon;
264
265 // [SO] 9/2/02: People were able to do an awful lot of damage
266 // when they were observers...
267 if (player->Bot == NULL && bot_observer)
268 {
269 return;
270 }
271
272 weapon = player->ReadyWeapon;
273 if (weapon == NULL || !weapon->CheckAmmo (AWeapon::PrimaryFire, true))
274 {
275 return;
276 }
277
278 player->mo->PlayAttacking ();
279 weapon->bAltFire = false;
280 if (state == NULL)
281 {
282 state = weapon->GetAtkState(!!player->refire);
283 }
284 P_SetPsprite (player, ps_weapon, state);
285 if (!(weapon->WeaponFlags & WIF_NOALERT))
286 {
287 P_NoiseAlert (player->mo, player->mo, false);
288 }
289 }
290
291 //---------------------------------------------------------------------------
292 //
293 // PROC P_FireWeaponAlt
294 //
295 //---------------------------------------------------------------------------
296
P_FireWeaponAlt(player_t * player,FState * state)297 void P_FireWeaponAlt (player_t *player, FState *state)
298 {
299 AWeapon *weapon;
300
301 // [SO] 9/2/02: People were able to do an awful lot of damage
302 // when they were observers...
303 if (player->Bot == NULL && bot_observer)
304 {
305 return;
306 }
307
308 weapon = player->ReadyWeapon;
309 if (weapon == NULL || weapon->FindState(NAME_AltFire) == NULL || !weapon->CheckAmmo (AWeapon::AltFire, true))
310 {
311 return;
312 }
313
314 player->mo->PlayAttacking ();
315 weapon->bAltFire = true;
316
317 if (state == NULL)
318 {
319 state = weapon->GetAltAtkState(!!player->refire);
320 }
321
322 P_SetPsprite (player, ps_weapon, state);
323 if (!(weapon->WeaponFlags & WIF_NOALERT))
324 {
325 P_NoiseAlert (player->mo, player->mo, false);
326 }
327 }
328
329 //---------------------------------------------------------------------------
330 //
331 // PROC P_DropWeapon
332 //
333 // The player died, so put the weapon away.
334 //
335 //---------------------------------------------------------------------------
336
P_DropWeapon(player_t * player)337 void P_DropWeapon (player_t *player)
338 {
339 if (player == NULL)
340 {
341 return;
342 }
343 // Since the weapon is dropping, stop blocking switching.
344 player->WeaponState &= ~WF_DISABLESWITCH;
345 if (player->ReadyWeapon != NULL)
346 {
347 P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetDownState());
348 }
349 }
350
351 //============================================================================
352 //
353 // P_BobWeapon
354 //
355 // [RH] Moved this out of A_WeaponReady so that the weapon can bob every
356 // tic and not just when A_WeaponReady is called. Not all weapons execute
357 // A_WeaponReady every tic, and it looks bad if they don't bob smoothly.
358 //
359 // [XA] Added new bob styles and exposed bob properties. Thanks, Ryan Cordell!
360 //
361 //============================================================================
362
P_BobWeapon(player_t * player,pspdef_t * psp,fixed_t * x,fixed_t * y)363 void P_BobWeapon (player_t *player, pspdef_t *psp, fixed_t *x, fixed_t *y)
364 {
365 static fixed_t curbob;
366
367 AWeapon *weapon;
368 fixed_t bobtarget;
369
370 weapon = player->ReadyWeapon;
371
372 if (weapon == NULL || weapon->WeaponFlags & WIF_DONTBOB)
373 {
374 *x = *y = 0;
375 return;
376 }
377
378 // [XA] Get the current weapon's bob properties.
379 int bobstyle = weapon->BobStyle;
380 int bobspeed = (weapon->BobSpeed * 128) >> 16;
381 fixed_t rangex = weapon->BobRangeX;
382 fixed_t rangey = weapon->BobRangeY;
383
384 // Bob the weapon based on movement speed.
385 int angle = (bobspeed*35/TICRATE*level.time)&FINEMASK;
386
387 // [RH] Smooth transitions between bobbing and not-bobbing frames.
388 // This also fixes the bug where you can "stick" a weapon off-center by
389 // shooting it when it's at the peak of its swing.
390 bobtarget = (player->WeaponState & WF_WEAPONBOBBING) ? player->bob : 0;
391 if (curbob != bobtarget)
392 {
393 if (abs (bobtarget - curbob) <= 1*FRACUNIT)
394 {
395 curbob = bobtarget;
396 }
397 else
398 {
399 fixed_t zoom = MAX<fixed_t> (1*FRACUNIT, abs (curbob - bobtarget) / 40);
400 if (curbob > bobtarget)
401 {
402 curbob -= zoom;
403 }
404 else
405 {
406 curbob += zoom;
407 }
408 }
409 }
410
411 if (curbob != 0)
412 {
413 fixed_t bobx = FixedMul(player->bob, rangex);
414 fixed_t boby = FixedMul(player->bob, rangey);
415 switch (bobstyle)
416 {
417 case AWeapon::BobNormal:
418 *x = FixedMul(bobx, finecosine[angle]);
419 *y = FixedMul(boby, finesine[angle & (FINEANGLES/2-1)]);
420 break;
421
422 case AWeapon::BobInverse:
423 *x = FixedMul(bobx, finecosine[angle]);
424 *y = boby - FixedMul(boby, finesine[angle & (FINEANGLES/2-1)]);
425 break;
426
427 case AWeapon::BobAlpha:
428 *x = FixedMul(bobx, finesine[angle]);
429 *y = FixedMul(boby, finesine[angle & (FINEANGLES/2-1)]);
430 break;
431
432 case AWeapon::BobInverseAlpha:
433 *x = FixedMul(bobx, finesine[angle]);
434 *y = boby - FixedMul(boby, finesine[angle & (FINEANGLES/2-1)]);
435 break;
436
437 case AWeapon::BobSmooth:
438 *x = FixedMul(bobx, finecosine[angle]);
439 *y = (boby - FixedMul(boby, finecosine[angle*2 & (FINEANGLES-1)])) / 2;
440 break;
441
442 case AWeapon::BobInverseSmooth:
443 *x = FixedMul(bobx, finecosine[angle]);
444 *y = (FixedMul(boby, finecosine[angle*2 & (FINEANGLES-1)]) + boby) / 2;
445 }
446 }
447 else
448 {
449 *x = 0;
450 *y = 0;
451 }
452 }
453
454 //============================================================================
455 //
456 // PROC A_WeaponReady
457 //
458 // Readies a weapon for firing or bobbing with its three ancillary functions,
459 // DoReadyWeaponToSwitch(), DoReadyWeaponToFire() and DoReadyWeaponToBob().
460 // [XA] Added DoReadyWeaponToReload() and DoReadyWeaponToZoom()
461 //
462 //============================================================================
463
DoReadyWeaponToSwitch(AActor * self,bool switchable)464 void DoReadyWeaponToSwitch (AActor *self, bool switchable)
465 {
466 // Prepare for switching action.
467 player_t *player;
468 if (self && (player = self->player))
469 {
470 if (switchable)
471 {
472 player->WeaponState |= WF_WEAPONSWITCHOK | WF_REFIRESWITCHOK;
473 }
474 else
475 {
476 // WF_WEAPONSWITCHOK is automatically cleared every tic by P_SetPsprite().
477 player->WeaponState &= ~WF_REFIRESWITCHOK;
478 }
479 }
480 }
481
DoReadyWeaponDisableSwitch(AActor * self,INTBOOL disable)482 void DoReadyWeaponDisableSwitch (AActor *self, INTBOOL disable)
483 {
484 // Discard all switch attempts?
485 player_t *player;
486 if (self && (player = self->player))
487 {
488 if (disable)
489 {
490 player->WeaponState |= WF_DISABLESWITCH;
491 player->WeaponState &= ~WF_REFIRESWITCHOK;
492 }
493 else
494 {
495 player->WeaponState &= ~WF_DISABLESWITCH;
496 }
497 }
498 }
499
DoReadyWeaponToFire(AActor * self,bool prim,bool alt)500 void DoReadyWeaponToFire (AActor *self, bool prim, bool alt)
501 {
502 player_t *player;
503 AWeapon *weapon;
504
505 if (!self || !(player = self->player) || !(weapon = player->ReadyWeapon))
506 {
507 return;
508 }
509
510 // Change player from attack state
511 if (self->InStateSequence(self->state, self->MissileState) ||
512 self->InStateSequence(self->state, self->MeleeState))
513 {
514 static_cast<APlayerPawn *>(self)->PlayIdle ();
515 }
516
517 // Play ready sound, if any.
518 if (weapon->ReadySound && player->psprites[ps_weapon].state == weapon->FindState(NAME_Ready))
519 {
520 if (!(weapon->WeaponFlags & WIF_READYSNDHALF) || pr_wpnreadysnd() < 128)
521 {
522 S_Sound (self, CHAN_WEAPON, weapon->ReadySound, 1, ATTN_NORM);
523 }
524 }
525
526 // Prepare for firing action.
527 player->WeaponState |= ((prim ? WF_WEAPONREADY : 0) | (alt ? WF_WEAPONREADYALT : 0));
528 return;
529 }
530
DoReadyWeaponToBob(AActor * self)531 void DoReadyWeaponToBob (AActor *self)
532 {
533 if (self && self->player && self->player->ReadyWeapon)
534 {
535 // Prepare for bobbing action.
536 self->player->WeaponState |= WF_WEAPONBOBBING;
537 self->player->psprites[ps_weapon].sx = 0;
538 self->player->psprites[ps_weapon].sy = WEAPONTOP;
539 }
540 }
541
DoReadyWeaponToGeneric(AActor * self,int paramflags)542 void DoReadyWeaponToGeneric(AActor *self, int paramflags)
543 {
544 int flags = 0;
545
546 for (size_t i = 0; i < countof(ButtonChecks); ++i)
547 {
548 if (paramflags & ButtonChecks[i].ReadyFlag)
549 {
550 flags |= ButtonChecks[i].StateFlag;
551 }
552 }
553 if (self != NULL && self->player != NULL)
554 {
555 self->player->WeaponState |= flags;
556 }
557 }
558
559 // This function replaces calls to A_WeaponReady in other codepointers.
DoReadyWeapon(AActor * self)560 void DoReadyWeapon(AActor *self)
561 {
562 DoReadyWeaponToBob(self);
563 DoReadyWeaponToFire(self);
564 DoReadyWeaponToSwitch(self);
565 DoReadyWeaponToGeneric(self, ~0);
566 }
567
DEFINE_ACTION_FUNCTION_PARAMS(AInventory,A_WeaponReady)568 DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady)
569 {
570 ACTION_PARAM_START(1);
571 ACTION_PARAM_INT(paramflags, 0);
572
573 DoReadyWeaponToSwitch(self, !(paramflags & WRF_NoSwitch));
574 if ((paramflags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(self, !(paramflags & WRF_NoPrimary), !(paramflags & WRF_NoSecondary));
575 if (!(paramflags & WRF_NoBob)) DoReadyWeaponToBob(self);
576 DoReadyWeaponToGeneric(self, paramflags);
577 DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch);
578 }
579
580 //---------------------------------------------------------------------------
581 //
582 // PROC P_CheckWeaponFire
583 //
584 // The player can fire the weapon.
585 // [RH] This was in A_WeaponReady before, but that only works well when the
586 // weapon's ready frames have a one tic delay.
587 //
588 //---------------------------------------------------------------------------
589
P_CheckWeaponFire(player_t * player)590 void P_CheckWeaponFire (player_t *player)
591 {
592 AWeapon *weapon = player->ReadyWeapon;
593
594 if (weapon == NULL)
595 return;
596
597 // Check for fire. Some weapons do not auto fire.
598 if ((player->WeaponState & WF_WEAPONREADY) && (player->cmd.ucmd.buttons & BT_ATTACK))
599 {
600 if (!player->attackdown || !(weapon->WeaponFlags & WIF_NOAUTOFIRE))
601 {
602 player->attackdown = true;
603 P_FireWeapon (player, NULL);
604 return;
605 }
606 }
607 else if ((player->WeaponState & WF_WEAPONREADYALT) && (player->cmd.ucmd.buttons & BT_ALTATTACK))
608 {
609 if (!player->attackdown || !(weapon->WeaponFlags & WIF_NOAUTOFIRE))
610 {
611 player->attackdown = true;
612 P_FireWeaponAlt (player, NULL);
613 return;
614 }
615 }
616 else
617 {
618 player->attackdown = false;
619 }
620 }
621
622 //---------------------------------------------------------------------------
623 //
624 // PROC P_CheckWeaponSwitch
625 //
626 // The player can change to another weapon at this time.
627 // [GZ] This was cut from P_CheckWeaponFire.
628 //
629 //---------------------------------------------------------------------------
630
P_CheckWeaponSwitch(player_t * player)631 void P_CheckWeaponSwitch (player_t *player)
632 {
633 if (player == NULL)
634 {
635 return;
636 }
637 if ((player->WeaponState & WF_DISABLESWITCH) || // Weapon changing has been disabled.
638 player->morphTics != 0) // Morphed classes cannot change weapons.
639 { // ...so throw away any pending weapon requests.
640 player->PendingWeapon = WP_NOCHANGE;
641 }
642
643 // Put the weapon away if the player has a pending weapon or has died, and
644 // we're at a place in the state sequence where dropping the weapon is okay.
645 if ((player->PendingWeapon != WP_NOCHANGE || player->health <= 0) &&
646 player->WeaponState & WF_WEAPONSWITCHOK)
647 {
648 P_DropWeapon(player);
649 }
650 }
651
652 //---------------------------------------------------------------------------
653 //
654 // PROC P_CheckWeaponButtons
655 //
656 // Check extra button presses for weapons.
657 //
658 //---------------------------------------------------------------------------
659
P_CheckWeaponButtons(player_t * player)660 static void P_CheckWeaponButtons (player_t *player)
661 {
662 if (player->Bot == NULL && bot_observer)
663 {
664 return;
665 }
666 AWeapon *weapon = player->ReadyWeapon;
667 if (weapon == NULL)
668 {
669 return;
670 }
671 // The button checks are ordered by precedence. The first one to match a
672 // button press and affect a state change wins.
673 for (size_t i = 0; i < countof(ButtonChecks); ++i)
674 {
675 if ((player->WeaponState & ButtonChecks[i].StateFlag) &&
676 (player->cmd.ucmd.buttons & ButtonChecks[i].ButtonFlag))
677 {
678 FState *state = weapon->GetStateForButtonName(ButtonChecks[i].StateName);
679 // [XA] don't change state if still null, so if the modder
680 // sets WRF_xxx to true but forgets to define the corresponding
681 // state, the weapon won't disappear. ;)
682 if (state != NULL)
683 {
684 P_SetPsprite(player, ps_weapon, state);
685 return;
686 }
687 }
688 }
689 }
690
691 //---------------------------------------------------------------------------
692 //
693 // PROC A_ReFire
694 //
695 // The player can re-fire the weapon without lowering it entirely.
696 //
697 //---------------------------------------------------------------------------
698
DEFINE_ACTION_FUNCTION_PARAMS(AInventory,A_ReFire)699 DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_ReFire)
700 {
701 ACTION_PARAM_START(1)
702 ACTION_PARAM_STATE(state, 0);
703
704 A_ReFire(self, state);
705 }
706
A_ReFire(AActor * self,FState * state)707 void A_ReFire(AActor *self, FState *state)
708 {
709 player_t *player = self->player;
710 bool pending;
711
712 if (NULL == player)
713 {
714 return;
715 }
716 pending = player->PendingWeapon != WP_NOCHANGE && (player->WeaponState & WF_REFIRESWITCHOK);
717 if ((player->cmd.ucmd.buttons & BT_ATTACK)
718 && !player->ReadyWeapon->bAltFire && !pending && player->health > 0)
719 {
720 player->refire++;
721 P_FireWeapon (player, state);
722 }
723 else if ((player->cmd.ucmd.buttons & BT_ALTATTACK)
724 && player->ReadyWeapon->bAltFire && !pending && player->health > 0)
725 {
726 player->refire++;
727 P_FireWeaponAlt (player, state);
728 }
729 else
730 {
731 player->refire = 0;
732 player->ReadyWeapon->CheckAmmo (player->ReadyWeapon->bAltFire
733 ? AWeapon::AltFire : AWeapon::PrimaryFire, true);
734 }
735 }
736
DEFINE_ACTION_FUNCTION(AInventory,A_ClearReFire)737 DEFINE_ACTION_FUNCTION(AInventory, A_ClearReFire)
738 {
739 player_t *player = self->player;
740
741 if (NULL != player)
742 {
743 player->refire = 0;
744 }
745 }
746
747 //---------------------------------------------------------------------------
748 //
749 // PROC A_CheckReload
750 //
751 // Present in Doom, but unused. Also present in Strife, and actually used.
752 // This and what I call A_XBowReFire are actually the same thing in Strife,
753 // not two separate functions as I have them here.
754 //
755 //---------------------------------------------------------------------------
756
DEFINE_ACTION_FUNCTION(AInventory,A_CheckReload)757 DEFINE_ACTION_FUNCTION(AInventory, A_CheckReload)
758 {
759 if (self->player != NULL)
760 {
761 self->player->ReadyWeapon->CheckAmmo (
762 self->player->ReadyWeapon->bAltFire ? AWeapon::AltFire
763 : AWeapon::PrimaryFire, true);
764 }
765 }
766
767 //---------------------------------------------------------------------------
768 //
769 // PROC A_Lower
770 //
771 //---------------------------------------------------------------------------
772
DEFINE_ACTION_FUNCTION(AInventory,A_Lower)773 DEFINE_ACTION_FUNCTION(AInventory, A_Lower)
774 {
775 player_t *player = self->player;
776 pspdef_t *psp;
777
778 if (NULL == player)
779 {
780 return;
781 }
782 psp = &player->psprites[ps_weapon];
783 if (player->morphTics || player->cheats & CF_INSTANTWEAPSWITCH)
784 {
785 psp->sy = WEAPONBOTTOM;
786 }
787 else
788 {
789 psp->sy += LOWERSPEED;
790 }
791 if (psp->sy < WEAPONBOTTOM)
792 { // Not lowered all the way yet
793 return;
794 }
795 if (player->playerstate == PST_DEAD)
796 { // Player is dead, so don't bring up a pending weapon
797 psp->sy = WEAPONBOTTOM;
798
799 // Player is dead, so keep the weapon off screen
800 P_SetPsprite (player, ps_weapon, NULL);
801 return;
802 }
803 // [RH] Clear the flash state. Only needed for Strife.
804 P_SetPsprite (player, ps_flash, NULL);
805 P_BringUpWeapon (player);
806 }
807
808 //---------------------------------------------------------------------------
809 //
810 // PROC A_Raise
811 //
812 //---------------------------------------------------------------------------
813
DEFINE_ACTION_FUNCTION(AInventory,A_Raise)814 DEFINE_ACTION_FUNCTION(AInventory, A_Raise)
815 {
816 if (self == NULL)
817 {
818 return;
819 }
820 player_t *player = self->player;
821 pspdef_t *psp;
822
823 if (NULL == player)
824 {
825 return;
826 }
827 if (player->PendingWeapon != WP_NOCHANGE)
828 {
829 P_DropWeapon(player);
830 return;
831 }
832 psp = &player->psprites[ps_weapon];
833 psp->sy -= RAISESPEED;
834 if (psp->sy > WEAPONTOP)
835 { // Not raised all the way yet
836 return;
837 }
838 psp->sy = WEAPONTOP;
839 if (player->ReadyWeapon != NULL)
840 {
841 P_SetPsprite (player, ps_weapon, player->ReadyWeapon->GetReadyState());
842 }
843 else
844 {
845 player->psprites[ps_weapon].state = NULL;
846 }
847 }
848
849
850
851
852 //
853 // A_GunFlash
854 //
855 enum GF_Flags
856 {
857 GFF_NOEXTCHANGE = 1,
858 };
859
DEFINE_ACTION_FUNCTION_PARAMS(AInventory,A_GunFlash)860 DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_GunFlash)
861 {
862 ACTION_PARAM_START(2)
863 ACTION_PARAM_STATE(flash, 0);
864 ACTION_PARAM_INT(Flags, 1);
865
866 player_t *player = self->player;
867
868 if (NULL == player)
869 {
870 return;
871 }
872 if(!(Flags & GFF_NOEXTCHANGE)) player->mo->PlayAttacking2 ();
873
874 if (flash == NULL)
875 {
876 if (player->ReadyWeapon->bAltFire) flash = player->ReadyWeapon->FindState(NAME_AltFlash);
877 if (flash == NULL) flash = player->ReadyWeapon->FindState(NAME_Flash);
878 }
879 P_SetPsprite (player, ps_flash, flash);
880 }
881
882
883
884 //
885 // WEAPON ATTACKS
886 //
887
888 //
889 // P_BulletSlope
890 // Sets a slope so a near miss is at aproximately
891 // the height of the intended target
892 //
893
P_BulletSlope(AActor * mo,AActor ** pLineTarget)894 angle_t P_BulletSlope (AActor *mo, AActor **pLineTarget)
895 {
896 static const int angdiff[3] = { -(1<<26), 1<<26, 0 };
897 int i;
898 angle_t an;
899 angle_t pitch;
900 AActor *linetarget;
901
902 // see which target is to be aimed at
903 i = 2;
904 do
905 {
906 an = mo->angle + angdiff[i];
907 pitch = P_AimLineAttack (mo, an, 16*64*FRACUNIT, &linetarget);
908
909 if (mo->player != NULL &&
910 level.IsFreelookAllowed() &&
911 mo->player->userinfo.GetAimDist() <= ANGLE_1/2)
912 {
913 break;
914 }
915 } while (linetarget == NULL && --i >= 0);
916 if (pLineTarget != NULL)
917 {
918 *pLineTarget = linetarget;
919 }
920 return pitch;
921 }
922
923
924 //
925 // P_GunShot
926 //
P_GunShot(AActor * mo,bool accurate,const PClass * pufftype,angle_t pitch)927 void P_GunShot (AActor *mo, bool accurate, const PClass *pufftype, angle_t pitch)
928 {
929 angle_t angle;
930 int damage;
931
932 damage = 5*(pr_gunshot()%3+1);
933 angle = mo->angle;
934
935 if (!accurate)
936 {
937 angle += pr_gunshot.Random2 () << 18;
938 }
939
940 P_LineAttack (mo, angle, PLAYERMISSILERANGE, pitch, damage, NAME_Hitscan, pufftype);
941 }
942
DEFINE_ACTION_FUNCTION(AInventory,A_Light0)943 DEFINE_ACTION_FUNCTION(AInventory, A_Light0)
944 {
945 if (self->player != NULL)
946 {
947 self->player->extralight = 0;
948 }
949 }
950
DEFINE_ACTION_FUNCTION(AInventory,A_Light1)951 DEFINE_ACTION_FUNCTION(AInventory, A_Light1)
952 {
953 if (self->player != NULL)
954 {
955 self->player->extralight = 1;
956 }
957 }
958
DEFINE_ACTION_FUNCTION(AInventory,A_Light2)959 DEFINE_ACTION_FUNCTION(AInventory, A_Light2)
960 {
961 if (self->player != NULL)
962 {
963 self->player->extralight = 2;
964 }
965 }
966
DEFINE_ACTION_FUNCTION_PARAMS(AInventory,A_Light)967 DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_Light)
968 {
969 ACTION_PARAM_START(1);
970 ACTION_PARAM_INT(light, 0);
971
972 if (self->player != NULL)
973 {
974 self->player->extralight = clamp<int>(light, -20, 20);
975 }
976 }
977
978 //------------------------------------------------------------------------
979 //
980 // PROC P_SetupPsprites
981 //
982 // Called at start of level for each player
983 //
984 //------------------------------------------------------------------------
985
P_SetupPsprites(player_t * player,bool startweaponup)986 void P_SetupPsprites(player_t *player, bool startweaponup)
987 {
988 int i;
989
990 // Remove all psprites
991 for (i = 0; i < NUMPSPRITES; i++)
992 {
993 player->psprites[i].state = NULL;
994 }
995 // Spawn the ready weapon
996 player->PendingWeapon = !startweaponup ? player->ReadyWeapon : WP_NOCHANGE;
997 P_BringUpWeapon (player);
998 }
999
1000 //------------------------------------------------------------------------
1001 //
1002 // PROC P_MovePsprites
1003 //
1004 // Called every tic by player thinking routine
1005 //
1006 //------------------------------------------------------------------------
1007
P_MovePsprites(player_t * player)1008 void P_MovePsprites (player_t *player)
1009 {
1010 int i;
1011 pspdef_t *psp;
1012 FState *state;
1013
1014 // [RH] If you don't have a weapon, then the psprites should be NULL.
1015 if (player->ReadyWeapon == NULL && (player->health > 0 || player->mo->DamageType != NAME_Fire))
1016 {
1017 P_SetPsprite (player, ps_weapon, NULL);
1018 P_SetPsprite (player, ps_flash, NULL);
1019 if (player->PendingWeapon != WP_NOCHANGE)
1020 {
1021 P_BringUpWeapon (player);
1022 }
1023 }
1024 else
1025 {
1026 psp = &player->psprites[0];
1027 for (i = 0; i < NUMPSPRITES; i++, psp++)
1028 {
1029 if ((state = psp->state) != NULL && psp->processPending) // a null state means not active
1030 {
1031 // drop tic count and possibly change state
1032 if (psp->tics != -1) // a -1 tic count never changes
1033 {
1034 psp->tics--;
1035
1036 // [BC] Apply double firing speed.
1037 if ( psp->tics && (player->cheats & CF_DOUBLEFIRINGSPEED))
1038 psp->tics--;
1039
1040 if(!psp->tics)
1041 {
1042 P_SetPsprite (player, i, psp->state->GetNextState());
1043 }
1044 }
1045 }
1046 }
1047 player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
1048 player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
1049 P_CheckWeaponSwitch (player);
1050 if (player->WeaponState & (WF_WEAPONREADY | WF_WEAPONREADYALT))
1051 {
1052 P_CheckWeaponFire (player);
1053 }
1054
1055 // Check custom buttons
1056 P_CheckWeaponButtons(player);
1057 }
1058 }
1059
operator <<(FArchive & arc,pspdef_t & def)1060 FArchive &operator<< (FArchive &arc, pspdef_t &def)
1061 {
1062 arc << def.state << def.tics << def.sx << def.sy
1063 << def.sprite << def.frame;
1064 return arc;
1065 }
1066