]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/p_pspr.c
ac97: fix buffering code, games/doom: enable sound
[plan9front.git] / sys / src / games / doom / p_pspr.c
1 // Emacs style mode select   -*- C++ -*- 
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
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.
11 //
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
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //      Weapon sprite animation, weapon objects.
21 //      Action functions for weapons.
22 //
23 //-----------------------------------------------------------------------------
24
25 static const char
26 rcsid[] = "$Id: p_pspr.c,v 1.5 1997/02/03 22:45:12 b1 Exp $";
27
28 #include "doomdef.h"
29 #include "d_event.h"
30
31
32 #include "m_random.h"
33 #include "p_local.h"
34 #include "s_sound.h"
35
36 // State.
37 #include "doomstat.h"
38
39 // Data.
40 #include "sounds.h"
41
42 #include "p_pspr.h"
43
44 #define LOWERSPEED              FRACUNIT*6
45 #define RAISESPEED              FRACUNIT*6
46
47 #define WEAPONBOTTOM    128*FRACUNIT
48 #define WEAPONTOP               32*FRACUNIT
49
50
51 // plasma cells for a bfg attack
52 #define BFGCELLS                40              
53
54
55 //
56 // P_SetPsprite
57 //
58 void
59 P_SetPsprite
60 ( player_t*     player,
61   int           position,
62   statenum_t    stnum ) 
63 {
64     pspdef_t*   psp;
65     state_t*    state;
66         
67     psp = &player->psprites[position];
68         
69     do
70     {
71         if (!stnum)
72         {
73             // object removed itself
74             psp->state = NULL;
75             break;      
76         }
77         
78         state = &states[stnum];
79         psp->state = state;
80         psp->tics = state->tics;        // could be 0
81
82         if (state->misc1)
83         {
84             // coordinate set
85             psp->sx = state->misc1 << FRACBITS;
86             psp->sy = state->misc2 << FRACBITS;
87         }
88         
89         // Call action routine.
90         // Modified handling.
91         if (state->action)
92         {
93             state->action(player, psp);
94             if (!psp->state)
95                 break;
96         }
97         
98         stnum = psp->state->nextstate;
99         
100     } while (!psp->tics);
101     // an initial state of 0 could cycle through
102 }
103
104
105
106 //
107 // P_CalcSwing
108 //      
109 fixed_t         swingx;
110 fixed_t         swingy;
111
112 void P_CalcSwing (player_t*     player)
113 {
114     fixed_t     swing;
115     int         angle;
116         
117     // OPTIMIZE: tablify this.
118     // A LUT would allow for different modes,
119     //  and add flexibility.
120
121     swing = player->bob;
122
123     angle = (FINEANGLES/70*leveltime)&FINEMASK;
124     swingx = FixedMul ( swing, finesine[angle]);
125
126     angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK;
127     swingy = -FixedMul ( swingx, finesine[angle]);
128 }
129
130
131
132 //
133 // P_BringUpWeapon
134 // Starts bringing the pending weapon up
135 // from the bottom of the screen.
136 // Uses player
137 //
138 void P_BringUpWeapon (player_t* player)
139 {
140     statenum_t  newstate;
141         
142     if (player->pendingweapon == wp_nochange)
143         player->pendingweapon = player->readyweapon;
144                 
145     if (player->pendingweapon == wp_chainsaw)
146         S_StartSound (player->mo, sfx_sawup);
147                 
148     newstate = weaponinfo[player->pendingweapon].upstate;
149
150     player->pendingweapon = wp_nochange;
151     player->psprites[ps_weapon].sy = WEAPONBOTTOM;
152
153     P_SetPsprite (player, ps_weapon, newstate);
154 }
155
156 //
157 // P_CheckAmmo
158 // Returns true if there is enough ammo to shoot.
159 // If not, selects the next weapon to use.
160 //
161 boolean P_CheckAmmo (player_t* player)
162 {
163     ammotype_t          ammo;
164     int                 count;
165
166     ammo = weaponinfo[player->readyweapon].ammo;
167
168     // Minimal amount for one shot varies.
169     if (player->readyweapon == wp_bfg)
170         count = BFGCELLS;
171     else if (player->readyweapon == wp_supershotgun)
172         count = 2;      // Double barrel.
173     else
174         count = 1;      // Regular.
175
176     // Some do not need ammunition anyway.
177     // Return if current ammunition sufficient.
178     if (ammo == am_noammo || player->ammo[ammo] >= count)
179         return true;
180                 
181     // Out of ammo, pick a weapon to change to.
182     // Preferences are set here.
183     do
184     {
185         if (player->weaponowned[wp_plasma]
186             && player->ammo[am_cell]
187             && (gamemode != shareware) )
188         {
189             player->pendingweapon = wp_plasma;
190         }
191         else if (player->weaponowned[wp_supershotgun] 
192                  && player->ammo[am_shell]>2
193                  && (gamemode == commercial) )
194         {
195             player->pendingweapon = wp_supershotgun;
196         }
197         else if (player->weaponowned[wp_chaingun]
198                  && player->ammo[am_clip])
199         {
200             player->pendingweapon = wp_chaingun;
201         }
202         else if (player->weaponowned[wp_shotgun]
203                  && player->ammo[am_shell])
204         {
205             player->pendingweapon = wp_shotgun;
206         }
207         else if (player->ammo[am_clip])
208         {
209             player->pendingweapon = wp_pistol;
210         }
211         else if (player->weaponowned[wp_chainsaw])
212         {
213             player->pendingweapon = wp_chainsaw;
214         }
215         else if (player->weaponowned[wp_missile]
216                  && player->ammo[am_misl])
217         {
218             player->pendingweapon = wp_missile;
219         }
220         else if (player->weaponowned[wp_bfg]
221                  && player->ammo[am_cell]>40
222                  && (gamemode != shareware) )
223         {
224             player->pendingweapon = wp_bfg;
225         }
226         else
227         {
228             // If everything fails.
229             player->pendingweapon = wp_fist;
230         }
231         
232     } while (player->pendingweapon == wp_nochange);
233
234     // Now set appropriate weapon overlay.
235     P_SetPsprite (player,
236                   ps_weapon,
237                   weaponinfo[player->readyweapon].downstate);
238
239     return false;       
240 }
241
242
243 //
244 // P_FireWeapon.
245 //
246 void P_FireWeapon (player_t* player)
247 {
248     statenum_t  newstate;
249         
250     if (!P_CheckAmmo (player))
251         return;
252         
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);
257 }
258
259
260
261 //
262 // P_DropWeapon
263 // Player died, so put the weapon away.
264 //
265 void P_DropWeapon (player_t* player)
266 {
267     P_SetPsprite (player,
268                   ps_weapon,
269                   weaponinfo[player->readyweapon].downstate);
270 }
271
272
273
274 //
275 // A_WeaponReady
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.
280 //
281 void
282 A_WeaponReady(void *_player, void *_psp)
283 {
284     player_t   *player = (player_t*)_player;
285     pspdef_t   *psp    = (pspdef_t*)_psp;
286     statenum_t newstate;
287     int        angle;
288     
289     // get out of attack state
290     if (player->mo->state == &states[S_PLAY_ATK1]
291         || player->mo->state == &states[S_PLAY_ATK2] )
292     {
293         P_SetMobjState (player->mo, S_PLAY);
294     }
295     
296     if (player->readyweapon == wp_chainsaw
297         && psp->state == &states[S_SAW])
298     {
299         S_StartSound (player->mo, sfx_sawidl);
300     }
301     
302     // check for change
303     //  if player is dead, put the weapon away
304     if (player->pendingweapon != wp_nochange || !player->health)
305     {
306         // change weapon
307         //  (pending weapon should allready be validated)
308         newstate = weaponinfo[player->readyweapon].downstate;
309         P_SetPsprite (player, ps_weapon, newstate);
310         return; 
311     }
312     
313     // check for fire
314     //  the missile launcher and bfg do not auto fire
315     if (player->cmd.buttons & BT_ATTACK)
316     {
317         if ( !player->attackdown
318              || (player->readyweapon != wp_missile
319                  && player->readyweapon != wp_bfg) )
320         {
321             player->attackdown = true;
322             P_FireWeapon (player);              
323             return;
324         }
325     }
326     else
327         player->attackdown = false;
328     
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]);
334 }
335
336
337
338 //
339 // A_ReFire
340 // The player can re-fire the weapon
341 // without lowering it entirely.
342 //
343 void A_ReFire(void *_player, void* /*psp*/)
344 {
345     player_t *player = (player_t*)_player;
346     
347     // check for fire
348     //  (if a weaponchange is pending, let it go through instead)
349     if ( (player->cmd.buttons & BT_ATTACK) 
350          && player->pendingweapon == wp_nochange
351          && player->health)
352     {
353         player->refire++;
354         P_FireWeapon (player);
355     }
356     else
357     {
358         player->refire = 0;
359         P_CheckAmmo (player);
360     }
361 }
362
363
364 void
365 A_CheckReload(void *_player, void * /*psp*/)
366 {
367     P_CheckAmmo ((player_t*)_player);
368 }
369
370
371
372 //
373 // A_Lower
374 // Lowers current weapon,
375 //  and changes weapon at bottom.
376 //
377 void
378 A_Lower(void *_player, void *_psp)
379 {
380     player_t *player = (player_t*)_player;
381     pspdef_t *psp    = (pspdef_t*)_psp;
382
383     psp->sy += LOWERSPEED;
384
385     // Is already down.
386     if (psp->sy < WEAPONBOTTOM )
387         return;
388
389     // Player is dead.
390     if (player->playerstate == PST_DEAD)
391     {
392         psp->sy = WEAPONBOTTOM;
393
394         // don't bring weapon back up
395         return;         
396     }
397     
398     // The old weapon has been lowered off the screen,
399     // so change the weapon and start raising it
400     if (!player->health)
401     {
402         // Player is dead, so keep the weapon off screen.
403         P_SetPsprite (player,  ps_weapon, S_NULL);
404         return; 
405     }
406         
407     player->readyweapon = player->pendingweapon; 
408
409     P_BringUpWeapon (player);
410 }
411
412
413 //
414 // A_Raise
415 //
416 void
417 A_Raise(void *_player, void *_psp)
418 {
419     player_t   *player = (player_t*)_player;
420     pspdef_t   *psp    = (pspdef_t*)_psp;
421     statenum_t newstate;
422         
423     psp->sy -= RAISESPEED;
424
425     if (psp->sy > WEAPONTOP )
426         return;
427     
428     psp->sy = WEAPONTOP;
429     
430     // The weapon has been raised all the way,
431     //  so change to the ready state.
432     newstate = weaponinfo[player->readyweapon].readystate;
433
434     P_SetPsprite (player, ps_weapon, newstate);
435 }
436
437
438
439 //
440 // A_GunFlash
441 //
442 void
443 A_GunFlash(void *_player, void * /*psp*/)
444 {
445     player_t *player = (player_t*)_player;
446     P_SetMobjState (player->mo, S_PLAY_ATK2);
447     P_SetPsprite (player,ps_flash,weaponinfo[player->readyweapon].flashstate);
448 }
449
450
451
452 //
453 // WEAPON ATTACKS
454 //
455
456
457 //
458 // A_Punch
459 //
460 void
461 A_Punch(void *_player, void* /*psp*/)
462 {
463     player_t *player = (player_t*)_player;
464     angle_t  angle;
465     int      damage;
466     int      slope;
467         
468     damage = (P_Random ()%10+1)<<1;
469
470     if (player->powers[pw_strength])    
471         damage *= 10;
472
473     angle = player->mo->angle;
474     angle += (P_Random()-P_Random())<<18;
475     slope = P_AimLineAttack (player->mo, angle, MELEERANGE);
476     P_LineAttack (player->mo, angle, MELEERANGE, slope, damage);
477
478     // turn to face target
479     if (linetarget)
480     {
481         S_StartSound (player->mo, sfx_punch);
482         player->mo->angle = R_PointToAngle2 (player->mo->x,
483                                              player->mo->y,
484                                              linetarget->x,
485                                              linetarget->y);
486     }
487 }
488
489
490 //
491 // A_Saw
492 //
493 void
494 A_Saw(void *_player, void * /*psp*/)
495 {
496     player_t *player = (player_t*)_player;
497     angle_t  angle;
498     int      damage;
499     int      slope;
500
501     damage = 2*(P_Random ()%10+1);
502     angle = player->mo->angle;
503     angle += (P_Random()-P_Random())<<18;
504     
505     // use meleerange + 1 se the puff doesn't skip the flash
506     slope = P_AimLineAttack (player->mo, angle, MELEERANGE+1);
507     P_LineAttack (player->mo, angle, MELEERANGE+1, slope, damage);
508
509     if (!linetarget)
510     {
511         S_StartSound (player->mo, sfx_sawful);
512         return;
513     }
514     S_StartSound (player->mo, sfx_sawhit);
515         
516     // turn to face target
517     angle = R_PointToAngle2 (player->mo->x, player->mo->y,
518                              linetarget->x, linetarget->y);
519     if (angle - player->mo->angle > ANG180)
520     {
521         if (angle - player->mo->angle < (unsigned int)(-ANG90/20))
522             player->mo->angle = angle + ANG90/21;
523         else
524             player->mo->angle -= ANG90/20;
525     }
526     else
527     {
528         if (angle - player->mo->angle > ANG90/20)
529             player->mo->angle = angle - ANG90/21;
530         else
531             player->mo->angle += ANG90/20;
532     }
533     player->mo->flags |= MF_JUSTATTACKED;
534 }
535
536
537
538 //
539 // A_FireMissile
540 //
541 void
542 A_FireMissile(void *_player, void* /*psp*/)
543 {
544     player_t *player = (player_t*)_player;
545     player->ammo[weaponinfo[player->readyweapon].ammo]--;
546     P_SpawnPlayerMissile (player->mo, MT_ROCKET);
547 }
548
549
550 //
551 // A_FireBFG
552 //
553 void
554 A_FireBFG(void *_player, void * /*psp*/)
555 {
556     player_t *player = (player_t*)_player;
557     player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS;
558     P_SpawnPlayerMissile (player->mo, MT_BFG);
559 }
560
561
562
563 //
564 // A_FirePlasma
565 //
566 void
567 A_FirePlasma(void *_player, void * /*psp*/)
568 {
569     player_t *player = (player_t*)_player;
570     player->ammo[weaponinfo[player->readyweapon].ammo]--;
571
572     P_SetPsprite (player,
573                   ps_flash,
574                   weaponinfo[player->readyweapon].flashstate+(P_Random ()&1) );
575
576     P_SpawnPlayerMissile (player->mo, MT_PLASMA);
577 }
578
579
580
581 //
582 // P_BulletSlope
583 // Sets a slope so a near miss is at aproximately
584 // the height of the intended target
585 //
586 fixed_t         bulletslope;
587
588
589 void P_BulletSlope (mobj_t *mo)
590 {
591     angle_t     an;
592     
593     // see which target is to be aimed at
594     an = mo->angle;
595     bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
596
597     if (!linetarget)
598     {
599         an += 1<<26;
600         bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
601         if (!linetarget)
602         {
603             an -= 2<<26;
604             bulletslope = P_AimLineAttack (mo, an, 16*64*FRACUNIT);
605         }
606     }
607 }
608
609
610 //
611 // P_GunShot
612 //
613 void
614 P_GunShot
615 ( mobj_t*       mo,
616   boolean       accurate )
617 {
618     angle_t     angle;
619     int         damage;
620         
621     damage = 5*(P_Random ()%3+1);
622     angle = mo->angle;
623
624     if (!accurate)
625         angle += (P_Random()-P_Random())<<18;
626
627     P_LineAttack (mo, angle, MISSILERANGE, bulletslope, damage);
628 }
629
630
631 //
632 // A_FirePistol
633 //
634 void
635 A_FirePistol(void *_player, void* /*psp*/)
636 {
637     player_t *player = (player_t*)_player;
638
639     S_StartSound (player->mo, sfx_pistol);
640
641     P_SetMobjState (player->mo, S_PLAY_ATK2);
642     player->ammo[weaponinfo[player->readyweapon].ammo]--;
643
644     P_SetPsprite (player,
645                   ps_flash,
646                   weaponinfo[player->readyweapon].flashstate);
647
648     P_BulletSlope (player->mo);
649     P_GunShot (player->mo, !player->refire);
650 }
651
652
653 //
654 // A_FireShotgun
655 //
656 void
657 A_FireShotgun(void *_player, void* /*psp*/)
658 {
659     player_t *player = (player_t*)_player;
660     int      i;
661         
662     S_StartSound (player->mo, sfx_shotgn);
663     P_SetMobjState (player->mo, S_PLAY_ATK2);
664
665     player->ammo[weaponinfo[player->readyweapon].ammo]--;
666
667     P_SetPsprite (player,
668                   ps_flash,
669                   weaponinfo[player->readyweapon].flashstate);
670
671     P_BulletSlope (player->mo);
672         
673     for (i=0 ; i<7 ; i++)
674         P_GunShot (player->mo, false);
675 }
676
677
678
679 //
680 // A_FireShotgun2
681 //
682 void
683 A_FireShotgun2(void *_player, void* /*psp*/)
684 {
685     player_t *player = (player_t*)_player;
686     int      i;
687     angle_t  angle;
688     int      damage;
689                 
690         
691     S_StartSound (player->mo, sfx_dshtgn);
692     P_SetMobjState (player->mo, S_PLAY_ATK2);
693
694     player->ammo[weaponinfo[player->readyweapon].ammo]-=2;
695
696     P_SetPsprite (player,
697                   ps_flash,
698                   weaponinfo[player->readyweapon].flashstate);
699
700     P_BulletSlope (player->mo);
701         
702     for (i=0 ; i<20 ; i++)
703     {
704         damage = 5*(P_Random ()%3+1);
705         angle = player->mo->angle;
706         angle += (P_Random()-P_Random())<<19;
707         P_LineAttack (player->mo,
708                       angle,
709                       MISSILERANGE,
710                       bulletslope + ((P_Random()-P_Random())<<5), damage);
711     }
712 }
713
714
715 //
716 // A_FireCGun
717 //
718 void
719 A_FireCGun(void *_player, void *_psp)
720 {
721     player_t *player = (player_t*)_player;
722     pspdef_t *psp    = (pspdef_t*)_psp;
723
724     S_StartSound (player->mo, sfx_pistol);
725
726     if (!player->ammo[weaponinfo[player->readyweapon].ammo])
727         return;
728                 
729     P_SetMobjState (player->mo, S_PLAY_ATK2);
730     player->ammo[weaponinfo[player->readyweapon].ammo]--;
731
732     P_SetPsprite (player,
733                   ps_flash,
734                   weaponinfo[player->readyweapon].flashstate
735                   + psp->state
736                   - &states[S_CHAIN1] );
737
738     P_BulletSlope (player->mo);
739         
740     P_GunShot (player->mo, !player->refire);
741 }
742
743
744
745 //
746 // ?
747 //
748 void A_Light0 (void *_player, void* /*psp*/)
749 {
750     ((player_t*)_player)->extralight = 0;
751 }
752
753 void A_Light1 (void *_player, void* /*psp*/)
754 {
755     ((player_t*)_player)->extralight = 1;
756 }
757
758 void A_Light2 (void *_player, void* /*psp*/)
759 {
760     ((player_t*)_player)->extralight = 2;
761 }
762
763
764 //
765 // A_BFGSpray
766 // Spawn a BFG explosion on every monster in view
767 //
768 void A_BFGSpray (void *_mo, void*) 
769 {
770     mobj_t *mo = (mobj_t*)_mo;
771     int                 i;
772     int                 j;
773     int                 damage;
774     angle_t             an;
775         
776     // offset angles from its attack angle
777     for (i=0 ; i<40 ; i++)
778     {
779         an = mo->angle - ANG90/2 + ANG90/40*i;
780
781         // mo->target is the originator (player)
782         //  of the missile
783         P_AimLineAttack (mo->target, an, 16*64*FRACUNIT);
784
785         if (!linetarget)
786             continue;
787
788         P_SpawnMobj (linetarget->x,
789                      linetarget->y,
790                      linetarget->z + (linetarget->height>>2),
791                      MT_EXTRABFG);
792         
793         damage = 0;
794         for (j=0;j<15;j++)
795             damage += (P_Random()&7) + 1;
796
797         P_DamageMobj (linetarget, mo->target,mo->target, damage);
798     }
799 }
800
801
802 //
803 // A_BFGsound
804 //
805 void
806 A_BFGsound(void *_player, void * /*psp*/)
807 {
808     S_StartSound (((player_t*)_player)->mo, sfx_bfg);
809 }
810
811
812
813 //
814 // P_SetupPsprites
815 // Called at start of level for each player.
816 //
817 void P_SetupPsprites (player_t* player) 
818 {
819     int i;
820         
821     // remove all psprites
822     for (i=0 ; i<NUMPSPRITES ; i++)
823         player->psprites[i].state = NULL;
824                 
825     // spawn the gun
826     player->pendingweapon = player->readyweapon;
827     P_BringUpWeapon (player);
828 }
829
830
831
832
833 //
834 // P_MovePsprites
835 // Called every tic by player thinking routine.
836 //
837 void P_MovePsprites (player_t* player) 
838 {
839     int         i;
840     pspdef_t*   psp;
841         
842     psp = &player->psprites[0];
843     for (i=0 ; i<NUMPSPRITES ; i++, psp++)
844     {
845         // a null state means not active
846         if ( psp->state )
847         {
848             // drop tic count and possibly change state
849
850             // a -1 tic count never changes
851             if (psp->tics != -1)        
852             {
853                 psp->tics--;
854                 if (!psp->tics)
855                     P_SetPsprite (player, i, psp->state->nextstate);
856             }                           
857         }
858     }
859     
860     player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx;
861     player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy;
862 }
863
864