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