1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
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.
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., 59 Temple Place - Suite 330, Boston, MA
28 * Weapon sprite animation, weapon objects.
29 * Action functions for weapons.
31 *-----------------------------------------------------------------------------*/
43 #include "rockmacros.h"
44 #define LOWERSPEED (FRACUNIT*6)
45 #define RAISESPEED (FRACUNIT*6)
46 #define WEAPONBOTTOM (FRACUNIT*128)
47 #define WEAPONTOP (FRACUNIT*32)
49 #define BFGCELLS bfgcells /* Ty 03/09/98 externalized in p_inter.c */
51 extern void P_Thrust(player_t
*, angle_t
, fixed_t
);
53 // The following array holds the recoil values // phares
55 static const int recoil_values
[] = { // phares
71 static void P_SetPsprite(player_t
*player
, int position
, statenum_t stnum
)
73 pspdef_t
*psp
= &player
->psprites
[position
];
81 // object removed itself
86 state
= &states
[stnum
];
88 psp
->tics
= state
->tics
; // could be 0
93 psp
->sx
= state
->misc1
<< FRACBITS
;
94 psp
->sy
= state
->misc2
<< FRACBITS
;
97 // Call action routine.
101 state
->action(player
, psp
);
105 stnum
= psp
->state
->nextstate
;
107 while (!psp
->tics
); // an initial state of 0 could cycle through
112 // Starts bringing the pending weapon up
113 // from the bottom of the screen.
117 static void P_BringUpWeapon(player_t
*player
)
121 if (player
->pendingweapon
== wp_nochange
)
122 player
->pendingweapon
= player
->readyweapon
;
124 if (player
->pendingweapon
== wp_chainsaw
)
125 S_StartSound (player
->mo
, sfx_sawup
);
127 newstate
= weaponinfo
[player
->pendingweapon
].upstate
;
129 player
->pendingweapon
= wp_nochange
;
130 // killough 12/98: prevent pistol from starting visibly at bottom of screen:
131 player
->psprites
[ps_weapon
].sy
=
132 mbf_features
? WEAPONBOTTOM
+FRACUNIT
*2 : WEAPONBOTTOM
;
134 P_SetPsprite(player
, ps_weapon
, newstate
);
137 // The first set is where the weapon preferences from // killough,
138 // default.cfg are stored. These values represent the keys used // phares
139 // in DOOM2 to bring up the weapon, i.e. 6 = plasma gun. These // |
140 // are NOT the wp_* constants. // V
142 int weapon_preferences
[2][NUMWEAPONS
+1] = {
143 {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // !compatibility preferences
144 {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // compatibility preferences
147 // P_SwitchWeapon checks current ammo levels and gives you the
148 // most preferred weapon with ammo. It will not pick the currently
149 // raised weapon. When called from P_CheckAmmo this won't matter,
150 // because the raised weapon has no ammo anyway. When called from
151 // G_BuildTiccmd you want to toggle to a different weapon regardless.
153 int P_SwitchWeapon(player_t
*player
)
155 int *prefer
= weapon_preferences
[demo_compatibility
!=0]; // killough 3/22/98
156 int currentweapon
= player
->readyweapon
;
157 int newweapon
= currentweapon
;
158 int i
= NUMWEAPONS
+1; // killough 5/2/98
160 // killough 2/8/98: follow preferences and fix BFG/SSG bugs
166 if (!player
->powers
[pw_strength
]) // allow chainsaw override
172 if (player
->ammo
[am_clip
])
173 newweapon
= wp_pistol
;
176 if (player
->weaponowned
[wp_shotgun
] && player
->ammo
[am_shell
])
177 newweapon
= wp_shotgun
;
180 if (player
->weaponowned
[wp_chaingun
] && player
->ammo
[am_clip
])
181 newweapon
= wp_chaingun
;
184 if (player
->weaponowned
[wp_missile
] && player
->ammo
[am_misl
])
185 newweapon
= wp_missile
;
188 if (player
->weaponowned
[wp_plasma
] && player
->ammo
[am_cell
] &&
189 gamemode
!= shareware
)
190 newweapon
= wp_plasma
;
193 if (player
->weaponowned
[wp_bfg
] && gamemode
!= shareware
&&
194 player
->ammo
[am_cell
] >= (demo_compatibility
? 41 : 40))
198 if (player
->weaponowned
[wp_chainsaw
])
199 newweapon
= wp_chainsaw
;
202 if (player
->weaponowned
[wp_supershotgun
] && gamemode
== commercial
&&
203 player
->ammo
[am_shell
] >= (demo_compatibility
? 3 : 2))
204 newweapon
= wp_supershotgun
;
207 while (newweapon
==currentweapon
&& --i
); // killough 5/2/98
211 // killough 5/2/98: whether consoleplayer prefers weapon w1 over weapon w2.
212 int P_WeaponPreferred(int w1
, int w2
)
215 (weapon_preferences
[0][0] != ++w2
&& (weapon_preferences
[0][0] == ++w1
||
216 (weapon_preferences
[0][1] != w2
&& (weapon_preferences
[0][1] == w1
||
217 (weapon_preferences
[0][2] != w2
&& (weapon_preferences
[0][2] == w1
||
218 (weapon_preferences
[0][3] != w2
&& (weapon_preferences
[0][3] == w1
||
219 (weapon_preferences
[0][4] != w2
&& (weapon_preferences
[0][4] == w1
||
220 (weapon_preferences
[0][5] != w2
&& (weapon_preferences
[0][5] == w1
||
221 (weapon_preferences
[0][6] != w2
&& (weapon_preferences
[0][6] == w1
||
222 (weapon_preferences
[0][7] != w2
&& (weapon_preferences
[0][7] == w1
228 // Returns true if there is enough ammo to shoot.
229 // If not, selects the next weapon to use.
230 // (only in demo_compatibility mode -- killough 3/22/98)
233 boolean
P_CheckAmmo(player_t
*player
)
235 ammotype_t ammo
= weaponinfo
[player
->readyweapon
].ammo
;
236 int count
= 1; // Regular
238 if (player
->readyweapon
== wp_bfg
) // Minimal amount for one shot varies.
241 if (player
->readyweapon
== wp_supershotgun
) // Double barrel.
244 // Some do not need ammunition anyway.
245 // Return if current ammunition sufficient.
247 if (ammo
== am_noammo
|| player
->ammo
[ammo
] >= count
)
250 // Out of ammo, pick a weapon to change to.
252 // killough 3/22/98: for old demos we do the switch here and now;
253 // for Boom games we cannot do this, and have different player
254 // preferences across demos or networks, so we have to use the
255 // G_BuildTiccmd() interface instead of making the switch here.
257 if (demo_compatibility
)
259 player
->pendingweapon
= P_SwitchWeapon(player
); // phares
260 // Now set appropriate weapon overlay.
261 P_SetPsprite(player
,ps_weapon
,weaponinfo
[player
->readyweapon
].downstate
);
271 int lastshottic
; // killough 3/22/98
273 static void P_FireWeapon(player_t
*player
)
277 if (!P_CheckAmmo(player
))
280 P_SetMobjState(player
->mo
, S_PLAY_ATK1
);
281 newstate
= weaponinfo
[player
->readyweapon
].atkstate
;
282 P_SetPsprite(player
, ps_weapon
, newstate
);
283 P_NoiseAlert(player
->mo
, player
->mo
);
284 lastshottic
= gametic
; // killough 3/22/98
289 // Player died, so put the weapon away.
292 void P_DropWeapon(player_t
*player
)
294 P_SetPsprite(player
, ps_weapon
, weaponinfo
[player
->readyweapon
].downstate
);
299 // The player can fire the weapon
300 // or change to another weapon at this time.
301 // Follows after getting weapon up,
302 // or after previous attack/fire sequence.
305 void A_WeaponReady(player_t
*player
, pspdef_t
*psp
)
307 // get out of attack state
308 if (player
->mo
->state
== &states
[S_PLAY_ATK1
]
309 || player
->mo
->state
== &states
[S_PLAY_ATK2
] )
310 P_SetMobjState(player
->mo
, S_PLAY
);
312 if (player
->readyweapon
== wp_chainsaw
&& psp
->state
== &states
[S_SAW
])
313 S_StartSound(player
->mo
, sfx_sawidl
);
316 // if player is dead, put the weapon away
318 if (player
->pendingweapon
!= wp_nochange
|| !player
->health
)
320 // change weapon (pending weapon should already be validated)
321 statenum_t newstate
= weaponinfo
[player
->readyweapon
].downstate
;
322 P_SetPsprite(player
, ps_weapon
, newstate
);
327 // the missile launcher and bfg do not auto fire
329 if (player
->cmd
.buttons
& BT_ATTACK
)
331 if (!player
->attackdown
|| (player
->readyweapon
!= wp_missile
&&
332 player
->readyweapon
!= wp_bfg
))
334 player
->attackdown
= true;
335 P_FireWeapon(player
);
340 player
->attackdown
= false;
342 // bob the weapon based on movement speed
344 int angle
= (128*leveltime
) & FINEMASK
;
345 psp
->sx
= FRACUNIT
+ FixedMul(player
->bob
, finecosine
[angle
]);
346 angle
&= FINEANGLES
/2-1;
347 psp
->sy
= WEAPONTOP
+ FixedMul(player
->bob
, finesine
[angle
]);
353 // The player can re-fire the weapon
354 // without lowering it entirely.
357 void A_ReFire(player_t
*player
, pspdef_t
*psp
)
361 // (if a weaponchange is pending, let it go through instead)
363 if ( (player
->cmd
.buttons
& BT_ATTACK
)
364 && player
->pendingweapon
== wp_nochange
&& player
->health
)
367 P_FireWeapon(player
);
376 void A_CheckReload(player_t
*player
, pspdef_t
*psp
)
384 // Lowers current weapon,
385 // and changes weapon at bottom.
388 void A_Lower(player_t
*player
, pspdef_t
*psp
)
390 psp
->sy
+= LOWERSPEED
;
393 if (psp
->sy
< WEAPONBOTTOM
)
397 if (player
->playerstate
== PST_DEAD
)
399 psp
->sy
= WEAPONBOTTOM
;
400 return; // don't bring weapon back up
403 // The old weapon has been lowered off the screen,
404 // so change the weapon and start raising it
407 { // Player is dead, so keep the weapon off screen.
408 P_SetPsprite(player
, ps_weapon
, S_NULL
);
412 player
->readyweapon
= player
->pendingweapon
;
414 P_BringUpWeapon(player
);
421 void A_Raise(player_t
*player
, pspdef_t
*psp
)
425 psp
->sy
-= RAISESPEED
;
427 if (psp
->sy
> WEAPONTOP
)
432 // The weapon has been raised all the way,
433 // so change to the ready state.
435 newstate
= weaponinfo
[player
->readyweapon
].readystate
;
437 P_SetPsprite(player
, ps_weapon
, newstate
);
441 // Weapons now recoil, amount depending on the weapon. // phares
443 // The P_SetPsprite call in each of the weapon firing routines // V
444 // was moved here so the recoil could be synched with the
445 // muzzle flash, rather than the pressing of the trigger.
446 // The BFG delay caused this to be necessary.
448 static void A_FireSomething(player_t
* player
,int adder
)
450 P_SetPsprite(player
, ps_flash
,
451 weaponinfo
[player
->readyweapon
].flashstate
+adder
);
453 // killough 3/27/98: prevent recoil in no-clipping mode
454 if (!(player
->mo
->flags
& MF_NOCLIP
))
455 if (!compatibility
&& weapon_recoil
)
457 ANG180
+player
->mo
->angle
, // ^
458 2048*recoil_values
[player
->readyweapon
]); // |
465 void A_GunFlash(player_t
*player
, pspdef_t
*psp
)
468 P_SetMobjState(player
->mo
, S_PLAY_ATK2
);
470 A_FireSomething(player
,0); // phares
481 void A_Punch(player_t
*player
, pspdef_t
*psp
)
485 int t
, slope
, damage
= (P_Random(pr_punch
)%10+1)<<1;
487 if (player
->powers
[pw_strength
])
490 angle
= player
->mo
->angle
;
492 // killough 5/5/98: remove dependence on order of evaluation:
493 t
= P_Random(pr_punchangle
);
494 angle
+= (t
- P_Random(pr_punchangle
))<<18;
496 /* killough 8/2/98: make autoaiming prefer enemies */
498 (slope
= P_AimLineAttack(player
->mo
, angle
, MELEERANGE
, MF_FRIEND
),
500 slope
= P_AimLineAttack(player
->mo
, angle
, MELEERANGE
, 0);
502 P_LineAttack(player
->mo
, angle
, MELEERANGE
, slope
, damage
);
507 S_StartSound(player
->mo
, sfx_punch
);
509 // turn to face target
511 player
->mo
->angle
= R_PointToAngle2(player
->mo
->x
, player
->mo
->y
,
512 linetarget
->x
, linetarget
->y
);
519 void A_Saw(player_t
*player
, pspdef_t
*psp
)
522 int slope
, damage
= 2*(P_Random(pr_saw
)%10+1);
523 angle_t angle
= player
->mo
->angle
;
524 // killough 5/5/98: remove dependence on order of evaluation:
525 int t
= P_Random(pr_saw
);
526 angle
+= (t
- P_Random(pr_saw
))<<18;
528 /* Use meleerange + 1 so that the puff doesn't skip the flash
529 * killough 8/2/98: make autoaiming prefer enemies */
531 (slope
= P_AimLineAttack(player
->mo
, angle
, MELEERANGE
+1, MF_FRIEND
),
533 slope
= P_AimLineAttack(player
->mo
, angle
, MELEERANGE
+1, 0);
535 P_LineAttack(player
->mo
, angle
, MELEERANGE
+1, slope
, damage
);
539 S_StartSound(player
->mo
, sfx_sawful
);
543 S_StartSound(player
->mo
, sfx_sawhit
);
545 // turn to face target
546 angle
= R_PointToAngle2(player
->mo
->x
, player
->mo
->y
,
547 linetarget
->x
, linetarget
->y
);
549 if (angle
- player
->mo
->angle
> ANG180
) {
550 if (angle
- player
->mo
->angle
< (unsigned)(-ANG90
/20))
551 player
->mo
->angle
= angle
+ ANG90
/21;
553 player
->mo
->angle
-= ANG90
/20;
555 if (angle
- player
->mo
->angle
> ANG90
/20)
556 player
->mo
->angle
= angle
- ANG90
/21;
558 player
->mo
->angle
+= ANG90
/20;
561 player
->mo
->flags
|= MF_JUSTATTACKED
;
568 void A_FireMissile(player_t
*player
, pspdef_t
*psp
)
571 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
572 P_SpawnPlayerMissile(player
->mo
, MT_ROCKET
);
579 void A_FireBFG(player_t
*player
, pspdef_t
*psp
)
582 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
] -= BFGCELLS
;
583 P_SpawnPlayerMissile(player
->mo
, MT_BFG
);
589 * This function emulates Doom's Pre-Beta BFG
590 * By Lee Killough 6/6/98, 7/11/98, 7/19/98, 8/20/98
592 * This code may not be used in other mods without appropriate credit given.
593 * Code leeches will be telefragged.
596 void A_FireOldBFG(player_t
*player
, pspdef_t
*psp
)
606 void A_FirePlasma(player_t
*player
, pspdef_t
*psp
)
609 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
611 A_FireSomething(player
,P_Random(pr_plasma
)&1); // phares
612 P_SpawnPlayerMissile(player
->mo
, MT_PLASMA
);
617 // Sets a slope so a near miss is at aproximately
618 // the height of the intended target
621 static fixed_t bulletslope
;
623 static void P_BulletSlope(mobj_t
*mo
)
625 angle_t an
= mo
->angle
; // see which target is to be aimed at
627 /* killough 8/2/98: make autoaiming prefer enemies */
628 uint_64_t mask
= mbf_features
? MF_FRIEND
: 0;
632 bulletslope
= P_AimLineAttack(mo
, an
, 16*64*FRACUNIT
, mask
);
634 bulletslope
= P_AimLineAttack(mo
, an
+= 1<<26, 16*64*FRACUNIT
, mask
);
636 bulletslope
= P_AimLineAttack(mo
, an
-= 2<<26, 16*64*FRACUNIT
, mask
);
638 while (mask
&& (mask
=0, !linetarget
)); /* killough 8/2/98 */
645 void P_GunShot(mobj_t
*mo
, boolean accurate
)
647 int damage
= 5*(P_Random(pr_gunshot
)%3+1);
648 angle_t angle
= mo
->angle
;
651 { // killough 5/5/98: remove dependence on order of evaluation:
652 int t
= P_Random(pr_misfire
);
653 angle
+= (t
- P_Random(pr_misfire
))<<18;
656 P_LineAttack(mo
, angle
, MISSILERANGE
, bulletslope
, damage
);
663 void A_FirePistol(player_t
*player
, pspdef_t
*psp
)
666 S_StartSound(player
->mo
, sfx_pistol
);
668 P_SetMobjState(player
->mo
, S_PLAY_ATK2
);
669 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
671 A_FireSomething(player
,0); // phares
672 P_BulletSlope(player
->mo
);
673 P_GunShot(player
->mo
, !player
->refire
);
680 void A_FireShotgun(player_t
*player
, pspdef_t
*psp
)
685 S_StartSound(player
->mo
, sfx_shotgn
);
686 P_SetMobjState(player
->mo
, S_PLAY_ATK2
);
688 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
690 A_FireSomething(player
,0); // phares
692 P_BulletSlope(player
->mo
);
695 P_GunShot(player
->mo
, false);
702 void A_FireShotgun2(player_t
*player
, pspdef_t
*psp
)
707 S_StartSound(player
->mo
, sfx_dshtgn
);
708 P_SetMobjState(player
->mo
, S_PLAY_ATK2
);
709 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
] -= 2;
711 A_FireSomething(player
,0); // phares
713 P_BulletSlope(player
->mo
);
717 int damage
= 5*(P_Random(pr_shotgun
)%3+1);
718 angle_t angle
= player
->mo
->angle
;
719 // killough 5/5/98: remove dependence on order of evaluation:
720 int t
= P_Random(pr_shotgun
);
721 angle
+= (t
- P_Random(pr_shotgun
))<<19;
722 t
= P_Random(pr_shotgun
);
723 P_LineAttack(player
->mo
, angle
, MISSILERANGE
, bulletslope
+
724 ((t
- P_Random(pr_shotgun
))<<5), damage
);
732 void A_FireCGun(player_t
*player
, pspdef_t
*psp
)
734 if (player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
] || comp
[comp_sound
])
735 S_StartSound(player
->mo
, sfx_pistol
);
737 if (!player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
])
740 P_SetMobjState(player
->mo
, S_PLAY_ATK2
);
741 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
743 A_FireSomething(player
,psp
->state
- &states
[S_CHAIN1
]); // phares
745 P_BulletSlope(player
->mo
);
747 P_GunShot(player
->mo
, !player
->refire
);
750 void A_Light0(player_t
*player
, pspdef_t
*psp
)
753 player
->extralight
= 0;
756 void A_Light1 (player_t
*player
, pspdef_t
*psp
)
759 player
->extralight
= 1;
762 void A_Light2 (player_t
*player
, pspdef_t
*psp
)
765 player
->extralight
= 2;
770 // Spawn a BFG explosion on every monster in view
773 void A_BFGSpray(mobj_t
*mo
)
777 for (i
=0 ; i
<40 ; i
++) // offset angles from its attack angle
780 angle_t an
= mo
->angle
- ANG90
/2 + ANG90
/40*i
;
782 // mo->target is the originator (player) of the missile
784 // killough 8/2/98: make autoaiming prefer enemies
786 (P_AimLineAttack(mo
->target
, an
, 16*64*FRACUNIT
, MF_FRIEND
),
788 P_AimLineAttack(mo
->target
, an
, 16*64*FRACUNIT
, 0);
793 P_SpawnMobj(linetarget
->x
, linetarget
->y
,
794 linetarget
->z
+ (linetarget
->height
>>2), MT_EXTRABFG
);
796 for (damage
=j
=0; j
<15; j
++)
797 damage
+= (P_Random(pr_bfg
)&7) + 1;
799 P_DamageMobj(linetarget
, mo
->target
, mo
->target
, damage
);
807 void A_BFGsound(player_t
*player
, pspdef_t
*psp
)
810 S_StartSound(player
->mo
, sfx_bfg
);
815 // Called at start of level for each player.
818 void P_SetupPsprites(player_t
*player
)
822 // remove all psprites
823 for (i
=0; i
<NUMPSPRITES
; i
++)
824 player
->psprites
[i
].state
= NULL
;
827 player
->pendingweapon
= player
->readyweapon
;
828 P_BringUpWeapon(player
);
833 // Called every tic by player thinking routine.
836 void P_MovePsprites(player_t
*player
)
838 pspdef_t
*psp
= player
->psprites
;
841 // a null state means not active
842 // drop tic count and possibly change state
843 // a -1 tic count never changes
845 for (i
=0; i
<NUMPSPRITES
; i
++, psp
++)
846 if (psp
->state
&& psp
->tics
!= -1 && !--psp
->tics
)
847 P_SetPsprite(player
, i
, psp
->state
->nextstate
);
849 player
->psprites
[ps_flash
].sx
= player
->psprites
[ps_weapon
].sx
;
850 player
->psprites
[ps_flash
].sy
= player
->psprites
[ps_weapon
].sy
;