1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
20 // Weapon sprite animation, weapon objects.
21 // Action functions for weapons.
23 //-----------------------------------------------------------------------------
26 rcsid
[] = "$Id: p_pspr.c,v 1.5 1997/02/03 22:45:12 b1 Exp $";
44 #define LOWERSPEED FRACUNIT*6
45 #define RAISESPEED FRACUNIT*6
47 #define WEAPONBOTTOM 128*FRACUNIT
48 #define WEAPONTOP 32*FRACUNIT
51 // plasma cells for a bfg attack
67 psp
= &player
->psprites
[position
];
73 // object removed itself
78 state
= &states
[stnum
];
80 psp
->tics
= state
->tics
; // could be 0
85 psp
->sx
= state
->misc1
<< FRACBITS
;
86 psp
->sy
= state
->misc2
<< FRACBITS
;
89 // Call action routine.
91 if (state
->action
.acp2
)
93 state
->action
.acp2(player
, psp
);
98 stnum
= psp
->state
->nextstate
;
100 } while (!psp
->tics
);
101 // an initial state of 0 could cycle through
112 void P_CalcSwing (player_t
* player
)
117 // OPTIMIZE: tablify this.
118 // A LUT would allow for different modes,
119 // and add flexibility.
123 angle
= (FINEANGLES
/70*leveltime
)&FINEMASK
;
124 swingx
= FixedMul ( swing
, finesine
[angle
]);
126 angle
= (FINEANGLES
/70*leveltime
+FINEANGLES
/2)&FINEMASK
;
127 swingy
= -FixedMul ( swingx
, finesine
[angle
]);
134 // Starts bringing the pending weapon up
135 // from the bottom of the screen.
138 void P_BringUpWeapon (player_t
* player
)
142 if (player
->pendingweapon
== wp_nochange
)
143 player
->pendingweapon
= player
->readyweapon
;
145 if (player
->pendingweapon
== wp_chainsaw
)
146 S_StartSound (player
->mo
, sfx_sawup
);
148 newstate
= weaponinfo
[player
->pendingweapon
].upstate
;
150 player
->pendingweapon
= wp_nochange
;
151 player
->psprites
[ps_weapon
].sy
= WEAPONBOTTOM
;
153 P_SetPsprite (player
, ps_weapon
, newstate
);
158 // Returns true if there is enough ammo to shoot.
159 // If not, selects the next weapon to use.
161 boolean
P_CheckAmmo (player_t
* player
)
166 ammo
= weaponinfo
[player
->readyweapon
].ammo
;
168 // Minimal amount for one shot varies.
169 if (player
->readyweapon
== wp_bfg
)
171 else if (player
->readyweapon
== wp_supershotgun
)
172 count
= 2; // Double barrel.
174 count
= 1; // Regular.
176 // Some do not need ammunition anyway.
177 // Return if current ammunition sufficient.
178 if (ammo
== am_noammo
|| player
->ammo
[ammo
] >= count
)
181 // Out of ammo, pick a weapon to change to.
182 // Preferences are set here.
185 if (player
->weaponowned
[wp_plasma
]
186 && player
->ammo
[am_cell
]
187 && (gamemode
!= shareware
) )
189 player
->pendingweapon
= wp_plasma
;
191 else if (player
->weaponowned
[wp_supershotgun
]
192 && player
->ammo
[am_shell
]>2
193 && (gamemode
== commercial
) )
195 player
->pendingweapon
= wp_supershotgun
;
197 else if (player
->weaponowned
[wp_chaingun
]
198 && player
->ammo
[am_clip
])
200 player
->pendingweapon
= wp_chaingun
;
202 else if (player
->weaponowned
[wp_shotgun
]
203 && player
->ammo
[am_shell
])
205 player
->pendingweapon
= wp_shotgun
;
207 else if (player
->ammo
[am_clip
])
209 player
->pendingweapon
= wp_pistol
;
211 else if (player
->weaponowned
[wp_chainsaw
])
213 player
->pendingweapon
= wp_chainsaw
;
215 else if (player
->weaponowned
[wp_missile
]
216 && player
->ammo
[am_misl
])
218 player
->pendingweapon
= wp_missile
;
220 else if (player
->weaponowned
[wp_bfg
]
221 && player
->ammo
[am_cell
]>40
222 && (gamemode
!= shareware
) )
224 player
->pendingweapon
= wp_bfg
;
228 // If everything fails.
229 player
->pendingweapon
= wp_fist
;
232 } while (player
->pendingweapon
== wp_nochange
);
234 // Now set appropriate weapon overlay.
235 P_SetPsprite (player
,
237 weaponinfo
[player
->readyweapon
].downstate
);
246 void P_FireWeapon (player_t
* player
)
250 if (!P_CheckAmmo (player
))
253 P_SetMobjState (player
->mo
, S_PLAY_ATK1
);
254 newstate
= weaponinfo
[player
->readyweapon
].atkstate
;
255 P_SetPsprite (player
, ps_weapon
, newstate
);
256 P_NoiseAlert (player
->mo
, player
->mo
);
263 // Player died, so put the weapon away.
265 void P_DropWeapon (player_t
* player
)
267 P_SetPsprite (player
,
269 weaponinfo
[player
->readyweapon
].downstate
);
276 // The player can fire the weapon
277 // or change to another weapon at this time.
278 // Follows after getting weapon up,
279 // or after previous attack/fire sequence.
289 // get out of attack state
290 if (player
->mo
->state
== &states
[S_PLAY_ATK1
]
291 || player
->mo
->state
== &states
[S_PLAY_ATK2
] )
293 P_SetMobjState (player
->mo
, S_PLAY
);
296 if (player
->readyweapon
== wp_chainsaw
297 && psp
->state
== &states
[S_SAW
])
299 S_StartSound (player
->mo
, sfx_sawidl
);
303 // if player is dead, put the weapon away
304 if (player
->pendingweapon
!= wp_nochange
|| !player
->health
)
307 // (pending weapon should allready be validated)
308 newstate
= weaponinfo
[player
->readyweapon
].downstate
;
309 P_SetPsprite (player
, ps_weapon
, newstate
);
314 // the missile launcher and bfg do not auto fire
315 if (player
->cmd
.buttons
& BT_ATTACK
)
317 if ( !player
->attackdown
318 || (player
->readyweapon
!= wp_missile
319 && player
->readyweapon
!= wp_bfg
) )
321 player
->attackdown
= true;
322 P_FireWeapon (player
);
327 player
->attackdown
= false;
329 // bob the weapon based on movement speed
330 angle
= (128*leveltime
)&FINEMASK
;
331 psp
->sx
= FRACUNIT
+ FixedMul (player
->bob
, finecosine
[angle
]);
332 angle
&= FINEANGLES
/2-1;
333 psp
->sy
= WEAPONTOP
+ FixedMul (player
->bob
, finesine
[angle
]);
340 // The player can re-fire the weapon
341 // without lowering it entirely.
349 // (if a weaponchange is pending, let it go through instead)
350 if ( (player
->cmd
.buttons
& BT_ATTACK
)
351 && player
->pendingweapon
== wp_nochange
355 P_FireWeapon (player
);
360 P_CheckAmmo (player
);
370 P_CheckAmmo (player
);
372 if (player
->ammo
[am_shell
]<2)
373 P_SetPsprite (player
, ps_weapon
, S_DSNR1
);
381 // Lowers current weapon,
382 // and changes weapon at bottom.
389 psp
->sy
+= LOWERSPEED
;
392 if (psp
->sy
< WEAPONBOTTOM
)
396 if (player
->playerstate
== PST_DEAD
)
398 psp
->sy
= WEAPONBOTTOM
;
400 // don't bring weapon back up
404 // The old weapon has been lowered off the screen,
405 // so change the weapon and start raising it
408 // Player is dead, so keep the weapon off screen.
409 P_SetPsprite (player
, ps_weapon
, S_NULL
);
413 player
->readyweapon
= player
->pendingweapon
;
415 P_BringUpWeapon (player
);
429 psp
->sy
-= RAISESPEED
;
431 if (psp
->sy
> WEAPONTOP
)
436 // The weapon has been raised all the way,
437 // so change to the ready state.
438 newstate
= weaponinfo
[player
->readyweapon
].readystate
;
440 P_SetPsprite (player
, ps_weapon
, newstate
);
453 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
454 P_SetPsprite (player
,ps_flash
,weaponinfo
[player
->readyweapon
].flashstate
);
476 damage
= (P_Random ()%10+1)<<1;
478 if (player
->powers
[pw_strength
])
481 angle
= player
->mo
->angle
;
482 angle
+= (P_Random()-P_Random())<<18;
483 slope
= P_AimLineAttack (player
->mo
, angle
, MELEERANGE
);
484 P_LineAttack (player
->mo
, angle
, MELEERANGE
, slope
, damage
);
486 // turn to face target
489 S_StartSound (player
->mo
, sfx_punch
);
490 player
->mo
->angle
= R_PointToAngle2 (player
->mo
->x
,
510 damage
= 2*(P_Random ()%10+1);
511 angle
= player
->mo
->angle
;
512 angle
+= (P_Random()-P_Random())<<18;
514 // use meleerange + 1 se the puff doesn't skip the flash
515 slope
= P_AimLineAttack (player
->mo
, angle
, MELEERANGE
+1);
516 P_LineAttack (player
->mo
, angle
, MELEERANGE
+1, slope
, damage
);
520 S_StartSound (player
->mo
, sfx_sawful
);
523 S_StartSound (player
->mo
, sfx_sawhit
);
525 // turn to face target
526 angle
= R_PointToAngle2 (player
->mo
->x
, player
->mo
->y
,
527 linetarget
->x
, linetarget
->y
);
528 if (angle
- player
->mo
->angle
> ANG180
)
530 if (angle
- player
->mo
->angle
< -ANG90
/20)
531 player
->mo
->angle
= angle
+ ANG90
/21;
533 player
->mo
->angle
-= ANG90
/20;
537 if (angle
- player
->mo
->angle
> ANG90
/20)
538 player
->mo
->angle
= angle
- ANG90
/21;
540 player
->mo
->angle
+= ANG90
/20;
542 player
->mo
->flags
|= MF_JUSTATTACKED
;
555 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
556 P_SpawnPlayerMissile (player
->mo
, MT_ROCKET
);
568 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
] -= BFGCELLS
;
569 P_SpawnPlayerMissile (player
->mo
, MT_BFG
);
582 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
584 P_SetPsprite (player
,
586 weaponinfo
[player
->readyweapon
].flashstate
+(P_Random ()&1) );
588 P_SpawnPlayerMissile (player
->mo
, MT_PLASMA
);
595 // Sets a slope so a near miss is at aproximately
596 // the height of the intended target
601 void P_BulletSlope (mobj_t
* mo
)
605 // see which target is to be aimed at
607 bulletslope
= P_AimLineAttack (mo
, an
, 16*64*FRACUNIT
);
612 bulletslope
= P_AimLineAttack (mo
, an
, 16*64*FRACUNIT
);
616 bulletslope
= P_AimLineAttack (mo
, an
, 16*64*FRACUNIT
);
633 damage
= 5*(P_Random ()%3+1);
637 angle
+= (P_Random()-P_Random())<<18;
639 P_LineAttack (mo
, angle
, MISSILERANGE
, bulletslope
, damage
);
651 S_StartSound (player
->mo
, sfx_pistol
);
653 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
654 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
656 P_SetPsprite (player
,
658 weaponinfo
[player
->readyweapon
].flashstate
);
660 P_BulletSlope (player
->mo
);
661 P_GunShot (player
->mo
, !player
->refire
);
675 S_StartSound (player
->mo
, sfx_shotgn
);
676 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
678 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
680 P_SetPsprite (player
,
682 weaponinfo
[player
->readyweapon
].flashstate
);
684 P_BulletSlope (player
->mo
);
686 for (i
=0 ; i
<7 ; i
++)
687 P_GunShot (player
->mo
, false);
705 S_StartSound (player
->mo
, sfx_dshtgn
);
706 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
708 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]-=2;
710 P_SetPsprite (player
,
712 weaponinfo
[player
->readyweapon
].flashstate
);
714 P_BulletSlope (player
->mo
);
716 for (i
=0 ; i
<20 ; i
++)
718 damage
= 5*(P_Random ()%3+1);
719 angle
= player
->mo
->angle
;
720 angle
+= (P_Random()-P_Random())<<19;
721 P_LineAttack (player
->mo
,
724 bulletslope
+ ((P_Random()-P_Random())<<5), damage
);
737 S_StartSound (player
->mo
, sfx_pistol
);
739 if (!player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
])
742 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
743 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
745 P_SetPsprite (player
,
747 weaponinfo
[player
->readyweapon
].flashstate
749 - &states
[S_CHAIN1
] );
751 P_BulletSlope (player
->mo
);
753 P_GunShot (player
->mo
, !player
->refire
);
761 void A_Light0 (player_t
*player
, pspdef_t
*psp
)
763 player
->extralight
= 0;
766 void A_Light1 (player_t
*player
, pspdef_t
*psp
)
768 player
->extralight
= 1;
771 void A_Light2 (player_t
*player
, pspdef_t
*psp
)
773 player
->extralight
= 2;
779 // Spawn a BFG explosion on every monster in view
781 void A_BFGSpray (mobj_t
* mo
)
788 // offset angles from its attack angle
789 for (i
=0 ; i
<40 ; i
++)
791 an
= mo
->angle
- ANG90
/2 + ANG90
/40*i
;
793 // mo->target is the originator (player)
795 P_AimLineAttack (mo
->target
, an
, 16*64*FRACUNIT
);
800 P_SpawnMobj (linetarget
->x
,
802 linetarget
->z
+ (linetarget
->height
>>2),
807 damage
+= (P_Random()&7) + 1;
809 P_DamageMobj (linetarget
, mo
->target
,mo
->target
, damage
);
822 S_StartSound (player
->mo
, sfx_bfg
);
829 // Called at start of level for each player.
831 void P_SetupPsprites (player_t
* player
)
835 // remove all psprites
836 for (i
=0 ; i
<NUMPSPRITES
; i
++)
837 player
->psprites
[i
].state
= NULL
;
840 player
->pendingweapon
= player
->readyweapon
;
841 P_BringUpWeapon (player
);
849 // Called every tic by player thinking routine.
851 void P_MovePsprites (player_t
* player
)
857 psp
= &player
->psprites
[0];
858 for (i
=0 ; i
<NUMPSPRITES
; i
++, psp
++)
860 // a null state means not active
861 if ( (state
= psp
->state
) )
863 // drop tic count and possibly change state
865 // a -1 tic count never changes
870 P_SetPsprite (player
, i
, psp
->state
->nextstate
);
875 player
->psprites
[ps_flash
].sx
= player
->psprites
[ps_weapon
].sx
;
876 player
->psprites
[ps_flash
].sy
= player
->psprites
[ps_weapon
].sy
;