]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/doom/p_inter.c
bring games/swar from 1ed sources.
[plan9front.git] / sys / src / games / doom / p_inter.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 //      Handling interactions (i.e., collisions).
21 //
22 //-----------------------------------------------------------------------------
23
24
25 static const char
26 rcsid[] = "$Id: p_inter.c,v 1.4 1997/02/03 22:45:11 b1 Exp $";
27
28
29 // Data.
30 #include "doomdef.h"
31 #include "dstrings.h"
32 #include "sounds.h"
33
34 #include "doomstat.h"
35
36 #include "m_random.h"
37 #include "i_system.h"
38
39 #include "am_map.h"
40
41 #include "p_local.h"
42
43 #include "s_sound.h"
44
45 #ifdef __GNUG__
46 #pragma implementation "p_inter.h"
47 #endif
48 #include "p_inter.h"
49
50
51 #define BONUSADD        6
52
53
54
55
56 // a weapon is found with two clip loads,
57 // a big item has five clip loads
58 int     maxammo[NUMAMMO] = {200, 50, 300, 50};
59 int     clipammo[NUMAMMO] = {10, 4, 20, 1};
60
61
62 //
63 // GET STUFF
64 //
65
66 //
67 // P_GiveAmmo
68 // Num is the number of clip loads,
69 // not the individual count (0= 1/2 clip).
70 // Returns false if the ammo can't be picked up at all
71 //
72
73 boolean
74 P_GiveAmmo
75 ( player_t*     player,
76   ammotype_t    ammo,
77   int           num )
78 {
79     int         oldammo;
80         
81     if (ammo == am_noammo)
82         return false;
83                 
84     if (ammo < 0 || ammo > NUMAMMO)
85         I_Error ("P_GiveAmmo: bad type %i", ammo);
86                 
87     if ( player->ammo[ammo] == player->maxammo[ammo]  )
88         return false;
89                 
90     if (num)
91         num *= clipammo[ammo];
92     else
93         num = clipammo[ammo]/2;
94     
95     if (gameskill == sk_baby
96         || gameskill == sk_nightmare)
97     {
98         // give double ammo in trainer mode,
99         // you'll need in nightmare
100         num <<= 1;
101     }
102     
103                 
104     oldammo = player->ammo[ammo];
105     player->ammo[ammo] += num;
106
107     if (player->ammo[ammo] > player->maxammo[ammo])
108         player->ammo[ammo] = player->maxammo[ammo];
109
110     // If non zero ammo, 
111     // don't change up weapons,
112     // player was lower on purpose.
113     if (oldammo)
114         return true;    
115
116     // We were down to zero,
117     // so select a new weapon.
118     // Preferences are not user selectable.
119     switch (ammo)
120     {
121       case am_clip:
122         if (player->readyweapon == wp_fist)
123         {
124             if (player->weaponowned[wp_chaingun])
125                 player->pendingweapon = wp_chaingun;
126             else
127                 player->pendingweapon = wp_pistol;
128         }
129         break;
130         
131       case am_shell:
132         if (player->readyweapon == wp_fist
133             || player->readyweapon == wp_pistol)
134         {
135             if (player->weaponowned[wp_shotgun])
136                 player->pendingweapon = wp_shotgun;
137         }
138         break;
139         
140       case am_cell:
141         if (player->readyweapon == wp_fist
142             || player->readyweapon == wp_pistol)
143         {
144             if (player->weaponowned[wp_plasma])
145                 player->pendingweapon = wp_plasma;
146         }
147         break;
148         
149       case am_misl:
150         if (player->readyweapon == wp_fist)
151         {
152             if (player->weaponowned[wp_missile])
153                 player->pendingweapon = wp_missile;
154         }
155       default:
156         break;
157     }
158         
159     return true;
160 }
161
162
163 //
164 // P_GiveWeapon
165 // The weapon name may have a MF_DROPPED flag ored in.
166 //
167 boolean
168 P_GiveWeapon
169 ( player_t*     player,
170   weapontype_t  weapon,
171   boolean       dropped )
172 {
173     boolean     gaveammo;
174     boolean     gaveweapon;
175         
176     if (netgame
177         && (deathmatch!=2)
178          && !dropped )
179     {
180         // leave placed weapons forever on net games
181         if (player->weaponowned[weapon])
182             return false;
183
184         player->bonuscount += BONUSADD;
185         player->weaponowned[weapon] = true;
186
187         if (deathmatch)
188             P_GiveAmmo (player, weaponinfo[weapon].ammo, 5);
189         else
190             P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
191         player->pendingweapon = weapon;
192
193         if (player == &players[consoleplayer])
194             S_StartSound (NULL, sfx_wpnup);
195         return false;
196     }
197         
198     if (weaponinfo[weapon].ammo != am_noammo)
199     {
200         // give one clip with a dropped weapon,
201         // two clips with a found weapon
202         if (dropped)
203             gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1);
204         else
205             gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
206     }
207     else
208         gaveammo = false;
209         
210     if (player->weaponowned[weapon])
211         gaveweapon = false;
212     else
213     {
214         gaveweapon = true;
215         player->weaponowned[weapon] = true;
216         player->pendingweapon = weapon;
217     }
218         
219     return (gaveweapon || gaveammo);
220 }
221
222  
223
224 //
225 // P_GiveBody
226 // Returns false if the body isn't needed at all
227 //
228 boolean
229 P_GiveBody
230 ( player_t*     player,
231   int           num )
232 {
233     if (player->health >= MAXHEALTH)
234         return false;
235                 
236     player->health += num;
237     if (player->health > MAXHEALTH)
238         player->health = MAXHEALTH;
239     player->mo->health = player->health;
240         
241     return true;
242 }
243
244
245
246 //
247 // P_GiveArmor
248 // Returns false if the armor is worse
249 // than the current armor.
250 //
251 boolean
252 P_GiveArmor
253 ( player_t*     player,
254   int           armortype )
255 {
256     int         hits;
257         
258     hits = armortype*100;
259     if (player->armorpoints >= hits)
260         return false;   // don't pick up
261                 
262     player->armortype = armortype;
263     player->armorpoints = hits;
264         
265     return true;
266 }
267
268
269
270 //
271 // P_GiveCard
272 //
273 void
274 P_GiveCard
275 ( player_t*     player,
276   card_t        card )
277 {
278     if (player->cards[card])
279         return;
280     
281     player->bonuscount = BONUSADD;
282     player->cards[card] = 1;
283 }
284
285
286 //
287 // P_GivePower
288 //
289 boolean
290 P_GivePower
291 ( player_t*     player,
292   int /*powertype_t*/   power )
293 {
294     if (power == pw_invulnerability)
295     {
296         player->powers[power] = INVULNTICS;
297         return true;
298     }
299     
300     if (power == pw_invisibility)
301     {
302         player->powers[power] = INVISTICS;
303         player->mo->flags |= MF_SHADOW;
304         return true;
305     }
306     
307     if (power == pw_infrared)
308     {
309         player->powers[power] = INFRATICS;
310         return true;
311     }
312     
313     if (power == pw_ironfeet)
314     {
315         player->powers[power] = IRONTICS;
316         return true;
317     }
318     
319     if (power == pw_strength)
320     {
321         P_GiveBody (player, 100);
322         player->powers[power] = 1;
323         return true;
324     }
325         
326     if (player->powers[power])
327         return false;   // already got it
328                 
329     player->powers[power] = 1;
330     return true;
331 }
332
333
334
335 //
336 // P_TouchSpecialThing
337 //
338 void
339 P_TouchSpecialThing
340 ( mobj_t*       special,
341   mobj_t*       toucher )
342 {
343     player_t*   player;
344     int         i;
345     fixed_t     delta;
346     int         sound;
347                 
348     delta = special->z - toucher->z;
349
350     if (delta > toucher->height
351         || delta < -8*FRACUNIT)
352     {
353         // out of reach
354         return;
355     }
356     
357         
358     sound = sfx_itemup; 
359     player = toucher->player;
360
361     // Dead thing touching.
362     // Can happen with a sliding player corpse.
363     if (toucher->health <= 0)
364         return;
365
366     // Identify by sprite.
367     switch (special->sprite)
368     {
369         // armor
370       case SPR_ARM1:
371         if (!P_GiveArmor (player, 1))
372             return;
373         player->message = GOTARMOR;
374         break;
375                 
376       case SPR_ARM2:
377         if (!P_GiveArmor (player, 2))
378             return;
379         player->message = GOTMEGA;
380         break;
381         
382         // bonus items
383       case SPR_BON1:
384         player->health++;               // can go over 100%
385         if (player->health > 200)
386             player->health = 200;
387         player->mo->health = player->health;
388         player->message = GOTHTHBONUS;
389         break;
390         
391       case SPR_BON2:
392         player->armorpoints++;          // can go over 100%
393         if (player->armorpoints > 200)
394             player->armorpoints = 200;
395         if (!player->armortype)
396             player->armortype = 1;
397         player->message = GOTARMBONUS;
398         break;
399         
400       case SPR_SOUL:
401         player->health += 100;
402         if (player->health > 200)
403             player->health = 200;
404         player->mo->health = player->health;
405         player->message = GOTSUPER;
406         sound = sfx_getpow;
407         break;
408         
409       case SPR_MEGA:
410         if (gamemode != commercial)
411             return;
412         player->health = 200;
413         player->mo->health = player->health;
414         P_GiveArmor (player,2);
415         player->message = GOTMSPHERE;
416         sound = sfx_getpow;
417         break;
418         
419         // cards
420         // leave cards for everyone
421       case SPR_BKEY:
422         if (!player->cards[it_bluecard])
423             player->message = GOTBLUECARD;
424         P_GiveCard (player, it_bluecard);
425         if (!netgame)
426             break;
427         return;
428         
429       case SPR_YKEY:
430         if (!player->cards[it_yellowcard])
431             player->message = GOTYELWCARD;
432         P_GiveCard (player, it_yellowcard);
433         if (!netgame)
434             break;
435         return;
436         
437       case SPR_RKEY:
438         if (!player->cards[it_redcard])
439             player->message = GOTREDCARD;
440         P_GiveCard (player, it_redcard);
441         if (!netgame)
442             break;
443         return;
444         
445       case SPR_BSKU:
446         if (!player->cards[it_blueskull])
447             player->message = GOTBLUESKUL;
448         P_GiveCard (player, it_blueskull);
449         if (!netgame)
450             break;
451         return;
452         
453       case SPR_YSKU:
454         if (!player->cards[it_yellowskull])
455             player->message = GOTYELWSKUL;
456         P_GiveCard (player, it_yellowskull);
457         if (!netgame)
458             break;
459         return;
460         
461       case SPR_RSKU:
462         if (!player->cards[it_redskull])
463             player->message = GOTREDSKULL;
464         P_GiveCard (player, it_redskull);
465         if (!netgame)
466             break;
467         return;
468         
469         // medikits, heals
470       case SPR_STIM:
471         if (!P_GiveBody (player, 10))
472             return;
473         player->message = GOTSTIM;
474         break;
475         
476       case SPR_MEDI:
477         if (!P_GiveBody (player, 25))
478             return;
479
480         if (player->health < 50)
481             player->message = GOTMEDINEED;
482         else
483             player->message = GOTMEDIKIT;
484         break;
485
486         
487         // power ups
488       case SPR_PINV:
489         if (!P_GivePower (player, pw_invulnerability))
490             return;
491         player->message = GOTINVUL;
492         sound = sfx_getpow;
493         break;
494         
495       case SPR_PSTR:
496         if (!P_GivePower (player, pw_strength))
497             return;
498         player->message = GOTBERSERK;
499         if (player->readyweapon != wp_fist)
500             player->pendingweapon = wp_fist;
501         sound = sfx_getpow;
502         break;
503         
504       case SPR_PINS:
505         if (!P_GivePower (player, pw_invisibility))
506             return;
507         player->message = GOTINVIS;
508         sound = sfx_getpow;
509         break;
510         
511       case SPR_SUIT:
512         if (!P_GivePower (player, pw_ironfeet))
513             return;
514         player->message = GOTSUIT;
515         sound = sfx_getpow;
516         break;
517         
518       case SPR_PMAP:
519         if (!P_GivePower (player, pw_allmap))
520             return;
521         player->message = GOTMAP;
522         sound = sfx_getpow;
523         break;
524         
525       case SPR_PVIS:
526         if (!P_GivePower (player, pw_infrared))
527             return;
528         player->message = GOTVISOR;
529         sound = sfx_getpow;
530         break;
531         
532         // ammo
533       case SPR_CLIP:
534         if (special->flags & MF_DROPPED)
535         {
536             if (!P_GiveAmmo (player,am_clip,0))
537                 return;
538         }
539         else
540         {
541             if (!P_GiveAmmo (player,am_clip,1))
542                 return;
543         }
544         player->message = GOTCLIP;
545         break;
546         
547       case SPR_AMMO:
548         if (!P_GiveAmmo (player, am_clip,5))
549             return;
550         player->message = GOTCLIPBOX;
551         break;
552         
553       case SPR_ROCK:
554         if (!P_GiveAmmo (player, am_misl,1))
555             return;
556         player->message = GOTROCKET;
557         break;
558         
559       case SPR_BROK:
560         if (!P_GiveAmmo (player, am_misl,5))
561             return;
562         player->message = GOTROCKBOX;
563         break;
564         
565       case SPR_CELL:
566         if (!P_GiveAmmo (player, am_cell,1))
567             return;
568         player->message = GOTCELL;
569         break;
570         
571       case SPR_CELP:
572         if (!P_GiveAmmo (player, am_cell,5))
573             return;
574         player->message = GOTCELLBOX;
575         break;
576         
577       case SPR_SHEL:
578         if (!P_GiveAmmo (player, am_shell,1))
579             return;
580         player->message = GOTSHELLS;
581         break;
582         
583       case SPR_SBOX:
584         if (!P_GiveAmmo (player, am_shell,5))
585             return;
586         player->message = GOTSHELLBOX;
587         break;
588         
589       case SPR_BPAK:
590         if (!player->backpack)
591         {
592             for (i=0 ; i<NUMAMMO ; i++)
593                 player->maxammo[i] *= 2;
594             player->backpack = true;
595         }
596         for (i=0 ; i<NUMAMMO ; i++)
597             P_GiveAmmo (player, i, 1);
598         player->message = GOTBACKPACK;
599         break;
600         
601         // weapons
602       case SPR_BFUG:
603         if (!P_GiveWeapon (player, wp_bfg, false) )
604             return;
605         player->message = GOTBFG9000;
606         sound = sfx_wpnup;      
607         break;
608         
609       case SPR_MGUN:
610         if (!P_GiveWeapon (player, wp_chaingun, special->flags&MF_DROPPED) )
611             return;
612         player->message = GOTCHAINGUN;
613         sound = sfx_wpnup;      
614         break;
615         
616       case SPR_CSAW:
617         if (!P_GiveWeapon (player, wp_chainsaw, false) )
618             return;
619         player->message = GOTCHAINSAW;
620         sound = sfx_wpnup;      
621         break;
622         
623       case SPR_LAUN:
624         if (!P_GiveWeapon (player, wp_missile, false) )
625             return;
626         player->message = GOTLAUNCHER;
627         sound = sfx_wpnup;      
628         break;
629         
630       case SPR_PLAS:
631         if (!P_GiveWeapon (player, wp_plasma, false) )
632             return;
633         player->message = GOTPLASMA;
634         sound = sfx_wpnup;      
635         break;
636         
637       case SPR_SHOT:
638         if (!P_GiveWeapon (player, wp_shotgun, special->flags&MF_DROPPED ) )
639             return;
640         player->message = GOTSHOTGUN;
641         sound = sfx_wpnup;      
642         break;
643                 
644       case SPR_SGN2:
645         if (!P_GiveWeapon (player, wp_supershotgun, special->flags&MF_DROPPED ) )
646             return;
647         player->message = GOTSHOTGUN2;
648         sound = sfx_wpnup;      
649         break;
650                 
651       default:
652         I_Error ("P_SpecialThing: Unknown gettable thing");
653     }
654         
655     if (special->flags & MF_COUNTITEM)
656         player->itemcount++;
657     P_RemoveMobj (special);
658     player->bonuscount += BONUSADD;
659     if (player == &players[consoleplayer])
660         S_StartSound (NULL, sound);
661 }
662
663
664 //
665 // KillMobj
666 //
667 void
668 P_KillMobj
669 ( mobj_t*       source,
670   mobj_t*       target )
671 {
672     mobjtype_t  item;
673     mobj_t*     mo;
674         
675     target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);
676
677     if (target->type != MT_SKULL)
678         target->flags &= ~MF_NOGRAVITY;
679
680     target->flags |= MF_CORPSE|MF_DROPOFF;
681     target->height >>= 2;
682
683     if (source && source->player)
684     {
685         // count for intermission
686         if (target->flags & MF_COUNTKILL)
687             source->player->killcount++;        
688
689         if (target->player)
690             source->player->frags[target->player-players]++;
691     }
692     else if (!netgame && (target->flags & MF_COUNTKILL) )
693     {
694         // count all monster deaths,
695         // even those caused by other monsters
696         players[0].killcount++;
697     }
698     
699     if (target->player)
700     {
701         // count environment kills against you
702         if (!source)    
703             target->player->frags[target->player-players]++;
704                         
705         target->flags &= ~MF_SOLID;
706         target->player->playerstate = PST_DEAD;
707         P_DropWeapon (target->player);
708
709         if (target->player == &players[consoleplayer]
710             && automapactive)
711         {
712             // don't die in auto map,
713             // switch view prior to dying
714             AM_Stop ();
715         }
716         
717     }
718
719     if (target->health < -target->info->spawnhealth 
720         && target->info->xdeathstate)
721     {
722         P_SetMobjState (target, target->info->xdeathstate);
723     }
724     else
725         P_SetMobjState (target, target->info->deathstate);
726     target->tics -= P_Random()&3;
727
728     if (target->tics < 1)
729         target->tics = 1;
730                 
731     //  I_StartSound (&actor->r, actor->info->deathsound);
732
733
734     // Drop stuff.
735     // This determines the kind of object spawned
736     // during the death frame of a thing.
737     switch (target->type)
738     {
739       case MT_WOLFSS:
740       case MT_POSSESSED:
741         item = MT_CLIP;
742         break;
743         
744       case MT_SHOTGUY:
745         item = MT_SHOTGUN;
746         break;
747         
748       case MT_CHAINGUY:
749         item = MT_CHAINGUN;
750         break;
751         
752       default:
753         return;
754     }
755
756     mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item);
757     mo->flags |= MF_DROPPED;    // special versions of items
758 }
759
760
761
762
763 //
764 // P_DamageMobj
765 // Damages both enemies and players
766 // "inflictor" is the thing that caused the damage
767 //  creature or missile, can be NULL (slime, etc)
768 // "source" is the thing to target after taking damage
769 //  creature or NULL
770 // Source and inflictor are the same for melee attacks.
771 // Source can be NULL for slime, barrel explosions
772 // and other environmental stuff.
773 //
774 void
775 P_DamageMobj
776 ( mobj_t*       target,
777   mobj_t*       inflictor,
778   mobj_t*       source,
779   int           damage )
780 {
781     unsigned    ang;
782     int         saved;
783     player_t*   player;
784     fixed_t     thrust;
785     int         temp;
786         
787     if ( !(target->flags & MF_SHOOTABLE) )
788         return; // shouldn't happen...
789                 
790     if (target->health <= 0)
791         return;
792
793     if ( target->flags & MF_SKULLFLY )
794     {
795         target->momx = target->momy = target->momz = 0;
796     }
797         
798     player = target->player;
799     if (player && gameskill == sk_baby)
800         damage >>= 1;   // take half damage in trainer mode
801                 
802
803     // Some close combat weapons should not
804     // inflict thrust and push the victim out of reach,
805     // thus kick away unless using the chainsaw.
806     if (inflictor
807         && !(target->flags & MF_NOCLIP)
808         && (!source
809             || !source->player
810             || source->player->readyweapon != wp_chainsaw))
811     {
812         ang = R_PointToAngle2 ( inflictor->x,
813                                 inflictor->y,
814                                 target->x,
815                                 target->y);
816                 
817         thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
818
819         // make fall forwards sometimes
820         if ( damage < 40
821              && damage > target->health
822              && target->z - inflictor->z > 64*FRACUNIT
823              && (P_Random ()&1) )
824         {
825             ang += ANG180;
826             thrust *= 4;
827         }
828                 
829         ang >>= ANGLETOFINESHIFT;
830         target->momx += FixedMul (thrust, finecosine[ang]);
831         target->momy += FixedMul (thrust, finesine[ang]);
832     }
833     
834     // player specific
835     if (player)
836     {
837         // end of game hell hack
838         if (target->subsector->sector->special == 11
839             && damage >= target->health)
840         {
841             damage = target->health - 1;
842         }
843         
844
845         // Below certain threshold,
846         // ignore damage in GOD mode, or with INVUL power.
847         if ( damage < 1000
848              && ( (player->cheats&CF_GODMODE)
849                   || player->powers[pw_invulnerability] ) )
850         {
851             return;
852         }
853         
854         if (player->armortype)
855         {
856             if (player->armortype == 1)
857                 saved = damage/3;
858             else
859                 saved = damage/2;
860             
861             if (player->armorpoints <= saved)
862             {
863                 // armor is used up
864                 saved = player->armorpoints;
865                 player->armortype = 0;
866             }
867             player->armorpoints -= saved;
868             damage -= saved;
869         }
870         player->health -= damage;       // mirror mobj health here for Dave
871         if (player->health < 0)
872             player->health = 0;
873         
874         player->attacker = source;
875         player->damagecount += damage;  // add damage after armor / invuln
876
877         if (player->damagecount > 100)
878             player->damagecount = 100;  // teleport stomp does 10k points...
879         
880         temp = damage < 100 ? damage : 100;
881
882         if (player == &players[consoleplayer])
883             I_Tactile (40,10,40+temp*2);
884     }
885     
886     // do the damage    
887     target->health -= damage;   
888     if (target->health <= 0)
889     {
890         P_KillMobj (source, target);
891         return;
892     }
893
894     if ( (P_Random () < target->info->painchance)
895          && !(target->flags&MF_SKULLFLY) )
896     {
897         target->flags |= MF_JUSTHIT;    // fight back!
898         
899         P_SetMobjState (target, target->info->painstate);
900     }
901                         
902     target->reactiontime = 0;           // we're awake now...   
903
904     if ( (!target->threshold || target->type == MT_VILE)
905          && source && source != target
906          && source->type != MT_VILE)
907     {
908         // if not intent on another player,
909         // chase after this one
910         target->target = source;
911         target->threshold = BASETHRESHOLD;
912         if (target->state == &states[target->info->spawnstate]
913             && target->info->seestate != S_NULL)
914             P_SetMobjState (target, target->info->seestate);
915     }
916                         
917 }
918