1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_inter.c 1537 2020-06-16 05:30:37Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Portions Copyright (C) 1998-2016 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 //
20 // $Log: p_inter.c,v $
21 // Revision 1.30  2004/09/12 19:40:07  darkwolf95
22 // additional chex quest 1 support
23 //
24 // Revision 1.29  2002/11/30 18:39:58  judgecutor
25 // * Fix CR+LF problem
26 // * Fix FFW bug (player spawining istead of weapon)
27 //
28 // Revision 1.28  2002/09/27 16:40:09  tonyd
29 // First commit of acbot
30 //
31 // Revision 1.27  2002/08/24 22:42:03  hurdler
32 // Apply Robert Hogberg patches
33 //
34 // Revision 1.26  2002/07/26 15:21:36  hurdler
35 //
36 // Revision 1.25  2002/07/23 15:07:11  mysterial
37 // Messages to second player appear on his half of the screen
38 //
39 // Revision 1.24  2002/01/21 23:14:28  judgecutor
40 // Frag's Weapon Falling fixes
41 //
42 // Revision 1.23  2001/12/26 22:46:01  hurdler
43 // Revision 1.22  2001/12/26 22:42:52  hurdler
44 // Revision 1.18  2001/06/10 21:16:01  bpereira
45 // Revision 1.17  2001/05/27 13:42:47  bpereira
46 // Revision 1.16  2001/05/16 21:21:14  bpereira
47 //
48 // Revision 1.15  2001/05/14 19:02:58  metzgermeister
49 //   * Fixed floor not moving up with player on E3M1
50 //   * Fixed crash due to oversized string in screen message ... bad bug!
51 //   * Corrected some typos
52 //   * fixed sound bug in SDL
53 //
54 // Revision 1.14  2001/04/19 05:51:47  metzgermeister
55 // fixed 10 shells instead of 4 - bug
56 //
57 // Revision 1.13  2001/03/30 17:12:50  bpereira
58 // Revision 1.12  2001/02/24 13:35:20  bpereira
59 //
60 // Revision 1.11  2001/01/25 22:15:43  bpereira
61 // added heretic support
62 //
63 // Revision 1.10  2000/11/02 17:50:07  stroggonmeth
64 // Big 3Dfloors & FraggleScript commit!!
65 //
66 // Revision 1.9  2000/10/02 18:25:45  bpereira
67 // Revision 1.8  2000/10/01 10:18:17  bpereira
68 // Revision 1.7  2000/09/28 20:57:16  bpereira
69 // Revision 1.6  2000/08/31 14:30:55  bpereira
70 // Revision 1.5  2000/04/16 18:38:07  bpereira
71 //
72 // Revision 1.4  2000/04/04 00:32:46  stroggonmeth
73 // Initial Boom compatability plus few misc changes all around.
74 //
75 // Revision 1.3  2000/02/27 00:42:10  hurdler
76 // Revision 1.2  2000/02/26 00:28:42  hurdler
77 // Mostly bug fix (see borislog.txt 23-2-2000, 24-2-2000)
78 //
79 //
80 // DESCRIPTION:
81 //      Handling interactions (i.e., collisions).
82 //
83 //-----------------------------------------------------------------------------
84 
85 #include "doomincl.h"
86 #include "p_local.h"
87 #include "p_tick.h"
88   // class-list
89 #include "p_inter.h"
90 #include "g_game.h"
91 #include "g_input.h"
92   // cv_allowrocketjump
93 #include "i_system.h"
94   //I_Tactile currently has no effect
95 #include "am_map.h"
96 #include "dstrings.h"
97 #include "m_random.h"
98 #include "s_sound.h"
99 #include "r_main.h"
100 #include "st_stuff.h"
101 
102 #define BONUSADD        6
103 #define PICKUP_FLASH_TICS     10
104 
105 
106 // a weapon is found with two clip loads,
107 // a big item has five clip loads
108 int     maxammo[NUMAMMO] = {200, 50, 300, 50};
109 int     clipammo[NUMAMMO] = {10, 4, 20, 1};
110 
111 consvar_t cv_fragsweaponfalling
112   = {"fragsweaponfalling"   ,"0", CV_NETVAR, CV_YesNo};
113 
114 // added 4-2-98 (Boris) for dehacked patch
115 // (i don't like that but do you see another solution ?)
116 int     MAXHEALTH= 100;
117 
118 //--------------------------------------------------------------------------
119 //
120 // PROC P_SetMessage
121 //
122 //--------------------------------------------------------------------------
123 
124 // [WDJ] Replaces the Heretic ultimatemsg flag for IDKFA and IDDQD.
125 // That made the IDKFA and IDDQD message block most other messages,
126 // until the next player tic.
127 // But Legacy message timing is different and separate.
128 // A user control limits the messages seen based on the message level,
129 // (Off, minimal, normal, verbose, debug).
130 // This also uses the new message level as priority for competing messages
131 // that are issued in the same tic.
132 
133 // Player Message for cheats and object interactions.
134 // Subject to message level control.
135 // This does not attempt to duplicate Heretic message blocking identically,
136 // as it has finer control.
137 // msglevel:
138 //   0..9    debug
139 //   10..19  verbose
140 //   20..29  normal play
141 //   30..39  minimal msg play
142 //   40..49  major
143 //   50..59  god mode messages
144 //   60..64  mandatory (even when messages are off)
P_SetMessage(player_t * player,char * message,byte msglevel)145 void P_SetMessage(player_t *player, char *message, byte msglevel)
146 {
147     // This actually optimizes cheaper than a table.
148     switch( cv_showmessages.EV )
149     {
150      case 5: // dev level
151      case 4: // debug level
152         break;
153      case 3: // verbose level
154         if( msglevel < 10 )  return;
155         break;
156      case 2: // play level
157         if( msglevel < 20 )  return;
158         break;
159      case 1: // minimal msg play level
160         if( msglevel < 30 )  return;
161         break;
162      default: // off
163         // Does not block mandatory messages.
164         if( msglevel < 60 )  return;
165         break;
166     }
167 
168     // Check competing messages in same tic.
169     // Per player, to support splitplayer.
170     if( player->msglevel > msglevel )
171         return;
172 
173     player->msglevel = msglevel;
174     player->message = message;
175     //player->messageTics = MESSAGETICS;
176     //BorderTopRefresh = true;
177 }
178 
179 //
180 // GET STUFF
181 //
182 
183 // added by Boris : preferred weapons order
VerifFavoritWeapon(player_t * player)184 void VerifFavoritWeapon (player_t *player)
185 {
186     int newweapon;
187 
188     if (player->pendingweapon != wp_nochange)
189         return;
190 
191     newweapon = FindBestWeapon(player);
192 
193     if (newweapon != player->readyweapon)
194         player->pendingweapon = newweapon;
195 }
196 
FindBestWeapon(player_t * player)197 int FindBestWeapon(player_t *player)
198 {
199     int actualprior, actualweapon = 0, i;
200 
201     actualprior = -1;
202 
203     for (i = 0; i < NUMWEAPONS; i++)
204     {
205         // skip super shotgun for non-Doom2
206         if (gamemode!=doom2_commercial && i==wp_supershotgun)
207             continue;
208         // skip plasma-bfg in shareware
209         if (gamemode==doom_shareware && (i==wp_plasma || i==wp_bfg))
210             continue;
211 
212         if (player->weaponowned[i] &&
213             actualprior < player->favoritweapon[i] &&
214             player->ammo[player->weaponinfo[i].ammo] >= player->weaponinfo[i].ammopershoot)
215         {
216             actualweapon = i;
217             actualprior = player->favoritweapon[i];
218          }
219     }
220 
221     return actualweapon;
222 }
223 
224 static const weapontype_t GetAmmoChange[] =
225 {
226         wp_goldwand,
227         wp_crossbow,
228         wp_blaster,
229         wp_skullrod,
230         wp_phoenixrod,
231         wp_mace
232 };
233 
234 //
235 // P_GiveAmmo
236 // Num is the number of clip loads,
237 // not the individual count (0= 1/2 clip).
238 // Returns false if the ammo can't be picked up at all
239 //
240 
241 static
P_GiveAmmo(player_t * player,ammotype_t ammo,int count)242 boolean P_GiveAmmo ( player_t*     player,
243                      ammotype_t    ammo,
244                      int           count )
245 {
246     unsigned int oldammo, newammo;
247 
248     if (ammo == am_noammo)
249         return false;
250 
251     if (ammo < 0 || ammo > NUMAMMO)
252     {
253         GenPrintf(EMSG_warn, "\2P_GiveAmmo: bad type %i", ammo);
254         return false;
255     }
256 
257     oldammo = player->ammo[ammo];
258     if ( oldammo == player->maxammo[ammo]  )
259         return false;
260 /*
261     if (num)
262         num *= clipammo[ammo];
263     else
264         num = clipammo[ammo]/2;
265 */
266     if (gameskill == sk_baby
267         || gameskill == sk_nightmare)
268     {
269         if( EN_heretic )
270             count += count>>1;  // add 50% more
271         else
272         {
273             // give double ammo in trainer mode,
274             // you'll need in nightmare
275             count <<= 1;  // add 100% more
276         }
277     }
278 
279 
280     newammo = oldammo + count;
281 
282     if (newammo > player->maxammo[ammo])
283         newammo = player->maxammo[ammo];
284 
285     player->ammo[ammo] = newammo;
286 
287     if( newammo > oldammo )
288     {
289         // ammo pickup screen indication
290         if(ammo == player->weaponinfo[player->readyweapon].ammo)
291         {
292             player->ammo_pickup = PICKUP_FLASH_TICS; // ammo for current weapon
293         }
294         else
295         {
296             player->ammo_pickup += PICKUP_FLASH_TICS/2;  // some other ammo
297             if( player->ammo_pickup > 35 )
298                 player->ammo_pickup = 35;
299         }
300     }
301 
302     // If non zero ammo,
303     // don't change up weapons,
304     // player was lower on purpose.
305     if (oldammo)
306         return true;
307 
308     // We were down to zero,
309     // so select a new weapon.
310     // Preferences are not user selectable.
311 
312     // Boris hack for preferred weapons order...
313     if(!player->originalweaponswitch)
314     {
315        if(player->ammo[player->weaponinfo[player->readyweapon].ammo]
316                      < player->weaponinfo[player->readyweapon].ammopershoot)
317          VerifFavoritWeapon(player);
318        return true;
319     }
320     else //eof Boris
321     if( EN_heretic )
322     {
323         if( ( player->readyweapon == wp_staff
324            || player->readyweapon == wp_gauntlets)
325            && player->weaponowned[GetAmmoChange[ammo]])
326              player->pendingweapon = GetAmmoChange[ammo];
327     }
328     else
329     switch (ammo)
330     {
331       case am_clip:
332         if (player->readyweapon == wp_fist)
333         {
334             if (player->weaponowned[wp_chaingun])
335                 player->pendingweapon = wp_chaingun;
336             else
337                 player->pendingweapon = wp_pistol;
338         }
339         break;
340 
341       case am_shell:
342         if (player->readyweapon == wp_fist
343             || player->readyweapon == wp_pistol)
344         {
345             if (player->weaponowned[wp_shotgun])
346                 player->pendingweapon = wp_shotgun;
347         }
348         break;
349 
350       case am_cell:
351         if (player->readyweapon == wp_fist
352             || player->readyweapon == wp_pistol)
353         {
354             if (player->weaponowned[wp_plasma])
355                 player->pendingweapon = wp_plasma;
356         }
357         break;
358 
359       case am_misl:
360         if (player->readyweapon == wp_fist)
361         {
362             if (player->weaponowned[wp_missile])
363                 player->pendingweapon = wp_missile;
364         }
365       default:
366         break;
367     }
368 
369     return true;
370 }
371 
372 // ammo get with the weapon
373 int GetWeaponAmmo[NUMWEAPONS] =
374 {
375     0,  // staff       fist
376    20,  // gold wand   pistol
377     8,  // crossbow    shotgun
378    20,  // blaster     chaingun
379     2,  // skull rod   missile
380    40,  // phoenix rod plasma
381    40,  // mace        bfg
382     0,  // gauntlets   chainsaw
383     8,  // beak        supershotgun
384 };
385 
386 static int has_ammo_dropped = 0;
387 //
388 // P_GiveWeapon
389 // The weapon name may have a MF_DROPPED flag ored in.
390 //
391 static
P_GiveWeapon(player_t * player,weapontype_t weapon,boolean dropped)392 boolean P_GiveWeapon ( player_t*     player,
393                        weapontype_t  weapon,
394                        boolean       dropped )
395 {
396     boolean     gaveammo;
397     boolean     gaveweapon;
398     int         ammo_count;
399 
400     // [WDJ] Orig: (cv_deathmatch != 2)
401     if( multiplayer && weapon_persist && !dropped )
402     {
403         // Deathmatch 1 and 3, map placed weapons persist.
404         // Each player can pick up each weapon type only once.
405 
406         // leave placed weapons forever on net games
407         if (player->weaponowned[weapon])
408             return false;
409 
410         player->bonuscount += BONUSADD;
411         player->weapon_pickup = PICKUP_FLASH_TICS;
412         player->weaponowned[weapon] = true;
413 
414         if( deathmatch )
415             P_GiveAmmo (player, player->weaponinfo[weapon].ammo, 5*clipammo[player->weaponinfo[weapon].ammo]);
416         else // coop
417             P_GiveAmmo (player, player->weaponinfo[weapon].ammo, GetWeaponAmmo[weapon]);
418 
419         // Boris hack preferred weapons order...
420         if(player->originalweaponswitch
421         || player->favoritweapon[weapon] > player->favoritweapon[player->readyweapon])
422             player->pendingweapon = weapon;     // do like Doom2 original
423 
424         //added:16-01-98:changed consoleplayer to displayplayer
425         //               (hear the sounds from the viewpoint)
426         if (player == displayplayer_ptr
427             || (cv_splitscreen.EV && player == displayplayer2_ptr))  // NULL when unused
428             S_StartSound(sfx_wpnup);
429         return false;
430     }
431 
432     if (player->weaponinfo[weapon].ammo != am_noammo)
433     {
434         // give one clip with a dropped weapon,
435         // two clips with a found weapon
436         if (dropped)
437         {
438             ammo_count = has_ammo_dropped ?
439                 (has_ammo_dropped < 0 ? 0 : has_ammo_dropped) : clipammo[player->weaponinfo[weapon].ammo];
440             //gaveammo = P_GiveAmmo (player, player->weaponinfo[weapon].ammo, clipammo[player->weaponinfo[weapon].ammo]);
441         }
442         else
443         {
444             //gaveammo = P_GiveAmmo (player, player->weaponinfo[weapon].ammo, GetWeaponAmmo[weapon]);
445             ammo_count = GetWeaponAmmo[weapon];
446         }
447         gaveammo = P_GiveAmmo (player, player->weaponinfo[weapon].ammo, ammo_count);
448     }
449     else
450         gaveammo = false;
451 
452    if (player->weaponowned[weapon])
453         gaveweapon = false;
454     else
455     {
456         gaveweapon = true;
457         player->weaponowned[weapon] = true;
458         player->weapon_pickup = PICKUP_FLASH_TICS;
459         if (player->originalweaponswitch
460         || player->favoritweapon[weapon] > player->favoritweapon[player->readyweapon])
461             player->pendingweapon = weapon;    // Doom2 original stuff
462     }
463 
464     return (gaveweapon || gaveammo);
465 }
466 
467 
468 
469 //
470 // P_GiveHealth  (P_GiveBody)
471 // Returns false if the health isn't needed at all
472 //
P_GiveHealth(player_t * player,int num)473 boolean P_GiveHealth ( player_t*     player,
474                        int           num )
475 {
476     int max;
477 
478     max = MAXHEALTH;
479     if(player->chickenTics)
480         max = MAXCHICKENHEALTH;
481 
482     if (player->health >= max)
483         return false;
484 
485     player->health += num;
486     if (player->health > max)
487         player->health = max;
488     player->mo->health = player->health;
489 
490     player->health_pickup = PICKUP_FLASH_TICS;
491     return true;
492 }
493 
494 
495 
496 //
497 // P_GiveArmor
498 // Returns false if the armor is worse
499 // than the current armor.
500 //
501 static
P_GiveArmor(player_t * player,int armortype)502 boolean P_GiveArmor ( player_t*     player,
503                       int           armortype )
504 {
505     int         hits;
506 
507     hits = armortype*100;
508     if (player->armorpoints >= hits)
509         return false;   // don't pick up
510 
511     player->armortype = armortype;
512     player->armorpoints = hits;
513 
514     player->armor_pickup = PICKUP_FLASH_TICS;
515     return true;
516 }
517 
518 
519 
520 //
521 // P_GiveCard
522 //
523 static
P_GiveCard(player_t * player,card_t card)524 boolean P_GiveCard ( player_t*     player,
525                      card_t        card )
526 {
527     if (player->cards & card )
528         return false;
529 
530     player->cards |= card;
531     player->bonuscount = BONUSADD;
532     player->key_pickup = PICKUP_FLASH_TICS;
533     return true;
534 }
535 
536 
537 //
538 // P_GivePower
539 //
540 //  power : powertype index
P_GivePower(player_t * player,int power)541 boolean P_GivePower ( player_t* player, int power )
542 {
543     // Was using inventory to detect heretic and hexen rules,
544     // but these rules have nothing to do with inventory.
545     if (power == pw_invulnerability)
546     {
547         // Already have it
548         if( EN_heretic_hexen && (player->powers[power] > BLINKTHRESHOLD ) )
549             return false;
550 
551         player->powers[power] = INVULNTICS;
552         return true;
553     }
554     if(power == pw_weaponlevel2)
555     {
556         // Already have it
557         if( EN_heretic_hexen && (player->powers[power] > BLINKTHRESHOLD) )
558             return false;
559 
560         player->powers[power] = WPNLEV2TICS;
561         player->weaponinfo = wpnlev2info;
562         return true;
563     }
564     if (power == pw_invisibility)
565     {
566         // Already have it
567         if( EN_heretic_hexen && (player->powers[power] > BLINKTHRESHOLD) )
568             return false;
569 
570         player->powers[power] = INVISTICS;
571         player->mo->flags |= MF_SHADOW;
572         return true;
573     }
574     if(power == pw_flight)
575     {
576         // Already have it
577         if(player->powers[power] > BLINKTHRESHOLD)
578             return(false);
579 
580         player->powers[power] = FLIGHTTICS;
581         player->mo->flags2 |= MF2_FLY;
582         player->mo->flags |= MF_NOGRAVITY;
583         if(player->mo->z <= player->mo->floorz)
584         {
585             player->flyheight = 10; // thrust the player in the air a bit
586         }
587         return(true);
588     }
589     if (power == pw_infrared)
590     {
591         // Already have it
592         if(player->powers[power] > BLINKTHRESHOLD)
593             return(false);
594 
595         player->powers[power] = INFRATICS;
596         return true;
597     }
598 
599     if (power == pw_ironfeet)
600     {
601         player->powers[power] = IRONTICS;
602         return true;
603     }
604 
605     if (power == pw_strength)
606     {
607         P_GiveHealth (player, 100);
608         player->powers[power] = 1;
609         return true;
610     }
611 
612     if (player->powers[power])
613         return false;   // already got it
614 
615     player->powers[power] = 1;
616     return true;
617 }
618 
619 // Boris stuff : dehacked patches hack
620 int max_armor=200;
621 int green_armor_class=1;
622 int blue_armor_class=2;
623 int maxsoul=200;
624 int soul_health=100;
625 int mega_health=200;
626 // eof Boris
627 
628 //---------------------------------------------------------------------------
629 //
630 // FUNC P_GiveArtifact
631 //
632 // Returns true if artifact accepted.
633 //
634 //---------------------------------------------------------------------------
635 
P_GiveArtifact(player_t * player,artitype_t arti,mobj_t * mo)636 boolean P_GiveArtifact(player_t *player, artitype_t arti, mobj_t *mo)
637 {
638     int i;
639 
640     i = 0;
641     while(player->inventory[i].type != arti && i < player->inventorySlotNum)
642     {
643         i++;
644     }
645     if(i == player->inventorySlotNum)
646     {
647         player->inventory[i].count = 1;
648         player->inventory[i].type = arti;
649         player->inventorySlotNum++;
650     }
651     else
652     {
653         if(player->inventory[i].count >= MAXARTECONT)
654         { // Player already has 16 of this item
655             return(false);
656         }
657         player->inventory[i].count++;
658     }
659     if( player->inventory[player->inv_ptr].count == 0 )
660         player->inv_ptr = i;
661 
662     if(mo && (mo->flags&MF_COUNTITEM))
663     {
664         player->itemcount++;
665     }
666     return(true);
667 }
668 
669 
670 //---------------------------------------------------------------------------
671 //
672 // PROC P_SetDormantArtifact
673 //
674 // Removes the MF_SPECIAL flag, and initiates the artifact pickup
675 // animation.
676 //
677 //---------------------------------------------------------------------------
678 
679 static
P_SetDormantArtifact(mobj_t * arti)680 void P_SetDormantArtifact(mobj_t *arti)
681 {
682     arti->flags &= ~MF_SPECIAL;
683     if( deathmatch
684         && (arti->type != MT_ARTIINVULNERABILITY )
685         && (arti->type != MT_ARTIINVISIBILITY)   )
686     {
687         P_SetMobjState(arti, S_DORMANTARTI1);
688     }
689     else
690     { // Don't respawn
691         P_SetMobjState(arti, S_DEADARTI1);
692     }
693     S_StartObjSound(arti, sfx_artiup);
694 }
695 
696 //---------------------------------------------------------------------------
697 //
698 // PROC A_RestoreArtifact
699 //
700 //---------------------------------------------------------------------------
701 
A_RestoreArtifact(mobj_t * arti)702 void A_RestoreArtifact(mobj_t *arti)
703 {
704         arti->flags |= MF_SPECIAL;
705         P_SetMobjState(arti, arti->info->spawnstate);
706         S_StartObjSound(arti, sfx_itmbk);
707 }
708 
709 //----------------------------------------------------------------------------
710 //
711 // PROC P_HideSpecialThing
712 //
713 //----------------------------------------------------------------------------
714 
P_HideSpecialThing(mobj_t * thing)715 void P_HideSpecialThing(mobj_t *thing)
716 {
717         thing->flags &= ~MF_SPECIAL;
718         thing->flags2 |= MF2_DONTDRAW;
719         P_SetMobjState(thing, S_HIDESPECIAL1);
720 }
721 
722 //---------------------------------------------------------------------------
723 //
724 // PROC A_RestoreSpecialThing1
725 //
726 // Make a special thing visible again.
727 //
728 //---------------------------------------------------------------------------
729 
A_RestoreSpecialThing1(mobj_t * thing)730 void A_RestoreSpecialThing1(mobj_t *thing)
731 {
732         if(thing->type == MT_WMACE)
733         { // Do random mace placement
734                 P_RepositionMace(thing);
735         }
736         thing->flags2 &= ~MF2_DONTDRAW;
737         S_StartObjSound(thing, sfx_itmbk);
738 }
739 
740 //---------------------------------------------------------------------------
741 //
742 // PROC A_RestoreSpecialThing2
743 //
744 //---------------------------------------------------------------------------
745 
A_RestoreSpecialThing2(mobj_t * thing)746 void A_RestoreSpecialThing2(mobj_t *thing)
747 {
748         thing->flags |= MF_SPECIAL;
749         P_SetMobjState(thing, thing->info->spawnstate);
750 }
751 
752 
753 //----------------------------------------------------------------------------
754 //
755 // PROC A_HideThing
756 //
757 //----------------------------------------------------------------------------
758 
A_HideThing(mobj_t * actor)759 void A_HideThing(mobj_t *actor)
760 {
761         //P_UnsetThingPosition(actor);
762         actor->flags2 |= MF2_DONTDRAW;
763 }
764 
765 //----------------------------------------------------------------------------
766 //
767 // PROC A_UnHideThing
768 //
769 //----------------------------------------------------------------------------
770 
A_UnHideThing(mobj_t * actor)771 void A_UnHideThing(mobj_t *actor)
772 {
773         //P_SetThingPosition(actor);
774         actor->flags2 &= ~MF2_DONTDRAW;
775 }
776 
777 
778 //
779 // P_TouchSpecialThing
780 //
P_TouchSpecialThing(mobj_t * special,mobj_t * toucher)781 void P_TouchSpecialThing ( mobj_t*       special,
782                            mobj_t*       toucher )
783 {
784     player_t*   player;
785     char *      msg = NULL;
786     byte        msglevel = 20;  // normal play for common ammo
787     spritenum_t group = 0xFFFF;  // combined handling by spritenum
788     boolean     special_dropped;  // special item was dropped
789     int         val, i;
790     fixed_t     delta;
791     int         sound;
792 
793     delta = special->z - toucher->z;
794 
795     //SoM: 3/27/2000: For some reason, the old code allowed the player to
796     //grab items that were out of reach...
797     if (delta > toucher->height
798         || delta < -special->height)
799     {
800         // out of reach
801         return;
802     }
803 
804     // Dead thing touching.
805     // Can happen with a sliding player corpse.
806     if (toucher->health <= 0 || toucher->flags&MF_CORPSE)
807         return;
808 
809     sound = sfx_itemup;
810     player = toucher->player;
811 
812     if( player &&
813         toucher != player->mo )  // voodoo doll toucher
814     {
815         if( voodoo_mode >= VM_target )
816         {
817             // Target last player to trigger a switch or linedef.
818             if( spechit_player && spechit_player->mo )
819             {
820                 player = spechit_player;
821                 toucher = player->mo;
822             }
823         }
824         if( !player || !player->mo )
825             return; // player left or voodoo multiplayer spawn
826     }
827 
828 #ifdef PARANOIA
829     if( !player )
830         I_Error("P_TouchSpecialThing: without player\n");
831 #endif
832 
833 
834     // FWF support
835     has_ammo_dropped = special->dropped_ammo_count;
836 
837     // Avoid muiltiple conversions to boolean.
838     special_dropped = (boolean)(special->flags & MF_DROPPED);
839 
840     // Identify by sprite.
841     switch (special->sprite)
842     {
843       case SPR_SHLD: // Item_Shield1
844         // armor
845       case SPR_ARM1:  // common armor
846         if (!P_GiveArmor (player, green_armor_class))
847             return;
848         msg = GOTARMOR;
849         msglevel = 28;
850         break;
851 
852       case SPR_SHD2: // Item_Shield2
853       case SPR_ARM2:
854         if (!P_GiveArmor (player, blue_armor_class))
855             return;
856         msg = GOTMEGA;
857         msglevel = 37;
858         break;
859 
860         // bonus items
861       case SPR_BON1:  // common health inc
862         val = player->health + 1;               // can go over 100%
863         if (val > 2*MAXHEALTH)
864             val = 2*MAXHEALTH;
865         if((val > player->health) && (player->health_pickup < 35))
866             player->health_pickup += PICKUP_FLASH_TICS/2;
867         player->mo->health = player->health = val;
868         msg = GOTHTHBONUS;
869         msglevel = 22;
870         break;
871 
872       case SPR_BON2:  // common armor inc
873         val = player->armorpoints + 1;          // can go over 100%
874         if (val > max_armor)
875             val = max_armor;
876         if((val > player->armorpoints) && (player->armor_pickup < 35))
877             player->armor_pickup += PICKUP_FLASH_TICS/2;
878         player->armorpoints = val;
879         if (!player->armortype)
880             player->armortype = 1;
881         msg = GOTARMBONUS;
882         msglevel = 22;
883         break;
884 
885       case SPR_SOUL:
886         val = player->health + soul_health;
887         if (val > maxsoul)
888             val = maxsoul;
889         if((val > player->health) && (player->health_pickup < 35))
890             player->health_pickup += PICKUP_FLASH_TICS;
891         player->mo->health = player->health = val;
892         msg = GOTSUPER;
893         msglevel = 38;
894         sound = sfx_getpow;
895         break;
896 
897       case SPR_MEGA:
898         if (gamemode != doom2_commercial)
899             return;
900         player->health = mega_health;
901         player->mo->health = player->health;
902         if(player->health_pickup < 35)
903             player->health_pickup += PICKUP_FLASH_TICS;
904         P_GiveArmor (player,2);
905         msg = GOTMSPHERE;
906         msglevel = 38;
907         sound = sfx_getpow;
908         break;
909 
910         // cards
911         // leave cards for everyone
912       case SPR_BKYY: // Key_Blue
913       case SPR_BKEY:
914         group = SPR_BKEY;
915         if( P_GiveCard (player, it_bluecard) )
916         {
917             msg = GOTBLUECARD;
918             msglevel = 45;
919         }
920         break;
921 
922       case SPR_CKYY: // Key_Yellow
923       case SPR_YKEY:
924         group = SPR_BKEY;
925         if( P_GiveCard (player, it_yellowcard) )
926         {
927             msg = GOTYELWCARD;
928             msglevel = 45;
929         }
930         break;
931 
932       case SPR_AKYY: // Key_Green
933       case SPR_RKEY:
934         group = SPR_BKEY;
935         if (P_GiveCard (player, it_redcard))
936         {
937             msg = GOTREDCARD;
938             msglevel = 45;
939         }
940         break;
941 
942       case SPR_BSKU:
943         group = SPR_BKEY;
944         if (P_GiveCard (player, it_blueskull))
945         {
946             msg = GOTBLUESKUL;
947             msglevel = 45;
948         }
949         break;
950 
951       case SPR_YSKU:
952         group = SPR_BKEY;
953         if (P_GiveCard (player, it_yellowskull))
954         {
955             msg = GOTYELWSKUL;
956             msglevel = 45;
957         }
958         break;
959 
960       case SPR_RSKU:
961         group = SPR_BKEY;
962         if (P_GiveCard (player, it_redskull))
963         {
964             msg = GOTREDSKULL;
965             msglevel = 45;
966         }
967         break;
968 
969         // medikits, heals
970       case SPR_PTN1: // Item_HealingPotion
971       case SPR_STIM:
972         if (!P_GiveHealth (player, 10))
973             return;
974         msg = GOTSTIM;
975         break;
976 
977       case SPR_MEDI:
978         // [WDJ] fix medkit message
979         // DoomWiki fix would put messages first, but that would give
980         // message even when not using the medkit
981         if (!P_GiveHealth (player, 25))  // add 25 to health
982             return;
983         // if health was used, then give message
984         // use fix from prboom, thanks to Quasar
985         if (player->health < 50) // old health was < 25, before adding 25
986         {
987             msg = GOTMEDINEED;
988             msglevel = 31;
989         }
990         else
991         {
992             msg = GOTMEDIKIT;
993             msglevel = 23;
994         }
995         break;
996 
997         // heretic Artifacts :
998       case SPR_PTN2: // Arti_HealingPotion
999           if(P_GiveArtifact(player, arti_health, special))
1000           {
1001               P_SetMessage(player, TXT_ARTIHEALTH, 28);
1002               P_SetDormantArtifact(special);
1003           }
1004           return;
1005       case SPR_SOAR: // Arti_Fly
1006           if(P_GiveArtifact(player, arti_fly, special))
1007           {
1008               P_SetMessage(player, TXT_ARTIFLY, 31);
1009               P_SetDormantArtifact(special);
1010           }
1011           return;
1012       case SPR_INVU: // Arti_Invulnerability
1013           if(P_GiveArtifact(player, arti_invulnerability, special))
1014           {
1015               P_SetMessage(player, TXT_ARTIINVULNERABILITY, 31);
1016               P_SetDormantArtifact(special);
1017           }
1018           return;
1019       case SPR_PWBK: // Arti_TomeOfPower
1020           if(P_GiveArtifact(player, arti_tomeofpower, special))
1021           {
1022               P_SetMessage(player, TXT_ARTITOMEOFPOWER, 31);
1023               P_SetDormantArtifact(special);
1024           }
1025           return;
1026       case SPR_INVS: // Arti_Invisibility
1027           if(P_GiveArtifact(player, arti_invisibility, special))
1028           {
1029               P_SetMessage(player, TXT_ARTIINVISIBILITY, 31);
1030               P_SetDormantArtifact(special);
1031           }
1032           return;
1033       case SPR_EGGC: // Arti_Egg
1034           if(P_GiveArtifact(player, arti_egg, special))
1035           {
1036               P_SetMessage(player, TXT_ARTIEGG, 31);
1037               P_SetDormantArtifact(special);
1038           }
1039           return;
1040       case SPR_SPHL: // Arti_SuperHealth
1041           if(P_GiveArtifact(player, arti_superhealth, special))
1042           {
1043               P_SetMessage(player, TXT_ARTISUPERHEALTH, 31);
1044               P_SetDormantArtifact(special);
1045           }
1046           return;
1047       case SPR_TRCH: // Arti_Torch
1048           if(P_GiveArtifact(player, arti_torch, special))
1049           {
1050               P_SetMessage(player, TXT_ARTITORCH, 31);
1051               P_SetDormantArtifact(special);
1052           }
1053           return;
1054       case SPR_FBMB: // Arti_FireBomb
1055           if(P_GiveArtifact(player, arti_firebomb, special))
1056           {
1057               P_SetMessage(player, TXT_ARTIFIREBOMB, 31);
1058               P_SetDormantArtifact(special);
1059           }
1060           return;
1061       case SPR_ATLP: // Arti_Teleport
1062           if(P_GiveArtifact(player, arti_teleport, special))
1063           {
1064               P_SetMessage(player, TXT_ARTITELEPORT, 31);
1065               P_SetDormantArtifact(special);
1066           }
1067           return;
1068 
1069         // power ups
1070       case SPR_PINV:
1071         if (!P_GivePower (player, pw_invulnerability))
1072             return;
1073         msg = GOTINVUL;
1074         msglevel = 34;
1075         sound = sfx_getpow;
1076         break;
1077 
1078       case SPR_PSTR:
1079         if (!P_GivePower (player, pw_strength))
1080             return;
1081         msg = GOTBERSERK;
1082         msglevel = 34;
1083         if (player->readyweapon != wp_fist)
1084             player->pendingweapon = wp_fist;
1085         sound = sfx_getpow;
1086         break;
1087 
1088       case SPR_PINS:
1089         if (!P_GivePower (player, pw_invisibility))
1090             return;
1091         msg = GOTINVIS;
1092         msglevel = 34;
1093         sound = sfx_getpow;
1094         break;
1095 
1096       case SPR_SUIT:
1097         if (!P_GivePower (player, pw_ironfeet))
1098             return;
1099         msg = GOTSUIT;
1100         msglevel = 32;
1101         sound = sfx_getpow;
1102         break;
1103 
1104       case SPR_SPMP: // Item_SuperMap
1105       case SPR_PMAP:
1106         if (!P_GivePower (player, pw_allmap))
1107             return;
1108         msg = GOTMAP;
1109         msglevel = 31;
1110         if( EN_doom_etc )
1111             sound = sfx_getpow;
1112         break;
1113 
1114       case SPR_PVIS:
1115         if (!P_GivePower (player, pw_infrared))
1116             return;
1117         msg = GOTVISOR;
1118         msglevel = 32;
1119         sound = sfx_getpow;
1120         break;
1121 
1122         // heretic Ammo
1123       case SPR_AMG1: // Ammo_GoldWandWimpy
1124         if(!P_GiveAmmo(player, am_goldwand, special->health))
1125             return;
1126         msg = TXT_AMMOGOLDWAND1;
1127         break;
1128       case SPR_AMG2: // Ammo_GoldWandHefty
1129         if(!P_GiveAmmo(player, am_goldwand, special->health))
1130             return;
1131         msg = TXT_AMMOGOLDWAND2;
1132         break;
1133       case SPR_AMM1: // Ammo_MaceWimpy
1134         if(!P_GiveAmmo(player, am_mace, special->health))
1135             return;
1136         msg = TXT_AMMOMACE1;
1137         break;
1138       case SPR_AMM2: // Ammo_MaceHefty
1139         if(!P_GiveAmmo(player, am_mace, special->health))
1140             return;
1141         msg = TXT_AMMOMACE2;
1142         break;
1143       case SPR_AMC1: // Ammo_CrossbowWimpy
1144         if(!P_GiveAmmo(player, am_crossbow, special->health))
1145             return;
1146         msg = TXT_AMMOCROSSBOW1;
1147         break;
1148       case SPR_AMC2: // Ammo_CrossbowHefty
1149         if(!P_GiveAmmo(player, am_crossbow, special->health))
1150             return;
1151         msg = TXT_AMMOCROSSBOW2;
1152         break;
1153       case SPR_AMB1: // Ammo_BlasterWimpy
1154         if(!P_GiveAmmo(player, am_blaster, special->health))
1155             return;
1156         msg = TXT_AMMOBLASTER1;
1157         break;
1158       case SPR_AMB2: // Ammo_BlasterHefty
1159         if(!P_GiveAmmo(player, am_blaster, special->health))
1160             return;
1161         msg = TXT_AMMOBLASTER2;
1162         break;
1163       case SPR_AMS1: // Ammo_SkullRodWimpy
1164         if(!P_GiveAmmo(player, am_skullrod, special->health))
1165             return;
1166         msg = TXT_AMMOSKULLROD1;
1167         break;
1168       case SPR_AMS2: // Ammo_SkullRodHefty
1169         if(!P_GiveAmmo(player, am_skullrod, special->health))
1170             return;
1171         msg = TXT_AMMOSKULLROD2;
1172         break;
1173       case SPR_AMP1: // Ammo_PhoenixRodWimpy
1174         if(!P_GiveAmmo(player, am_phoenixrod, special->health))
1175             return;
1176         msg = TXT_AMMOPHOENIXROD1;
1177         break;
1178       case SPR_AMP2: // Ammo_PhoenixRodHefty
1179         if(!P_GiveAmmo(player, am_phoenixrod, special->health))
1180             return;
1181         msg = TXT_AMMOPHOENIXROD2;
1182         break;
1183 
1184         // ammo
1185       case SPR_CLIP:
1186         if (!P_GiveAmmo (player, am_clip,
1187                ((special_dropped)? clipammo[am_clip]/2 : clipammo[am_clip]) ))
1188         msg = GOTCLIP;
1189         break;
1190 
1191       case SPR_AMMO:
1192         if (!P_GiveAmmo (player, am_clip,5*clipammo[am_clip]))
1193             return;
1194         msg = GOTCLIPBOX;
1195         break;
1196 
1197       case SPR_ROCK:
1198         if (!P_GiveAmmo (player, am_misl,clipammo[am_misl]))
1199             return;
1200         msg = GOTROCKET;
1201         break;
1202 
1203       case SPR_BROK:
1204         if (!P_GiveAmmo (player, am_misl,5*clipammo[am_misl]))
1205             return;
1206         msg = GOTROCKBOX;
1207         break;
1208 
1209       case SPR_CELL:
1210         if (!P_GiveAmmo (player, am_cell,clipammo[am_cell]))
1211             return;
1212         msg = GOTCELL;
1213         break;
1214 
1215       case SPR_CELP:
1216         if (!P_GiveAmmo (player, am_cell,5*clipammo[am_cell]))
1217             return;
1218         msg = GOTCELLBOX;
1219         break;
1220 
1221       case SPR_SHEL:
1222         if (!P_GiveAmmo (player, am_shell,clipammo[am_shell]))
1223             return;
1224         msg = GOTSHELLS;
1225         break;
1226 
1227       case SPR_SBOX:
1228         if (!P_GiveAmmo (player, am_shell,5*clipammo[am_shell]))
1229             return;
1230         msg = GOTSHELLBOX;
1231         break;
1232 
1233       case SPR_BPAK:
1234         if (!player->backpack)
1235         {
1236             for (i=0 ; i<NUMAMMO ; i++)
1237                 player->maxammo[i] *= 2;
1238             player->backpack = true;
1239         }
1240         for (i=0 ; i<NUMAMMO ; i++)
1241             P_GiveAmmo (player, i, clipammo[i]);
1242         msg = GOTBACKPACK;
1243         msglevel = 27;
1244         break;
1245 
1246       case SPR_BAGH: // Item_BagOfHolding
1247         if(!player->backpack)
1248         {
1249             for(i = 0; i < NUMAMMO; i++)
1250                 player->maxammo[i] *= 2;
1251             player->backpack = true;
1252         }
1253         P_GiveAmmo(player, am_goldwand, AMMO_GWND_WIMPY);
1254         P_GiveAmmo(player, am_blaster, AMMO_BLSR_WIMPY);
1255         P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY);
1256         P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY);
1257         P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY);
1258         msg = TXT_ITEMBAGOFHOLDING;
1259         msglevel = 27;
1260         break;
1261 
1262         // weapons
1263       case SPR_BFUG:
1264         if (!P_GiveWeapon (player, wp_bfg, special_dropped) )
1265             return;
1266         msg = GOTBFG9000;
1267         msglevel = 38;
1268         sound = sfx_wpnup;
1269         break;
1270 
1271       case SPR_MGUN:
1272         if (!P_GiveWeapon (player, wp_chaingun, special_dropped) )
1273             return;
1274         msg = GOTCHAINGUN;
1275         msglevel = 29;
1276         sound = sfx_wpnup;
1277         break;
1278 
1279       case SPR_CSAW:
1280         if (!P_GiveWeapon (player, wp_chainsaw, false) )
1281             return;
1282         msg = GOTCHAINSAW;
1283         msglevel = 21;
1284         sound = sfx_wpnup;
1285         break;
1286 
1287       case SPR_LAUN:
1288         if (!P_GiveWeapon (player, wp_missile, special_dropped) )
1289             return;
1290         msg = GOTLAUNCHER;
1291         msglevel = 32;
1292         sound = sfx_wpnup;
1293         break;
1294 
1295       case SPR_PLAS:
1296         if (!P_GiveWeapon (player, wp_plasma, special_dropped) )
1297             return;
1298         msg = GOTPLASMA;
1299         msglevel = 32;
1300         sound = sfx_wpnup;
1301         break;
1302 
1303       case SPR_SHOT:
1304         if (!P_GiveWeapon (player, wp_shotgun, special_dropped) )
1305             return;
1306         msg = GOTSHOTGUN;
1307         msglevel = 24;
1308         sound = sfx_wpnup;
1309         break;
1310 
1311       case SPR_SGN2:
1312         if (!P_GiveWeapon (player, wp_supershotgun, special_dropped) )
1313             return;
1314         msg = GOTSHOTGUN2;
1315         msglevel = 32;
1316         sound = sfx_wpnup;
1317         break;
1318 
1319       // heretic weapons
1320       case SPR_WMCE: // Weapon_Mace
1321         if(!P_GiveWeapon(player, wp_mace, special_dropped))
1322             return;
1323         msg = TXT_WPNMACE;
1324         msglevel = 32;
1325         sound = sfx_wpnup;
1326         break;
1327       case SPR_WBOW: // Weapon_Crossbow
1328         if(!P_GiveWeapon(player, wp_crossbow, special_dropped))
1329             return;
1330         msg = TXT_WPNCROSSBOW;
1331         msglevel = 24;
1332         sound = sfx_wpnup;
1333         break;
1334       case SPR_WBLS: // Weapon_Blaster
1335         if(!P_GiveWeapon(player, wp_blaster, special_dropped))
1336             return;
1337         msg = TXT_WPNBLASTER;
1338         msglevel = 32;
1339         sound = sfx_wpnup;
1340         break;
1341       case SPR_WSKL: // Weapon_SkullRod
1342         if(!P_GiveWeapon(player, wp_skullrod, special_dropped))
1343             return;
1344         msg = TXT_WPNSKULLROD;
1345         msglevel = 36;
1346         sound = sfx_wpnup;
1347         break;
1348       case SPR_WPHX: // Weapon_PhoenixRod
1349         if(!P_GiveWeapon(player, wp_phoenixrod, special_dropped))
1350             return;
1351         msg = TXT_WPNPHOENIXROD;
1352         msglevel = 38;
1353         sound = sfx_wpnup;
1354         break;
1355       case SPR_WGNT: // Weapon_Gauntlets
1356         if(!P_GiveWeapon(player, wp_gauntlets, false))
1357             return;
1358         msg = TXT_WPNGAUNTLETS;
1359         msglevel = 21;
1360         sound = sfx_wpnup;
1361         break;
1362 
1363       default:
1364         // SoM: New gettable things with FraggleScript!
1365         //debug_Printf ("\2P_TouchSpecialThing: Unknown gettable thing\n");
1366         return;
1367     }
1368 
1369     if( msg )
1370     {
1371         P_SetMessage( player, msg, msglevel );
1372     }
1373     if( group == SPR_BKEY )  // all keys
1374     {
1375         // keycard
1376         if( EN_heretic )
1377             sound = sfx_keyup;
1378         if (multiplayer)  return;  // leave keys in multiplayer
1379     }
1380 
1381     if (special->flags & MF_COUNTITEM)
1382         player->itemcount++;
1383     P_RemoveMobj ( special );
1384     player->bonuscount += BONUSADD;
1385 
1386     //added:16-01-98:consoleplayer -> displayplayer (hear sounds from viewpoint)
1387     if (player == displayplayer_ptr
1388         || (cv_splitscreen.EV && player == displayplayer2_ptr))  // NULL when unused
1389         S_StartSound(sound);
1390 }
1391 
1392 
1393 
1394 #ifdef thatsbuggycode
1395 //
1396 //  Tell each supported thing to check again its position,
1397 //  because the 'base' thing has vanished or diminished,
1398 //  the supported things might fall.
1399 //
1400 //added:28-02-98:
P_CheckSupportThings(mobj_t * mobj)1401 void P_CheckSupportThings (mobj_t* mobj)
1402 {
1403     fixed_t   supportz = mobj->z + mobj->height;
1404 
1405     while ((mobj = mobj->supportthings))
1406     {
1407         // only for things above support thing
1408         if (mobj->z > supportz)
1409             mobj->eflags |= MF_CHECKPOS;
1410     }
1411 }
1412 
1413 
1414 //
1415 //  If a thing moves and supportthings,
1416 //  move the supported things along.
1417 //
1418 //added:28-02-98:
P_MoveSupportThings(mobj_t * mobj,fixed_t xmove,fixed_t ymove,fixed_t zmove)1419 void P_MoveSupportThings (mobj_t* mobj, fixed_t xmove, fixed_t ymove, fixed_t zmove)
1420 {
1421     fixed_t   supportz = mobj->z + mobj->height;
1422     mobj_t    *mo = mobj->supportthings;
1423 
1424     while (mo)
1425     {
1426         //added:28-02-98:debug
1427         if (mo==mobj)
1428         {
1429             mobj->supportthings = NULL;
1430             break;
1431         }
1432 
1433         // only for things above support thing
1434         if (mobj->z > supportz)
1435         {
1436             mobj->eflags |= MF_CHECKPOS;
1437             mobj->momx += xmove;
1438             mobj->momy += ymove;
1439             mobj->momz += zmove;
1440         }
1441 
1442         mo = mo->supportthings;
1443     }
1444 }
1445 
1446 
1447 //
1448 //  Link a thing to it's 'base' (supporting) thing.
1449 //  When the supporting thing will move or change size,
1450 //  the supported will then be aware.
1451 //
1452 //added:28-02-98:
P_LinkFloorThing(mobj_t * mobj)1453 void P_LinkFloorThing(mobj_t*   mobj)
1454 {
1455     mobj_t*     mo;
1456     mobj_t*     nmo;
1457 
1458     // no supporting thing
1459     if (!(mo = mobj->floorthing))
1460         return;
1461 
1462     // link mobj 'above' the lower mobjs, so that lower supporting
1463     // mobjs act upon this mobj
1464     while ( (nmo = mo->supportthings) &&
1465             (nmo->z<=mobj->z) )
1466     {
1467         // dont link multiple times
1468         if (nmo==mobj)
1469             return;
1470 
1471         mo = nmo;
1472     }
1473     mo->supportthings = mobj;
1474     mobj->supportthings = nmo;
1475 }
1476 
1477 
1478 //
1479 //  Unlink a thing from it's support,
1480 //  when it's 'floorthing' has changed,
1481 //  before linking with the new 'floorthing'.
1482 //
1483 //added:28-02-98:
P_UnlinkFloorThing(mobj_t * mobj)1484 void P_UnlinkFloorThing(mobj_t*   mobj)
1485 {
1486     mobj_t*     mo;
1487 
1488     if (!(mo = mobj->floorthing))      // just to be sure (may happen)
1489        return;
1490 
1491     while (mo->supportthings)
1492     {
1493         if (mo->supportthings == mobj)
1494         {
1495             mo->supportthings = NULL;
1496             break;
1497         }
1498         mo = mo->supportthings;
1499     }
1500 }
1501 #endif
1502 
1503 
1504 #define BUFFSIZE  512
1505 // Death messages relating to the target (dying) player
1506 //
1507 static
P_DeathMessages(mobj_t * target,mobj_t * inflictor,mobj_t * source)1508 void P_DeathMessages ( mobj_t*       target,
1509                        mobj_t*       inflictor,
1510                        mobj_t*       source )
1511 {
1512     char txt[BUFFSIZE+1];
1513     int     w;
1514     char    *str;
1515 
1516     if (!target || !target->player)
1517         return;
1518 
1519     if (target->player->mo != target )  // voodoo doll died
1520         return;
1521 
1522     if (source && source->player)
1523     {
1524         if (source->player==target->player)
1525         {
1526             str = text[DEATHMSG_SUICIDE];
1527             snprintf(txt, BUFFSIZE, str, player_names[target->player - players]);
1528             txt[BUFFSIZE-1] = 0;
1529             GenPrintf(EMSG_playmsg, txt);
1530             if( cv_splitscreen.EV )
1531                 GenPrintf(EMSG_playmsg2, txt);
1532         }
1533         else
1534         {
1535             if (target->health < -9000) // telefrag !
1536                 str = text[DEATHMSG_TELEFRAG];
1537             else
1538             {
1539                 w = source->player->readyweapon;
1540                 if( inflictor )
1541                 {
1542                     switch(inflictor->type) {
1543                     case MT_ROCKET   : w = wp_missile; break;
1544                     case MT_PLASMA   : w = wp_plasma;  break;
1545                     case MT_EXTRABFG :
1546                     case MT_BFG      : w = wp_bfg;     break;
1547                     default : break;
1548                     }
1549                 }
1550 
1551                 switch(w)
1552                 {
1553                 case wp_fist:
1554                     str = text[DEATHMSG_FIST];
1555                     break;
1556                 case wp_pistol:
1557                     str = text[DEATHMSG_GUN];
1558                     break;
1559                 case wp_shotgun:
1560                     str = text[DEATHMSG_SHOTGUN];
1561                     break;
1562                 case wp_chaingun:
1563                     str = text[DEATHMSG_MACHGUN];
1564                     break;
1565                 case wp_missile:
1566                     str = text[DEATHMSG_ROCKET];
1567                     if (target->health < -target->info->spawnhealth &&
1568                         target->info->xdeathstate)
1569                         str = text[DEATHMSG_GIBROCKET];
1570                     break;
1571                 case wp_plasma:
1572                     str = text[DEATHMSG_PLASMA];
1573                     break;
1574                 case wp_bfg:
1575                     str = text[DEATHMSG_BFGBALL];
1576                     break;
1577                 case wp_chainsaw:
1578                     str = text[DEATHMSG_CHAINSAW];
1579                     break;
1580                 case wp_supershotgun:
1581                     str = text[DEATHMSG_SUPSHOTGUN];
1582                     break;
1583                 default:
1584                     str = text[DEATHMSG_PLAYUNKNOW];
1585                     break;
1586                 }
1587             }
1588 
1589             snprintf(txt, BUFFSIZE, str,
1590                      player_names[target->player - players],
1591                      player_names[source->player - players]);
1592             txt[BUFFSIZE-1] = 0;
1593             GenPrintf(EMSG_playmsg, txt);
1594             if( cv_splitscreen.EV )
1595                 GenPrintf(EMSG_playmsg2, txt);
1596         }
1597     }
1598     else
1599     {
1600         if (!source)
1601         {
1602             // environment kills
1603             w = target->player->specialsector;      //see p_spec.c
1604 
1605             if (w==5)
1606                 str = text[DEATHMSG_HELLSLIME];
1607             else if (w==7)
1608                 str = text[DEATHMSG_NUKE];
1609             else if (w==16 || w==4)
1610                 str = text[DEATHMSG_SUPHELLSLIME];
1611             else
1612                 str = text[DEATHMSG_SPECUNKNOW];
1613         }
1614         else
1615         {
1616             // check for lava,slime,water,crush,fall,monsters..
1617             if (source->type == MT_BARREL)
1618             {
1619                 if (source->target->player)
1620                 {
1621                     GenPrintf(EMSG_playmsg, text[DEATHMSG_BARRELFRAG],
1622                                 player_names[target->player - players],
1623                                 player_names[source->target->player - players]);
1624                     return;
1625                 }
1626                 else
1627                     str = text[DEATHMSG_BARREL];
1628             }
1629             else
1630             switch (source->type)
1631             {
1632               case MT_POSSESSED: str = text[DEATHMSG_POSSESSED]; break;
1633               case MT_SHOTGUY:   str = text[DEATHMSG_SHOTGUY];   break;
1634               case MT_VILE:      str = text[DEATHMSG_VILE];      break;
1635               case MT_FATSO:     str = text[DEATHMSG_FATSO];     break;
1636               case MT_CHAINGUY:  str = text[DEATHMSG_CHAINGUY];  break;
1637               case MT_TROOP:     str = text[DEATHMSG_TROOP];     break;
1638               case MT_SERGEANT:  str = text[DEATHMSG_SERGEANT];  break;
1639               case MT_SHADOWS:   str = text[DEATHMSG_SHADOWS];   break;
1640               case MT_HEAD:      str = text[DEATHMSG_HEAD];      break;
1641               case MT_BRUISER:   str = text[DEATHMSG_BRUISER];   break;
1642               case MT_UNDEAD:    str = text[DEATHMSG_UNDEAD];    break;
1643               case MT_KNIGHT:    str = text[DEATHMSG_KNIGHT];    break;
1644               case MT_SKULL:     str = text[DEATHMSG_SKULL];     break;
1645               case MT_SPIDER:    str = text[DEATHMSG_SPIDER];    break;
1646               case MT_BABY:      str = text[DEATHMSG_BABY];      break;
1647               case MT_CYBORG:    str = text[DEATHMSG_CYBORG];    break;
1648               case MT_PAIN:      str = text[DEATHMSG_PAIN];      break;
1649               case MT_WOLFSS:    str = text[DEATHMSG_WOLFSS];    break;
1650               default:           str = text[DEATHMSG_DEAD];      break;
1651             }
1652         }
1653         GenPrintf(EMSG_playmsg, str, player_names[target->player - players]);
1654     }
1655 }
1656 
1657 // WARNING : check cv_fraglimit>0 before call this function !
P_CheckFragLimit(player_t * p)1658 void P_CheckFragLimit(player_t *p)
1659 {
1660     int fragteam = 0;
1661     if( cv_teamplay.EV )
1662     {
1663         int i;
1664         for(i=0;i<MAXPLAYERS;i++)
1665         {
1666             if(ST_SameTeam(p,&players[i]))
1667                 fragteam += ST_PlayerFrags(i);
1668         }
1669     }
1670     else
1671     {
1672         fragteam = ST_PlayerFrags(p - players);
1673     }
1674     // CV_VALUE, may be too large for EV
1675     if( fragteam >= cv_fraglimit.value )
1676         G_ExitLevel();
1677 }
1678 
1679 
1680 /************************************************************
1681  *
1682  *  Returns ammo count in current weapon
1683  *
1684  ************************************************************
1685  */
1686 static
P_AmmoInWeapon(player_t * player)1687 int P_AmmoInWeapon(player_t *player)
1688 {
1689     ammotype_t  ammo = player->weaponinfo[player->readyweapon].ammo;
1690     int         ammo_count = player->ammo[ammo];
1691 
1692     return ammo == am_noammo ? 0
1693         : ammo_count ? ammo_count : -1;
1694 }
1695 
1696 
1697 // P_KillMobj
1698 //
1699 //      source is the attacker, (for revenge, frags)
1700 //      target is the 'target' of the attack, target dies...
1701 //      inflictor is the weapon, missile, creature melee, or NULL, (for messages)
1702 //                                          113
P_KillMobj(mobj_t * target,mobj_t * inflictor,mobj_t * source)1703 void P_KillMobj ( mobj_t*  target,
1704                   mobj_t*  inflictor,
1705                   mobj_t*  source )
1706 {
1707     mobjtype_t  item = 0;
1708     mobj_t*     mo;
1709     int         drop_ammo_count = 0;
1710 
1711     // dead target is no more shootable
1712     if( ! cv_solidcorpse.EV )
1713         target->flags &= ~MF_SHOOTABLE;
1714 
1715     target->flags &= ~(MF_FLOAT|MF_SKULLFLY);
1716 
1717     if (target->type != MT_SKULL)
1718         target->flags &= ~MF_NOGRAVITY;
1719 
1720 #ifdef DOGS
1721     // [WDJ] MBF dogs, extension for DoomLegacy.
1722     if( (target->type == MT_DOG) || (target->type == helper_MT) )
1723         G_KillDog( target );
1724 #endif
1725 
1726     // scream a corpse :)
1727     if( target->flags & MF_CORPSE )
1728     {
1729         // Turn it to gibs.
1730         P_SetMobjState (target, S_GIBS);
1731 
1732         target->flags &= ~MF_SOLID;
1733         target->height = 0;
1734         target->radius<<= 1;
1735         target->skin = 0;
1736 
1737         //added:22-02-98: lets have a neat 'crunch' sound!
1738         S_StartObjSound(target, sfx_slop);
1739         return;
1740     }
1741 
1742     //added:22-02-98: remember who exploded the barrel, so that the guy who
1743     //                shot the barrel which killed another guy, gets the frag!
1744     //                (source is passed from barrel to barrel also!)
1745     //                (only for multiplayer fun, does not remember monsters)
1746     if ((target->type == MT_BARREL || target->type == MT_POD)
1747         && source && source->player)
1748     {
1749         P_SetReference(target->target, source);
1750         target->target = source;
1751     }
1752 
1753     if( EV_legacy < 131 )  // old Legacy, Boom, MBF
1754     {
1755         // Version 131 and after this is done later in A_Fall.
1756         // (this fix the stepping monster)
1757         target->flags   |= MF_CORPSE|MF_DROPOFF;
1758         target->height >>= 2;
1759         if( EV_legacy >= 112 )
1760             target->radius -= (target->radius>>4);      //for solid corpses
1761     }
1762     // show death messages, only if it concern the console player
1763     // (be it an attacker or a target)
1764     if (target->player && (target->player == consoleplayer_ptr) )
1765         P_DeathMessages (target, inflictor, source);
1766     else
1767     if (source && source->player && (source->player == consoleplayer_ptr) )
1768         P_DeathMessages (target, inflictor, source);
1769     else
1770     if (target->player && target->player->bot)	//added by AC for acbot
1771        P_DeathMessages (target, inflictor, source);
1772 
1773 
1774 
1775     // if killed by a player
1776     if (source && source->player)
1777     {
1778         // count for intermission
1779         if (target->flags & MF_COUNTKILL)
1780             source->player->killcount++;
1781 
1782         // count frags if player killed player
1783         if (target->player)
1784         {
1785             source->player->frags[target->player-players]++;
1786             if( EN_heretic )
1787             {
1788                 if(source->player == displayplayer_ptr
1789                 || source->player == displayplayer2_ptr )
1790                     S_StartSound(sfx_gfrag);
1791 
1792                 // Make a super chicken
1793                 if(source->player->chickenTics)
1794                     P_GivePower(source->player, pw_weaponlevel2);
1795             }
1796             // check fraglimit cvar
1797             if (cv_fraglimit.value)
1798                 P_CheckFragLimit(source->player);
1799         }
1800     }
1801     else if (!multiplayer && (target->flags & MF_COUNTKILL))
1802     {
1803         // count all monster deaths,
1804         // even those caused by other monsters
1805         players[0].killcount++;
1806     }
1807 
1808     // if a player avatar dies...
1809     if (target->player)
1810     {
1811         // count environment kills against you (you fragged yourself!)
1812         if (!source)
1813             target->player->frags[target->player-players]++;
1814 
1815         if( ! cv_solidcorpse.EV )
1816             target->flags &= ~MF_SOLID;                     // does not block
1817         target->flags2 &= ~MF2_FLY;
1818         target->player->powers[pw_flight] = 0;
1819         target->player->powers[pw_weaponlevel2] = 0;
1820         target->player->playerstate = PST_DEAD;
1821         P_DropWeapon (target->player);                  // put weapon away
1822         if (target->player == consoleplayer_ptr )
1823         {
1824             // don't die in auto map,
1825             // switch view prior to dying
1826             if (automapactive)
1827                 AM_Stop ();
1828 
1829             //added:22-02-98: recenter view for next live...
1830             localaiming[0] = 0;
1831         }
1832         if (target->player == displayplayer2_ptr) // NULL when unused
1833         {
1834             // player 2
1835             //added:22-02-98: recenter view for next live...
1836             localaiming[1] = 0;
1837         }
1838 /* HERETODO
1839         if(target->flags2&MF2_FIREDAMAGE)
1840         { // Player flame death
1841             P_SetMobjState(target, S_PLAY_FDTH1);
1842             //S_StartObjSound(target, sfx_hedat1); // Burn sound
1843             goto done;
1844         }
1845 */
1846     }
1847 
1848     if ( target->info->xdeathstate
1849          && ( target->health < -(
1850                (EN_heretic)? (target->info->spawnhealth>>1)  // heretic
1851                : target->info->spawnhealth  // doom
1852             ) )
1853        )
1854     {
1855         P_SetMobjState (target, target->info->xdeathstate);
1856     }
1857     else
1858         P_SetMobjState (target, target->info->deathstate);
1859 
1860     target->tics -= PP_Random(pr_killtics)&3;
1861 
1862     if (target->tics < 1)
1863         target->tics = 1;
1864 
1865     // Drop stuff.
1866     // This determines the kind of object spawned
1867     // during the death frame of a thing.
1868 
1869     // Frags Weapon Falling support
1870     if( target->player && cv_fragsweaponfalling.EV )
1871     {
1872         drop_ammo_count = P_AmmoInWeapon(target->player);
1873         //if (!drop_ammo_count)
1874         //    goto done;
1875 
1876         if (EN_heretic)
1877         {
1878             switch (target->player->readyweapon)
1879             {
1880                 case wp_crossbow:
1881                     item = MT_HMISC15;
1882                     break;
1883 
1884                 case wp_blaster:
1885                     item = MT_RIPPER;
1886                     break;
1887 
1888                 case wp_skullrod:
1889                     item = MT_WSKULLROD;
1890                     break;
1891 
1892                 case wp_phoenixrod:
1893                     item = MT_WPHOENIXROD;
1894                     break;
1895 
1896                 case wp_mace:
1897                     item = MT_WMACE;
1898                     break;
1899 
1900                 default:
1901                     //debug_Printf("Unknown weapon %d\n", target->player->readyweapon);
1902                     goto done;
1903             }
1904         }
1905         else
1906         {
1907             switch (target->player->readyweapon)
1908             {
1909                 case wp_shotgun:
1910                     item = MT_SHOTGUN;
1911                     break;
1912 
1913                 case wp_supershotgun:
1914                     item = MT_SUPERSHOTGUN;
1915                     break;
1916 
1917                 case wp_chaingun:
1918                     item = MT_CHAINGUN;
1919                     break;
1920 
1921                 case wp_missile:
1922                     item = MT_ROCKETLAUNCH;
1923                     break;
1924 
1925                 case wp_plasma:
1926                     item = MT_PLASMAGUN;
1927                     break;
1928 
1929                 case wp_bfg:
1930                     item = MT_BFG9000;
1931                     break;
1932 
1933                 default:
1934                     //debug_Printf("Unknown weapon %d\n", target->player->readyweapon);
1935                     goto done;
1936             }
1937         }
1938     }
1939     else
1940     {
1941         //DarkWolf95: Support for Chex Quest
1942         if(gamemode == chexquest1)  //don't drop monster ammo in chex quest
1943            goto done;
1944 
1945         switch (target->type)
1946         {
1947             case MT_WOLFSS:
1948             case MT_POSSESSED:
1949                 item = MT_CLIP;
1950                 break;
1951 
1952             case MT_SHOTGUY:
1953                 item = MT_SHOTGUN;
1954                 break;
1955 
1956             case MT_CHAINGUY:
1957                 item = MT_CHAINGUN;
1958                 break;
1959 
1960             default:
1961                 goto done;
1962         }
1963     }
1964 
1965     // SoM: Damnit! Why not use the target's floorz?
1966     // Doom, Boom, MBF use ONFLOORZ.
1967     mo = P_SpawnMobj (target->x, target->y,
1968                       ((EV_legacy < 132) ? ONFLOORZ : target->floorz), item);
1969     mo->flags |= MF_DROPPED;    // special versions of items
1970 
1971     if( !cv_fragsweaponfalling.EV )
1972         drop_ammo_count = 0;    // Doom default ammo count
1973 
1974     mo->dropped_ammo_count = drop_ammo_count;
1975 
1976 done:
1977     return;
1978 }
1979 
1980 
1981 //---------------------------------------------------------------------------
1982 //
1983 // FUNC P_MinotaurSlam
1984 //
1985 //---------------------------------------------------------------------------
1986 
1987 static
P_MinotaurSlam(mobj_t * source,mobj_t * target)1988 void P_MinotaurSlam(mobj_t *source, mobj_t *target)
1989 {
1990     angle_t angle;
1991     fixed_t thrust;
1992 
1993     angle = R_PointToAngle2(source->x, source->y, target->x, target->y);
1994     thrust = 16*FRACUNIT + (PP_Random(ph_minoslam)<<10);
1995     target->momx += FixedMul(thrust, cosine_ANG(angle));
1996     target->momy += FixedMul(thrust, sine_ANG(angle));
1997     P_DamageMobj(target, NULL, NULL, HITDICE(6));
1998     if(target->player)
1999     {
2000         target->reactiontime = 14 + (PP_Random(ph_minoslam)&7);
2001     }
2002 }
2003 
2004 //---------------------------------------------------------------------------
2005 //
2006 // FUNC P_TouchWhirlwind
2007 //
2008 //---------------------------------------------------------------------------
2009 
2010 static
P_TouchWhirlwind(mobj_t * target)2011 boolean P_TouchWhirlwind(mobj_t *target)
2012 {
2013     int randVal;
2014 
2015     target->angle += PP_SignedRandom(ph_whirlwind)<<20;
2016     target->momx += PP_SignedRandom(ph_whirlwind)<<10;
2017     target->momy += PP_SignedRandom(ph_whirlwind)<<10;
2018     if(leveltime&16 && !(target->flags2&MF2_BOSS))
2019     {
2020         randVal = PP_Random(ph_whirlwind);
2021         if(randVal > 160)
2022         {
2023             randVal = 160;
2024         }
2025         target->momz += randVal<<10;
2026         if(target->momz > 12*FRACUNIT)
2027         {
2028             target->momz = 12*FRACUNIT;
2029         }
2030     }
2031     if(!(leveltime&7))
2032     {
2033         return P_DamageMobj(target, NULL, NULL, 3);
2034     }
2035     return false;
2036 }
2037 
2038 
2039 //---------------------------------------------------------------------------
2040 //
2041 // FUNC P_ChickenMorphPlayer
2042 //
2043 // Returns true if the player gets turned into a chicken.
2044 //
2045 //---------------------------------------------------------------------------
2046 
2047 // [WDJ] Fixed to keep the same player mobj.
2048 // Used to change the player mobj, and hide the prev as a corpse above
2049 // the ceiling using S_FREETARGMOBJ.  This could happen in Line attack or Damage.
P_ChickenMorphPlayer(player_t * player)2050 boolean P_ChickenMorphPlayer(player_t *player)
2051 {
2052     mobj_t *pmo;
2053     int oldflags2;
2054 
2055     if(player->chickenTics)
2056     {
2057         if((player->chickenTics < CHICKENTICS-TICRATE)
2058             && !player->powers[pw_weaponlevel2])
2059         { // Make a super chicken
2060             P_GivePower(player, pw_weaponlevel2);
2061         }
2062         return false;
2063     }
2064     if(player->powers[pw_invulnerability])
2065     { // Immune when invulnerable
2066         return false;
2067     }
2068     pmo = player->mo;
2069     oldflags2 = pmo->flags2;
2070     P_MorphMobj(pmo, MT_CHICPLAYER, MM_telefog,
2071 #ifdef PLAYER_CHICKEN_KEEPS_SHADOW
2072                       MF_SHADOW
2073 #else
2074                       0
2075 #endif
2076                 );
2077     pmo->special1 = player->readyweapon;  // save for later restore
2078     pmo->flags2 |= oldflags2&MF2_FLY;  // preserve fly
2079     // Clear skin so it does not override chicken.
2080     pmo->skin = NULL;  // Chickens all look alike.
2081     pmo->tflags &= ~MFT_TRANSLATION6; // no color translation for chicken
2082     // spawnhealth for chicken is 100, this is 30
2083     player->health = pmo->health = MAXCHICKENHEALTH;
2084     player->armorpoints = player->armortype = 0;
2085 #ifndef PLAYER_CHICKEN_KEEPS_SHADOW
2086     // If keep MF_SHADOW and cancel invisibility, then MF_SHADOW is permananet.
2087     player->powers[pw_invisibility] = 0;
2088 #endif
2089     player->powers[pw_weaponlevel2] = 0;
2090     player->weaponinfo = wpnlev1info;
2091     player->chickenTics = CHICKENTICS;  // start chicken timer
2092     P_ActivateBeak(player);
2093     return true;
2094 }
2095 
2096 //---------------------------------------------------------------------------
2097 //
2098 // FUNC P_ChickenMorph
2099 //
2100 //---------------------------------------------------------------------------
2101 
2102 // Other actors, not players.
P_ChickenMorph(mobj_t * actor)2103 boolean P_ChickenMorph(mobj_t *actor)
2104 {
2105     mobjtype_t moType;
2106 
2107     if(actor->player)
2108     {
2109         return false;
2110     }
2111     moType = actor->type;
2112     switch(moType)
2113     {
2114         case MT_POD:
2115         case MT_CHICKEN:
2116         case MT_HHEAD:
2117         case MT_MINOTAUR:
2118         case MT_SORCERER1:
2119         case MT_SORCERER2:
2120             return false;
2121         default:
2122             break;
2123     }
2124 
2125     // preserve position, angle, target, invisible
2126     P_MorphMobj(actor, MT_CHICKEN, MM_telefog, MF_SHADOW);
2127     actor->special1 = CHICKENTICS+PP_Random(ph_chickenmorph);  // monster chickentics
2128     actor->special2 = moType;  // save type for restore
2129     return true;
2130 }
2131 
2132 //---------------------------------------------------------------------------
2133 //
2134 // FUNC P_AutoUseChaosDevice
2135 //
2136 //---------------------------------------------------------------------------
2137 
P_AutoUseChaosDevice(player_t * player)2138 boolean P_AutoUseChaosDevice(player_t *player)
2139 {
2140     int i;
2141 
2142     for(i = 0; i < player->inventorySlotNum; i++)
2143     {
2144         if(player->inventory[i].type == arti_teleport)
2145         {
2146             P_PlayerUseArtifact(player, arti_teleport);
2147             player->health = player->mo->health = (player->health+1)/2;
2148             return(true);
2149         }
2150     }
2151     return(false);
2152 }
2153 
2154 //---------------------------------------------------------------------------
2155 //
2156 // PROC P_AutoUseHealth
2157 //
2158 //---------------------------------------------------------------------------
2159 
2160 // From Heretic
P_AutoUseHealth(player_t * player,int saveHealth)2161 void P_AutoUseHealth(player_t *player, int saveHealth)
2162 {
2163     int i;
2164     int count;
2165     int normalCount;
2166     int superCount;
2167 
2168     // Uses P_PlayerUseArtifact, so do not need to know inventory slot.
2169     normalCount = superCount = 0;
2170     for(i = 0; i < player->inventorySlotNum; i++)
2171     {
2172         if(player->inventory[i].type == arti_health)
2173         {
2174             normalCount = player->inventory[i].count;
2175         }
2176         else if(player->inventory[i].type == arti_superhealth)
2177         {
2178             superCount = player->inventory[i].count;
2179         }
2180     }
2181     if((gameskill == sk_baby) && (normalCount*25 >= saveHealth))
2182     { // Use quartz flasks
2183         count = (saveHealth+24)/25;
2184         for(i = 0; i < count; i++)
2185             P_PlayerUseArtifact( player, arti_health);
2186     }
2187     else if(superCount*100 >= saveHealth)
2188     { // Use mystic urns
2189         count = (saveHealth+99)/100;
2190         for(i = 0; i < count; i++)
2191             P_PlayerUseArtifact( player, arti_superhealth);
2192     }
2193     else if((gameskill == sk_baby)
2194         && (superCount*100+normalCount*25 >= saveHealth))
2195     { // Use mystic urns and quartz flasks
2196         count = (saveHealth+24)/25;
2197         for(i = 0; i < count; i++)
2198             P_PlayerUseArtifact( player, arti_health);
2199 
2200         saveHealth -= count*25;
2201         count = (saveHealth+99)/100;
2202         for(i = 0; i < count; i++)
2203             P_PlayerUseArtifact( player, arti_superhealth);
2204     }
2205     player->mo->health = player->health;
2206 }
2207 
2208 
2209 //
2210 // P_DamageMobj
2211 // Damages both enemies and players
2212 // "inflictor" is the thing that caused the damage
2213 //  creature or missile, can be NULL (slime, etc)
2214 // "source" is the thing to target after taking damage
2215 //  creature or NULL
2216 // Source and inflictor are the same for melee attacks.
2217 // Source can be NULL for slime, barrel explosions
2218 // and other environmental stuff.
2219 //
2220 // Return true when damaged, for blood splats and other effects.
2221 // Fixed to not change the player mobj.
P_DamageMobj(mobj_t * target,mobj_t * inflictor,mobj_t * source,int damage)2222 boolean P_DamageMobj ( mobj_t*   target,
2223                        mobj_t*   inflictor,
2224                        mobj_t*   source,
2225                        int       damage )
2226 {
2227     angle_t     ang;
2228     int         angf;
2229     int         saved;
2230     player_t*   player;  // always target->player
2231     fixed_t     thrust;
2232     boolean     voodoo_target = false;
2233     boolean     mbf_justhit = false;  // MBF, delayed MF_JUSTHIT
2234     boolean     takedamage = true;  // block damage between members of same team
2235 
2236     player = target->player;
2237 
2238     // [WDJ] 7/2017 Moved the voodoo intercept of damage to be tested earlier
2239     // because of the weapons and armor specific player checks that
2240     // can get applied to the wrong player otherwise.
2241     // This code can change the target of the damage.
2242     // If we ever implement the player as a monster, this code needs to be first.
2243     if( player )
2244     {
2245         // [WDJ] 2/7/2011 Intercept voodoo damage
2246         voodoo_target = (player->mo != target);
2247         if( voodoo_target )
2248         {
2249             mobj_t * voodoo_thing = target;
2250             if(voodoo_mode >= VM_target)
2251             {
2252                 // Multiplayer and single player:
2253                 // try to find someone appropriate, instead of spawn point player.
2254                 // Target source player causing damage
2255                 if( source && source->player
2256                     && (source->player->mo == source) )
2257                 {
2258                     // Shooting any voodoo doll, select shooting player
2259                     player = source->player;
2260                 }
2261                 // Target last player to trigger a switch or linedef.
2262                 else if( spechit_player && spechit_player->mo )
2263                 {
2264                     player = spechit_player;
2265                 }
2266             }
2267 
2268             if(! player->mo )  // this player is not present
2269             {
2270                 if( voodoo_mode < VM_target )
2271                 {
2272                     // remove this voodoo doll to avoid segfaults
2273                     P_RemoveMobj( voodoo_thing );
2274                 }
2275                 goto ret_false;
2276             }
2277 
2278             if( voodoo_mode == VM_vanilla )
2279             {
2280                 target->health -= damage;  // damage the voodoo too
2281             }
2282             else
2283             {
2284                 if( multiplayer && (damage > player->health))
2285                 {
2286                     // Kill the voodoo, so it cannot kill after respawn.
2287                     // Voodoo doll in crusher is game fatal otherwise.
2288                     voodoo_thing->health = 0;
2289                     voodoo_thing->player = NULL;
2290                     P_KillMobj ( voodoo_thing, inflictor, source );
2291                     spechit_player = NULL;  // cancel voodoo damage
2292                 }
2293                 // let player mobj get the damage, no Zombies
2294                 target = player->mo;
2295             }
2296         }
2297 
2298         if( gameskill == sk_baby )
2299             damage >>= 1;   // take half damage in trainer mode
2300     }
2301 
2302 
2303     // killough 8/31/98: allow bouncers to take damage
2304     if ( !(target->flags & (MF_SHOOTABLE | MF_BOUNCES)) )
2305         goto ret_false; // shouldn't happen...
2306 
2307     // [WDJ] Solid Corpse health < 0.
2308     if( (target->health <= 0) && !(target->flags & MF_CORPSE) )
2309         goto ret_false;
2310 
2311     if ( target->flags & MF_SKULLFLY )
2312     {
2313         // Minotaur is invulnerable during charge attack
2314         if(target->type == MT_MINOTAUR)
2315             goto ret_false;
2316 
2317         target->momx = target->momy = target->momz = 0;
2318     }
2319 
2320     // Special damage types
2321     if(inflictor)
2322     {
2323         switch(inflictor->type)
2324         {
2325         case MT_EGGFX:
2326             if(player)
2327             {
2328                 // Fixed to not change the player mobj.
2329                 P_ChickenMorphPlayer(player);
2330             }
2331             else
2332             {
2333                 P_ChickenMorph(target);
2334             }
2335             goto ret_false; // Always return
2336         case MT_WHIRLWIND:
2337             takedamage = P_TouchWhirlwind(target);
2338             goto ret_damage;
2339         case MT_MINOTAUR:
2340             if(inflictor->flags&MF_SKULLFLY)
2341             { // Slam only when in charge mode
2342                 P_MinotaurSlam(inflictor, target);
2343                 goto ret_true;
2344             }
2345             break;
2346         case MT_MACEFX4: // Death ball
2347             if((target->flags2&MF2_BOSS) || target->type == MT_HHEAD)
2348             { // Don't allow cheap boss kills
2349                 break;
2350             }
2351             else if(player)
2352             { // Player specific checks
2353                 if(player->powers[pw_invulnerability])
2354                 { // Can't hurt invulnerable players
2355                     break;
2356                 }
2357                 if(P_AutoUseChaosDevice(player))
2358                 { // Player was saved using chaos device
2359                     goto ret_false;
2360                 }
2361             }
2362             damage = 10000; // Something's gonna die
2363             break;
2364         case MT_PHOENIXFX2: // Flame thrower
2365             if(player && PP_Random(ph_phoenixdam2) < 128)
2366             { // Freeze player for a bit
2367                 target->reactiontime += 4;
2368             }
2369             break;
2370         case MT_RAINPLR1: // Rain missiles
2371         case MT_RAINPLR2:
2372         case MT_RAINPLR3:
2373         case MT_RAINPLR4:
2374             if(target->flags2&MF2_BOSS)
2375             { // Decrease damage for bosses
2376                 damage = (PP_Random(ph_raindam)&7)+1;
2377             }
2378             break;
2379         case MT_HORNRODFX2:
2380         case MT_PHOENIXFX1:
2381             if(target->type == MT_SORCERER2 && PP_Random(ph_sordam) < 96)
2382             { // D'Sparil teleports away
2383                 P_DSparilTeleport(target);
2384                 goto ret_false;
2385             }
2386             break;
2387         case MT_BLASTERFX1:
2388         case MT_RIPPER:
2389             if(target->type == MT_HHEAD)
2390             { // Less damage to Ironlich bosses
2391                 damage = PP_Random(ph_headdam)&1;
2392                 if(!damage)
2393                     goto ret_false;
2394             }
2395             break;
2396         default:
2397             break;
2398         }
2399     }
2400 
2401     // Some close combat weapons should not
2402     // inflict thrust and push the victim out of reach,
2403     // thus kick away unless using the chainsaw.
2404     if (inflictor
2405         && !(target->flags & MF_NOCLIP)  // unless target is NOCLIP
2406         && !(inflictor->flags2&MF2_NODMGTHRUST)  // unless inflictor cannot thrust
2407         && (!source  // not chainsaw
2408             || !source->player
2409             || source->player->readyweapon != wp_chainsaw))
2410     {
2411         // Impose thrust upon the target from the weapon
2412         fixed_t  amomx, amomy, amomz=0;//SoM: 3/28/2000
2413 
2414         ang = R_PointToAngle2 ( inflictor->x, inflictor->y,
2415                                 target->x, target->y);
2416 
2417         if (EN_heretic )
2418             thrust = damage*(FRACUNIT>>3)*150/target->info->mass;
2419         else
2420             thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
2421 
2422         // sometimes a target shot down might fall off a ledge forwards
2423         if ( damage < 40
2424              && damage > target->health
2425              && target->z - inflictor->z > 64*FRACUNIT
2426              && (PP_Random(pr_damagemobj) & 0x01)
2427            )
2428         {
2429             ang += ANG180;
2430             thrust *= 4;
2431         }
2432 
2433         angf = ANGLE_TO_FINE(ang);
2434 
2435         if( EN_heretic
2436             && source && (source == inflictor)
2437             && source->player
2438             && source->player->powers[pw_weaponlevel2]
2439             && source->player->readyweapon == wp_staff)
2440         {
2441             // Staff power level 2
2442             target->momx += FixedMul(10*FRACUNIT, finecosine[angf]);
2443             target->momy += FixedMul(10*FRACUNIT, finesine[angf]);
2444             if(!(target->flags&MF_NOGRAVITY))
2445             {
2446                 target->momz += 5*FRACUNIT;
2447             }
2448         }
2449         else
2450         {
2451             // all other thrusting weapons
2452             amomx = FixedMul (thrust, finecosine[angf]);
2453             amomy = FixedMul (thrust, finesine[angf]);
2454             target->momx += amomx;
2455             target->momy += amomy;
2456 
2457             // added momz (do it better for missiles explosion)
2458             if ( source
2459                  && (EV_legacy >= 124)
2460                  && ((EV_legacy < 129) || !cv_allowrocketjump.EV))
2461             {
2462                 // Legacy
2463                 int dist,z;
2464 
2465                 if(source==target) // rocket in yourself (suicide)
2466                 {
2467                     viewx=inflictor->x;
2468                     viewy=inflictor->y;
2469                     z=inflictor->z;
2470                 }
2471                 else
2472                 {
2473                     viewx=source->x;
2474                     viewy=source->y;
2475                     z=source->z;
2476                 }
2477                 dist=R_PointToDist(target->x,target->y);
2478 
2479                 viewx=0;
2480                 viewy=z;
2481                 ang = R_PointToAngle(dist,target->z);
2482                 amomz = FixedMul (thrust, sine_ANG(ang));
2483             }
2484             else //SoM: 2/28/2000: Added new function.
2485             if( (EV_legacy >= 129) && cv_allowrocketjump.EV )
2486             {
2487                 // Legacy rocket jump.
2488                 fixed_t delta1 = abs(inflictor->z - target->z);
2489                 fixed_t delta2 = abs(inflictor->z - (target->z + target->height));
2490                 amomz = (abs(amomx) + abs(amomy))>>1;
2491 
2492                 if(delta1 >= delta2 && inflictor->momz < 0)
2493                     amomz = -amomz;
2494             }
2495 
2496             target->momz += amomz;
2497 #ifdef CLIENTPREDICTION2
2498             if( player && player->spirit )
2499             {
2500                 player->spirit->momx += amomx;
2501                 player->spirit->momy += amomy;
2502                 player->spirit->momz += amomz;
2503             }
2504 #endif
2505             // [WDJ] MBF
2506             // killough 11/98: thrust objects hanging off ledges
2507             if( target->eflags & MF_FALLING && (target->tipcount >= MAXTIPCOUNT) )
2508                 target->tipcount = 0;
2509         }
2510     }
2511 
2512     // Solid Corpse specific
2513     if( target->flags & MF_CORPSE )
2514     {
2515         target->health -= damage;
2516         // [WDJ] Corpse health < 0, so solid corpse test is < 0.
2517         if( target->health < -target->info->spawnhealth )
2518             P_KillMobj ( target, inflictor, source );  // to gibs
2519         // Keep corpse from ticking the P_Random in the pain test.
2520         goto ret_true;
2521     }
2522 
2523     // target player specific
2524     if (player)
2525     {
2526         // end of game hell hack
2527         if (target->subsector->sector->special == 11
2528             && damage >= target->health)
2529         {
2530             damage = target->health - 1;
2531         }
2532 
2533         // Below certain threshold,
2534         // ignore damage in GOD mode, or with INVUL power.
2535         if( (player->cheats&CF_GODMODE) || player->powers[pw_invulnerability] )
2536         {
2537             // Boom, MBF: killough 3/26/98: make god mode 100%
2538             // !comp[comp_god]
2539             if( (player->cheats&CF_GODMODE) && EN_invul_god )
2540                 goto ret_false;
2541 
2542             if( damage < 1000 )
2543                 goto ret_false;
2544         }
2545 
2546         if (player->armortype)
2547         {
2548             if (player->armortype == 1)
2549                 saved = (EN_heretic)? damage>>1 : damage/3;
2550             else
2551                 saved = (EN_heretic)? (damage>>1)+(damage>>2) : damage/2;
2552 
2553             if (player->armorpoints <= saved)
2554             {
2555                 // armor is used up
2556                 saved = player->armorpoints;
2557                 player->armortype = 0;
2558             }
2559             player->armorpoints -= saved;
2560             damage -= saved;
2561         }
2562 
2563         // added team play and teamdamage (view logboris at 13-8-98 to understand)
2564         // [WDJ] 2/7/2011  Allow damage to player when:
2565         // olddemo (version < 125) // because they did not have these restrictions
2566         // OR telefrag        // not subject to friendly fire tests
2567         // OR no source       // no source interaction, sector damage etc.
2568         // OR NOT sourceplayer  // monster attack
2569         // OR (source==target)  // self inflicted damage (missile launcher)
2570         // OR voodoo_target   // voodoo damage allowed by previous tests
2571         // OR NOT multiplayer  // single player
2572         // OR ( coop         // all on same team
2573         //     AND cv_teamdamage  // team members can hurt each other
2574         //    )
2575         // OR ( deathmatch   // teams or individual, not coop
2576         //     AND ( NOT teamplay
2577         //               // otherwise teamplay
2578         //           OR cv_teamdamage   // team members can hurt each other
2579         //           OR (target.team != source.team)
2580         //         )
2581         //    )
2582         // [WDJ] For readability and understanding, please do not try to reduce
2583         // these equations, they are not executed very often, and the
2584         // compiler will reduce them during optimization anyway.
2585         if( (! source)		   // no source interaction, sector damage etc.
2586             || (! source->player)  // monster attack
2587             || voodoo_target	   // allowed voodoo damage
2588             || (damage>1000)       // telefrag and death-ball
2589             || (EV_legacy < 125)   // old demoversion bypasses restrictions
2590             || (source==target)    // self-inflicted
2591             || (! multiplayer)     // single player
2592             || ( (!deathmatch) && cv_teamdamage.EV )  // coop
2593             || ( deathmatch        // deathmatch 1,2,3
2594                  && ( (!cv_teamplay.EV)    // no teams
2595                       || cv_teamdamage.EV  // can damage within team
2596                       || ! ST_SameTeam(source->player,player) // diff team
2597                     )
2598                  )
2599             )
2600         {
2601             if(damage >= player->health
2602                 && ((gameskill == sk_baby) || deathmatch)
2603                 && !player->chickenTics)
2604             { // Try to use some inventory health
2605                 P_AutoUseHealth(player, damage - player->health + 1);
2606             }
2607 
2608             // Update player health here, because they may die before
2609             // reaching the later player update.
2610             player->health -= damage;   // mirror mobj health here for Dave
2611             if (player->health < 0)
2612                 player->health = 0;
2613             // [WDJ] If player->mo is updated here, it prevents player gibs.
2614             // target = player->mo, as set in voodoo logic.
2615 
2616             player->damagecount += damage;  // add damage after armor / invuln
2617 
2618             if (player->damagecount > 100)
2619                 player->damagecount = 100;  // teleport stomp does 10k points...
2620 
2621             //added:22-02-98: force feedback ??? electro-shock???
2622             if (player == consoleplayer_ptr )
2623                 I_Tactile (40,10,40+min(damage, 100)*2);
2624         }
2625         else
2626         {
2627             takedamage = false;  // block damage
2628         }
2629         player->attacker = source;
2630     }
2631 
2632     if( takedamage )
2633     {
2634         target->health -= damage;
2635 
2636         // check for kill
2637         if (target->health <= 0)
2638         {
2639             target->special1 = damage;
2640             if(player && inflictor && !player->chickenTics)
2641             { // Check for flame death
2642                 if((inflictor->flags2&MF2_FIREDAMAGE)
2643                     || ((inflictor->type == MT_PHOENIXFX1)
2644                     && (target->health > -50) && (damage > 25)))
2645                 {
2646                     target->flags2 |= MF2_FIREDAMAGE;
2647                 }
2648             }
2649 
2650             P_KillMobj ( target, inflictor, source );
2651             goto ret_true;
2652         }
2653 
2654         // This must be after KillMobj, so target damage can be negative.
2655         if( player )
2656         {
2657             if( player->mo )
2658                 player->mo->health = player->health; // keep mobj and player health same
2659         }
2660 
2661         // [WDJ] MBF, From MBF, PrBoom, EternityEngine.
2662         // killough 9/7/98: keep track of targets so that friends can help friends
2663         if( EN_mbf )
2664         {
2665             // If target is a player, set player's target to source,
2666             // so that a friend can tell who is hurting a player
2667             if(player)
2668             {
2669                 P_SetReference(target->target, source);
2670                 target->target = source;
2671             }
2672 
2673             // killough 9/8/98:
2674             // If target's health is less than 50%, move it to the front of its list.
2675             // This will slightly increase the chances that enemies will choose to
2676             // "finish it off", but its main purpose is to alert friends of danger.
2677             if( target->health*2 < target->info->spawnhealth )
2678             {
2679                 P_MoveClassThink( &target->thinker, 1 );  // move first
2680             }
2681         }
2682 
2683         if( (PP_Random(pr_painchance) < target->info->painchance)
2684             && !(target->flags&(MF_SKULLFLY|MF_CORPSE)) )
2685         {
2686             if( EN_mbf )
2687                 mbf_justhit = true;  // defer setting MF_JUSTHIT to below
2688             else
2689                 target->flags |= MF_JUSTHIT;    // fight back!
2690 
2691             P_SetMobjState (target, target->info->painstate);
2692         }
2693 
2694         target->reactiontime = 0;           // we're awake now...
2695     }
2696 
2697     if ( source && source != target  // fixes bug where monster attacks self
2698          && source->type != MT_VILE
2699          && (!target->threshold || target->type == MT_VILE)
2700          && !(source->flags2 & MF2_BOSS)
2701          && !(source->type == MT_WIZARD && target->type == MT_SORCERER2)
2702          && ( MONSTER_INFIGHTING
2703               || ! (EN_mbf && SAME_FRIEND(source, target))
2704             )
2705        )
2706     {
2707         // killough 2/15/98: remember last enemy, to prevent sleeping early
2708         // 2/21/98: Place priority on players
2709         // killough 9/9/98: cleaned up, made more consistent:
2710         if( !target->lastenemy
2711             || target->lastenemy->health <= 0
2712             ||( EN_mbf ?
2713                   ( target->target != source
2714                     && SAME_FRIEND(target, target->lastenemy) )  // MBF
2715                 : ! target->lastenemy->player  // Vanilla
2716               )
2717           )
2718         {
2719             // remember last enemy - killough
2720             P_SetReference(target->lastenemy, target->target);
2721             target->lastenemy = target->target;
2722         }
2723 
2724         // if not intent on another player,
2725         // chase after this one
2726         P_SetReference(target->target, source);       // killough 11/98
2727         target->target = source;
2728 
2729         target->threshold = BASETHRESHOLD;
2730         if( target->state == &states[target->info->spawnstate]
2731             && target->info->seestate != S_NULL)
2732             P_SetMobjState (target, target->info->seestate);
2733     }
2734 
2735     // killough 11/98: Don't attack a friend, unless hit by that friend.
2736     // cph 2006/04/01 - implicitly this is only if mbf_features
2737     if( mbf_justhit   // set by MBF to defer MF_JUSTHIT to here
2738         && ( !target->target
2739              || target->target == source
2740              || !BOTH_FRIEND(target, target->target) )
2741       )
2742         target->flags |= MF_JUSTHIT;    // fight back!
2743 
2744 ret_damage:
2745     return takedamage;
2746 
2747 ret_false:
2748     return false;
2749 
2750 ret_true:
2751     return true;  // damaged
2752 }
2753