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
18 // Revision 1.1 2000/02/29 18:21:04 stegerg
19 // Doom port based on ADoomPPC. Read README.AROS!
23 // Weapon sprite animation, weapon objects.
24 // Action functions for weapons.
26 //-----------------------------------------------------------------------------
47 #define LOWERSPEED FRACUNIT*6
48 #define RAISESPEED FRACUNIT*6
50 #define WEAPONBOTTOM 128*FRACUNIT
51 #define WEAPONTOP 32*FRACUNIT
54 // plasma cells for a bfg attack
55 int BFGCELLS
=40; // Dehacked
70 psp
= &player
->psprites
[position
];
76 // object removed itself
81 state
= &states
[stnum
];
83 psp
->tics
= state
->tics
; // could be 0
88 psp
->sx
= state
->misc1
<< FRACBITS
;
89 psp
->sy
= state
->misc2
<< FRACBITS
;
92 // Call action routine.
94 if (state
->action
.acp2
)
96 state
->action
.acp2(player
, psp
);
101 stnum
= psp
->state
->nextstate
;
103 } while (!psp
->tics
);
104 // an initial state of 0 could cycle through
115 void P_CalcSwing (player_t
* player
)
120 // OPTIMIZE: tablify this.
121 // A LUT would allow for different modes,
122 // and add flexibility.
126 angle
= (FINEANGLES
/70*leveltime
)&FINEMASK
;
127 swingx
= FixedMul ( swing
, finesine
[angle
]);
129 angle
= (FINEANGLES
/70*leveltime
+FINEANGLES
/2)&FINEMASK
;
130 swingy
= -FixedMul ( swingx
, finesine
[angle
]);
137 // Starts bringing the pending weapon up
138 // from the bottom of the screen.
141 void P_BringUpWeapon (player_t
* player
)
145 if (player
->pendingweapon
== wp_nochange
)
146 player
->pendingweapon
= player
->readyweapon
;
148 if (player
->pendingweapon
== wp_chainsaw
)
149 S_StartSound (player
->mo
, sfx_sawup
);
151 newstate
= weaponinfo
[player
->pendingweapon
].upstate
;
153 player
->pendingweapon
= wp_nochange
;
154 player
->psprites
[ps_weapon
].sy
= WEAPONBOTTOM
;
156 P_SetPsprite (player
, ps_weapon
, newstate
);
161 // Returns true if there is enough ammo to shoot.
162 // If not, selects the next weapon to use.
164 boolean
P_CheckAmmo (player_t
* player
)
169 ammo
= weaponinfo
[player
->readyweapon
].ammo
;
171 // Minimal amount for one shot varies.
172 if (player
->readyweapon
== wp_bfg
)
174 else if (player
->readyweapon
== wp_supershotgun
)
175 count
= 2; // Double barrel.
177 count
= 1; // Regular.
179 // Some do not need ammunition anyway.
180 // Return if current ammunition sufficient.
181 if (ammo
== am_noammo
|| player
->ammo
[ammo
] >= count
)
184 // Out of ammo, pick a weapon to change to.
185 // Preferences are set here.
188 if (player
->weaponowned
[wp_plasma
]
189 && player
->ammo
[am_cell
]
190 && (gamemode
!= shareware
) )
192 player
->pendingweapon
= wp_plasma
;
194 else if (player
->weaponowned
[wp_supershotgun
]
195 && player
->ammo
[am_shell
]>2
196 && (gamemode
== commercial
) )
198 player
->pendingweapon
= wp_supershotgun
;
200 else if (player
->weaponowned
[wp_chaingun
]
201 && player
->ammo
[am_clip
])
203 player
->pendingweapon
= wp_chaingun
;
205 else if (player
->weaponowned
[wp_shotgun
]
206 && player
->ammo
[am_shell
])
208 player
->pendingweapon
= wp_shotgun
;
210 else if (player
->ammo
[am_clip
])
212 player
->pendingweapon
= wp_pistol
;
214 else if (player
->weaponowned
[wp_chainsaw
])
216 player
->pendingweapon
= wp_chainsaw
;
218 else if (player
->weaponowned
[wp_missile
]
219 && player
->ammo
[am_misl
])
221 player
->pendingweapon
= wp_missile
;
223 else if (player
->weaponowned
[wp_bfg
]
224 && player
->ammo
[am_cell
]>40
225 && (gamemode
!= shareware
) )
227 player
->pendingweapon
= wp_bfg
;
231 // If everything fails.
232 player
->pendingweapon
= wp_fist
;
235 } while (player
->pendingweapon
== wp_nochange
);
237 // Now set appropriate weapon overlay.
238 P_SetPsprite (player
,
240 weaponinfo
[player
->readyweapon
].downstate
);
249 void P_FireWeapon (player_t
* player
)
253 if (!P_CheckAmmo (player
))
256 P_SetMobjState (player
->mo
, S_PLAY_ATK1
);
257 newstate
= weaponinfo
[player
->readyweapon
].atkstate
;
258 P_SetPsprite (player
, ps_weapon
, newstate
);
259 P_NoiseAlert (player
->mo
, player
->mo
);
266 // Player died, so put the weapon away.
268 void P_DropWeapon (player_t
* player
)
270 P_SetPsprite (player
,
272 weaponinfo
[player
->readyweapon
].downstate
);
279 // The player can fire the weapon
280 // or change to another weapon at this time.
281 // Follows after getting weapon up,
282 // or after previous attack/fire sequence.
292 // get out of attack state
293 if (player
->mo
->state
== &states
[S_PLAY_ATK1
]
294 || player
->mo
->state
== &states
[S_PLAY_ATK2
] )
296 P_SetMobjState (player
->mo
, S_PLAY
);
299 if (player
->readyweapon
== wp_chainsaw
300 && psp
->state
== &states
[S_SAW
])
302 S_StartSound (player
->mo
, sfx_sawidl
);
306 // if player is dead, put the weapon away
307 if (player
->pendingweapon
!= wp_nochange
|| !player
->health
)
310 // (pending weapon should allready be validated)
311 newstate
= weaponinfo
[player
->readyweapon
].downstate
;
312 P_SetPsprite (player
, ps_weapon
, newstate
);
317 // the missile launcher and bfg do not auto fire
318 if (player
->cmd
.buttons
& BT_ATTACK
)
320 if ( !player
->attackdown
321 || (player
->readyweapon
!= wp_missile
322 && player
->readyweapon
!= wp_bfg
) )
324 player
->attackdown
= true;
325 P_FireWeapon (player
);
330 player
->attackdown
= false;
332 // bob the weapon based on movement speed
333 angle
= (128*leveltime
)&FINEMASK
;
334 psp
->sx
= FRACUNIT
+ FixedMul (player
->bob
, finecosine
[angle
]);
335 angle
&= FINEANGLES
/2-1;
336 psp
->sy
= WEAPONTOP
+ FixedMul (player
->bob
, finesine
[angle
]);
343 // The player can re-fire the weapon
344 // without lowering it entirely.
352 // (if a weaponchange is pending, let it go through instead)
353 if ( (player
->cmd
.buttons
& BT_ATTACK
)
354 && player
->pendingweapon
== wp_nochange
358 P_FireWeapon (player
);
363 P_CheckAmmo (player
);
373 P_CheckAmmo (player
);
375 if (player
->ammo
[am_shell
]<2)
376 P_SetPsprite (player
, ps_weapon
, S_DSNR1
);
384 // Lowers current weapon,
385 // and changes weapon at bottom.
392 psp
->sy
+= LOWERSPEED
;
395 if (psp
->sy
< WEAPONBOTTOM
)
399 if (player
->playerstate
== PST_DEAD
)
401 psp
->sy
= WEAPONBOTTOM
;
403 // don't bring weapon back up
407 // The old weapon has been lowered off the screen,
408 // so change the weapon and start raising it
411 // Player is dead, so keep the weapon off screen.
412 P_SetPsprite (player
, ps_weapon
, S_NULL
);
416 player
->readyweapon
= player
->pendingweapon
;
418 P_BringUpWeapon (player
);
432 psp
->sy
-= RAISESPEED
;
434 if (psp
->sy
> WEAPONTOP
)
439 // The weapon has been raised all the way,
440 // so change to the ready state.
441 newstate
= weaponinfo
[player
->readyweapon
].readystate
;
443 P_SetPsprite (player
, ps_weapon
, newstate
);
456 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
457 P_SetPsprite (player
,ps_flash
,weaponinfo
[player
->readyweapon
].flashstate
);
479 damage
= (P_Random ()%10+1)<<1;
481 if (player
->powers
[pw_strength
])
484 angle
= player
->mo
->angle
;
485 angle
+= (P_Random()-P_Random())<<18;
486 slope
= P_AimLineAttack (player
->mo
, angle
, MELEERANGE
);
487 P_LineAttack (player
->mo
, angle
, MELEERANGE
, slope
, damage
);
489 // turn to face target
492 S_StartSound (player
->mo
, sfx_punch
);
493 player
->mo
->angle
= R_PointToAngle2 (player
->mo
->x
,
513 damage
= 2*(P_Random ()%10+1);
514 angle
= player
->mo
->angle
;
515 angle
+= (P_Random()-P_Random())<<18;
517 // use meleerange + 1 se the puff doesn't skip the flash
518 slope
= P_AimLineAttack (player
->mo
, angle
, MELEERANGE
+1);
519 P_LineAttack (player
->mo
, angle
, MELEERANGE
+1, slope
, damage
);
523 S_StartSound (player
->mo
, sfx_sawful
);
526 S_StartSound (player
->mo
, sfx_sawhit
);
528 // turn to face target
529 angle
= R_PointToAngle2 (player
->mo
->x
, player
->mo
->y
,
530 linetarget
->x
, linetarget
->y
);
531 if (angle
- player
->mo
->angle
> ANG180
)
533 if (angle
- player
->mo
->angle
< -ANG90
/20)
534 player
->mo
->angle
= angle
+ ANG90
/21;
536 player
->mo
->angle
-= ANG90
/20;
540 if (angle
- player
->mo
->angle
> ANG90
/20)
541 player
->mo
->angle
= angle
- ANG90
/21;
543 player
->mo
->angle
+= ANG90
/20;
545 player
->mo
->flags
|= MF_JUSTATTACKED
;
558 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
559 P_SpawnPlayerMissile (player
->mo
, MT_ROCKET
);
571 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
] -= BFGCELLS
;
572 P_SpawnPlayerMissile (player
->mo
, MT_BFG
);
585 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
587 P_SetPsprite (player
,
589 weaponinfo
[player
->readyweapon
].flashstate
+(P_Random ()&1) );
591 P_SpawnPlayerMissile (player
->mo
, MT_PLASMA
);
598 // Sets a slope so a near miss is at aproximately
599 // the height of the intended target
604 void P_BulletSlope (mobj_t
* mo
)
608 // see which target is to be aimed at
610 bulletslope
= P_AimLineAttack (mo
, an
, 16*64*FRACUNIT
);
615 bulletslope
= P_AimLineAttack (mo
, an
, 16*64*FRACUNIT
);
619 bulletslope
= P_AimLineAttack (mo
, an
, 16*64*FRACUNIT
);
636 damage
= 5*(P_Random ()%3+1);
640 angle
+= (P_Random()-P_Random())<<18;
642 P_LineAttack (mo
, angle
, MISSILERANGE
, bulletslope
, damage
);
654 S_StartSound (player
->mo
, sfx_pistol
);
656 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
657 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
659 P_SetPsprite (player
,
661 weaponinfo
[player
->readyweapon
].flashstate
);
663 P_BulletSlope (player
->mo
);
664 P_GunShot (player
->mo
, !player
->refire
);
678 S_StartSound (player
->mo
, sfx_shotgn
);
679 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
681 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
683 P_SetPsprite (player
,
685 weaponinfo
[player
->readyweapon
].flashstate
);
687 P_BulletSlope (player
->mo
);
689 for (i
=0 ; i
<7 ; i
++)
690 P_GunShot (player
->mo
, false);
708 S_StartSound (player
->mo
, sfx_dshtgn
);
709 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
711 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]-=2;
713 P_SetPsprite (player
,
715 weaponinfo
[player
->readyweapon
].flashstate
);
717 P_BulletSlope (player
->mo
);
719 for (i
=0 ; i
<20 ; i
++)
721 damage
= 5*(P_Random ()%3+1);
722 angle
= player
->mo
->angle
;
723 angle
+= (P_Random()-P_Random())<<19;
724 P_LineAttack (player
->mo
,
727 bulletslope
+ ((P_Random()-P_Random())<<5), damage
);
740 S_StartSound (player
->mo
, sfx_pistol
);
742 if (!player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
])
745 P_SetMobjState (player
->mo
, S_PLAY_ATK2
);
746 player
->ammo
[weaponinfo
[player
->readyweapon
].ammo
]--;
748 P_SetPsprite (player
,
750 weaponinfo
[player
->readyweapon
].flashstate
752 - &states
[S_CHAIN1
] );
754 P_BulletSlope (player
->mo
);
756 P_GunShot (player
->mo
, !player
->refire
);
764 void A_Light0 (player_t
*player
, pspdef_t
*psp
)
766 player
->extralight
= 0;
769 void A_Light1 (player_t
*player
, pspdef_t
*psp
)
771 player
->extralight
= 1;
774 void A_Light2 (player_t
*player
, pspdef_t
*psp
)
776 player
->extralight
= 2;
782 // Spawn a BFG explosion on every monster in view
784 void A_BFGSpray (mobj_t
* mo
)
791 // offset angles from its attack angle
792 for (i
=0 ; i
<40 ; i
++)
794 an
= mo
->angle
- ANG90
/2 + ANG90
/40*i
;
796 // mo->target is the originator (player)
798 P_AimLineAttack (mo
->target
, an
, 16*64*FRACUNIT
);
803 P_SpawnMobj (linetarget
->x
,
805 linetarget
->z
+ (linetarget
->height
>>2),
810 damage
+= (P_Random()&7) + 1;
812 P_DamageMobj (linetarget
, mo
->target
,mo
->target
, damage
);
825 S_StartSound (player
->mo
, sfx_bfg
);
832 // Called at start of level for each player.
834 void P_SetupPsprites (player_t
* player
)
838 // remove all psprites
839 for (i
=0 ; i
<NUMPSPRITES
; i
++)
840 player
->psprites
[i
].state
= NULL
;
843 player
->pendingweapon
= player
->readyweapon
;
844 P_BringUpWeapon (player
);
852 // Called every tic by player thinking routine.
854 void P_MovePsprites (player_t
* player
)
860 psp
= &player
->psprites
[0];
861 for (i
=0 ; i
<NUMPSPRITES
; i
++, psp
++)
863 // a null state means not active
864 if ( (state
= psp
->state
) )
866 // drop tic count and possibly change state
868 // a -1 tic count never changes
873 P_SetPsprite (player
, i
, psp
->state
->nextstate
);
878 player
->psprites
[ps_flash
].sx
= player
->psprites
[ps_weapon
].sx
;
879 player
->psprites
[ps_flash
].sy
= player
->psprites
[ps_weapon
].sy
;