1 /** @file p_inter.c  Handling interactions (i.e., collisions).
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 2003-2005 Samuel Villarreal <svkaiser@gmail.com>
6  * @authors Copyright © 1993-1996 by id Software, Inc.
7  *
8  * @par License
9  * GPL: http://www.gnu.org/licenses/gpl.html
10  *
11  * <small>This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version. This program is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17  * Public License for more details. You should have received a copy of the GNU
18  * General Public License along with this program; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA</small>
21  */
22 
23 #ifdef MSVC
24 #  pragma optimize("g", off)
25 #endif
26 
27 #include "jdoom64.h"
28 
29 #include "d_net.h"
30 #include "d_netsv.h"
31 #include "dmu_lib.h"
32 #include "player.h"
33 #include "p_map.h"
34 #include "p_user.h"
35 #include "p_tick.h"
36 #include "p_actor.h"
37 #include "p_inventory.h"
38 #include "p_start.h"
39 
40 #define BONUSADD            (6)
41 
42 // A weapon is found with two clip loads, a big item has five clip loads.
43 int maxAmmo[NUM_AMMO_TYPES] = { 200, 50, 300, 50 };
44 int clipAmmo[NUM_AMMO_TYPES] = { 10, 4, 20, 1 };
45 
46 /**
47  * @param player        Player to be given ammo.
48  * @param ammo          Type of ammo to be given.
49  * @param num           Number of clip loads, not the individual count.
50  *
51  * @return              @c false, if the ammo can't be picked up at all.
52  */
P_GiveAmmo(player_t * player,ammotype_t ammo,int num)53 dd_bool P_GiveAmmo(player_t *player, ammotype_t ammo, int num)
54 {
55     if (ammo == AT_NOAMMO)
56         return false;
57 
58     if ((int) ammo < 0 || (int) ammo >= NUM_AMMO_TYPES)
59         Con_Error("P_GiveAmmo: bad type %i", ammo);
60 
61     if (!(player->ammo[ammo].owned < player->ammo[ammo].max))
62         return false;
63 
64     if (num)
65         num *= clipAmmo[ammo];
66     else
67         num = clipAmmo[ammo] / 2;
68 
69     if (gfw_Rule(skill) == SM_BABY)
70     {
71         // Give double ammo in trainer mode.
72         num <<= 1;
73     }
74 
75     // We are about to receive some more ammo. Does the player want to
76     // change weapon automatically?
77     P_MaybeChangeWeapon(player, WT_NOCHANGE, ammo, false);
78 
79     if (player->ammo[ammo].owned + num > player->ammo[ammo].max)
80         player->ammo[ammo].owned = player->ammo[ammo].max;
81     else
82         player->ammo[ammo].owned += num;
83     player->update |= PSF_AMMO;
84 
85     // Maybe unhide the HUD?
86     ST_HUDUnHide(player - players, HUE_ON_PICKUP_AMMO);
87 
88     return true;
89 }
90 
91 /**
92  * The weapon name may have a MF_DROPPED flag ored in.
93  */
P_GiveWeapon(player_t * player,weapontype_t weapon,dd_bool dropped)94 dd_bool P_GiveWeapon(player_t *player, weapontype_t weapon, dd_bool dropped)
95 {
96     ammotype_t i;
97     dd_bool gaveAmmo = false;
98     dd_bool gaveWeapon;
99     int numClips;
100 
101     if (IS_NETGAME && (gfw_Rule(deathmatch) != 2) && !dropped)
102     {
103         // leave placed weapons forever on net games
104         if (player->weapons[weapon].owned)
105             return false;
106 
107         player->bonusCount += BONUSADD;
108         player->weapons[weapon].owned = true;
109         player->update |= PSF_OWNED_WEAPONS;
110 
111         // Give some of each of the ammo types used by this weapon.
112         for (i = 0; i < NUM_AMMO_TYPES; ++i)
113         {
114             if (!weaponInfo[weapon][player->class_].mode[0].ammoType[i])
115                 continue; // Weapon does not take this type of ammo.
116 
117             if (gfw_Rule(deathmatch))
118                 numClips = 5;
119             else
120                 numClips = 2;
121 
122             if (P_GiveAmmo(player, i, numClips))
123                 gaveAmmo = true; // At least ONE type of ammo was given.
124         }
125 
126         // Should we change weapon automatically?
127         P_MaybeChangeWeapon(player, weapon, AT_NOAMMO, gfw_Rule(deathmatch) == 1);
128 
129         // Maybe unhide the HUD?
130         ST_HUDUnHide(player - players, HUE_ON_PICKUP_WEAPON);
131 
132         S_ConsoleSound(SFX_WPNUP, NULL, player - players);
133         return false;
134     }
135     else
136     {
137         // Give some of each of the ammo types used by this weapon.
138         for (i = 0; i < NUM_AMMO_TYPES; ++i)
139         {
140             if (!weaponInfo[weapon][player->class_].mode[0].ammoType[i])
141                 continue;   // Weapon does not take this type of ammo.
142 
143             // Give one clip with a dropped weapon, two clips with a found
144             // weapon.
145             if (dropped)
146                 numClips = 1;
147             else
148                 numClips = 2;
149 
150             if (P_GiveAmmo(player, i, numClips))
151                 gaveAmmo = true; // At least ONE type of ammo was given.
152         }
153 
154         if (player->weapons[weapon].owned)
155             gaveWeapon = false;
156         else
157         {
158             gaveWeapon = true;
159             player->weapons[weapon].owned = true;
160             player->update |= PSF_OWNED_WEAPONS;
161 
162             // Should we change weapon automatically?
163             P_MaybeChangeWeapon(player, weapon, AT_NOAMMO, false);
164         }
165 
166         // Maybe unhide the HUD?
167         if (gaveWeapon)
168             ST_HUDUnHide(player - players, HUE_ON_PICKUP_WEAPON);
169 
170         return (gaveWeapon || gaveAmmo);
171     }
172 }
173 
174 /**
175  * Returns false if the body isn't needed at all
176  *
177  * XXX This is P_GiveHealth in doom/p_inter.c
178  */
P_GiveBody(player_t * player,int num)179 dd_bool P_GiveBody(player_t *player, int num)
180 {
181     if (player->health >= maxHealth)
182         return false;
183 
184     player->health += num;
185     if (player->health > maxHealth)
186         player->health = maxHealth;
187 
188     player->plr->mo->health = player->health;
189     player->update |= PSF_HEALTH;
190 
191     // Maybe unhide the HUD?
192     ST_HUDUnHide(player - players, HUE_ON_PICKUP_HEALTH);
193 
194     return true;
195 }
196 
197 /**
198  * @return              @c true, iff the armor was given.
199  */
P_GiveArmor(player_t * plr,int type,int points)200 dd_bool P_GiveArmor(player_t* plr, int type, int points)
201 {
202     if (plr->armorPoints >= points)
203         return false; // Don't pick up.
204 
205     P_PlayerSetArmorType(plr, type);
206     P_PlayerGiveArmorBonus(plr, points - plr->armorPoints);
207 
208     // Maybe unhide the HUD?
209     ST_HUDUnHide(plr - players, HUE_ON_PICKUP_ARMOR);
210 
211     return true;
212 }
213 
P_GiveKey(player_t * player,keytype_t card)214 void P_GiveKey(player_t* player, keytype_t card)
215 {
216     if (player->keys[card])
217         return;
218 
219     player->bonusCount = BONUSADD;
220     player->keys[card] = 1;
221     player->update |= PSF_KEYS;
222 
223     // Maybe unhide the HUD?
224     ST_HUDUnHide(player - players, HUE_ON_PICKUP_KEY);
225 }
226 
227 /**
228  * d64tc
229  */
P_GiveItem(player_t * player,inventoryitemtype_t item)230 dd_bool P_GiveItem(player_t* player, inventoryitemtype_t item)
231 {
232     if (!P_InventoryGive(player - players, item, false))
233         return false;
234 
235     player->bonusCount = BONUSADD;
236 
237     return true;
238 }
239 
P_GiveBackpack(player_t * player)240 void P_GiveBackpack(player_t* player)
241 {
242     int i;
243 
244     if (!player->backpack)
245     {
246         player->update |= PSF_MAX_AMMO;
247         for (i = 0; i < NUM_AMMO_TYPES; ++i)
248         {
249             player->ammo[i].max *= 2;
250         }
251 
252         player->backpack = true;
253     }
254 
255     for (i = 0; i < NUM_AMMO_TYPES; ++i)
256     {
257         P_GiveAmmo(player, i, 1);
258     }
259 
260     P_SetMessage(player, GOTBACKPACK);
261 }
262 
P_GivePower(player_t * player,int power)263 dd_bool P_GivePower(player_t* player, int power)
264 {
265     player->update |= PSF_POWERS;
266 
267     switch (power)
268     {
269     case PT_INVULNERABILITY:
270         player->powers[power] = INVULNTICS;
271         break;
272 
273     case PT_INVISIBILITY:
274         player->powers[power] = INVISTICS;
275         player->plr->mo->flags |= MF_SHADOW;;
276         break;
277 
278     case PT_FLIGHT:
279         player->powers[power] = 1;
280         player->plr->mo->flags2 |= MF2_FLY;
281         player->plr->mo->flags |= MF_NOGRAVITY;
282         if (player->plr->mo->origin[VZ] <= player->plr->mo->floorZ)
283         {
284             player->flyHeight = 10; // Thrust the player in the air a bit.
285             player->plr->mo->flags |= DDPF_FIXMOM;
286         }
287         break;
288 
289     case PT_INFRARED:
290         player->powers[power] = INFRATICS;
291         break;
292 
293     case PT_IRONFEET:
294         player->powers[power] = IRONTICS;
295         break;
296 
297     case PT_STRENGTH:
298         P_GiveBody(player, maxHealth);
299         player->powers[power] = 1;
300         break;
301 
302     default:
303         if (player->powers[power])
304             return false; // Already got it.
305 
306         player->powers[power] = 1;
307         break;
308     }
309 
310     if (power == PT_ALLMAP)
311         ST_RevealAutomap(player - players, true);
312 
313     // Maybe unhide the HUD?
314     ST_HUDUnHide(player - players, HUE_ON_PICKUP_POWER);
315 
316     return true;
317 }
318 
P_TakePower(player_t * player,int power)319 dd_bool P_TakePower(player_t* player, int power)
320 {
321     mobj_t*             plrmo = player->plr->mo;
322 
323     player->update |= PSF_POWERS;
324     if (player->powers[PT_FLIGHT])
325     {
326         if (plrmo->origin[VZ] != plrmo->floorZ && cfg.common.lookSpring)
327         {
328             player->centering = true;
329         }
330 
331         plrmo->flags2 &= ~MF2_FLY;
332         plrmo->flags &= ~MF_NOGRAVITY;
333         player->powers[power] = 0;
334         return true;
335     }
336 
337     if (!player->powers[power])
338         return false; // Don't got it.
339 
340     player->powers[power] = 0;
341     return true;
342 }
343 
P_TogglePower(player_t * player,powertype_t powerType)344 dd_bool P_TogglePower(player_t *player, powertype_t powerType)
345 {
346     DENG_ASSERT(player != 0);
347     DENG_ASSERT(powerType >= PT_FIRST && powerType < NUM_POWER_TYPES);
348 
349     if (!player->powers[powerType])
350     {
351         return P_GivePower(player, powerType);
352     }
353     else
354     {
355         return P_TakePower(player, powerType);
356     }
357 }
358 
359 typedef enum {
360     IT_NONE = 0,
361     IT_HEALTH_PACK,
362     IT_HEALTH_KIT,
363     IT_HEALTH_BONUS,
364     IT_HEALTH_SOULSPHERE,
365     IT_ARMOR_GREEN,
366     IT_ARMOR_BLUE,
367     IT_ARMOR_BONUS,
368     IT_WEAPON_BFG,
369     IT_WEAPON_CHAINGUN,
370     IT_WEAPON_CHAINSAW,
371     IT_WEAPON_RLAUNCHER,
372     IT_WEAPON_PLASMARIFLE,
373     IT_WEAPON_SHOTGUN,
374     IT_WEAPON_SSHOTGUN,
375     IT_WEAPON_LASERGUN,
376     IT_AMMO_CLIP,
377     IT_AMMO_CLIP_BOX,
378     IT_AMMO_ROCKET,
379     IT_AMMO_ROCKET_BOX,
380     IT_AMMO_CELL,
381     IT_AMMO_CELL_BOX,
382     IT_AMMO_SHELL,
383     IT_AMMO_SHELL_BOX,
384     IT_KEY_BLUE,
385     IT_KEY_YELLOW,
386     IT_KEY_RED,
387     IT_KEY_BLUESKULL,
388     IT_KEY_YELLOWSKULL,
389     IT_KEY_REDSKULL,
390     IT_INVUL,
391     IT_BESERK,
392     IT_INVIS,
393     IT_SUIT,
394     IT_ALLMAP,
395     IT_VISOR,
396     IT_BACKPACK,
397     IT_MEGASPHERE,
398     IT_DEMONKEY1,
399     IT_DEMONKEY2,
400     IT_DEMONKEY3
401 } itemtype_t;
402 
getItemTypeBySprite(spritetype_e sprite)403 static itemtype_t getItemTypeBySprite(spritetype_e sprite)
404 {
405     static const struct item_s {
406         itemtype_t      type;
407         spritetype_e    sprite;
408     } items[] = {
409         { IT_HEALTH_PACK, SPR_STIM },
410         { IT_HEALTH_KIT, SPR_MEDI },
411         { IT_HEALTH_BONUS, SPR_BON1 },
412         { IT_HEALTH_SOULSPHERE, SPR_SOUL },
413         { IT_ARMOR_GREEN, SPR_ARM1 },
414         { IT_ARMOR_BLUE, SPR_ARM2 },
415         { IT_ARMOR_BONUS, SPR_BON2 },
416         { IT_WEAPON_BFG, SPR_BFUG },
417         { IT_WEAPON_CHAINGUN, SPR_MGUN },
418         { IT_WEAPON_CHAINSAW, SPR_CSAW },
419         { IT_WEAPON_RLAUNCHER, SPR_LAUN },
420         { IT_WEAPON_PLASMARIFLE, SPR_PLSM },
421         { IT_WEAPON_SHOTGUN, SPR_SHOT },
422         { IT_WEAPON_SSHOTGUN, SPR_SGN2 },
423         { IT_WEAPON_LASERGUN, SPR_LSRG },
424         { IT_AMMO_CLIP, SPR_CLIP },
425         { IT_AMMO_CLIP_BOX, SPR_AMMO },
426         { IT_AMMO_ROCKET, SPR_RCKT },
427         { IT_AMMO_ROCKET_BOX, SPR_BROK },
428         { IT_AMMO_CELL, SPR_CELL },
429         { IT_AMMO_CELL_BOX, SPR_CELP },
430         { IT_AMMO_SHELL, SPR_SHEL },
431         { IT_AMMO_SHELL_BOX, SPR_SBOX },
432         { IT_KEY_BLUE, SPR_BKEY },
433         { IT_KEY_YELLOW, SPR_YKEY },
434         { IT_KEY_RED, SPR_RKEY },
435         { IT_KEY_BLUESKULL, SPR_BSKU },
436         { IT_KEY_YELLOWSKULL, SPR_YSKU },
437         { IT_KEY_REDSKULL, SPR_RSKU },
438         { IT_INVUL, SPR_PINV },
439         { IT_BESERK, SPR_PSTR },
440         { IT_INVIS, SPR_PINS },
441         { IT_SUIT, SPR_SUIT },
442         { IT_ALLMAP, SPR_PMAP },
443         { IT_VISOR, SPR_PVIS },
444         { IT_BACKPACK, SPR_BPAK },
445         { IT_MEGASPHERE, SPR_MEGA },
446         { IT_DEMONKEY1, SPR_ART1 },
447         { IT_DEMONKEY2, SPR_ART2 },
448         { IT_DEMONKEY3, SPR_ART3 },
449         { IT_NONE, 0 }
450     };
451     uint                i;
452 
453     for (i = 0; items[i].type != IT_NONE; ++i)
454         if (items[i].sprite == sprite)
455             return items[i].type;
456 
457     return IT_NONE;
458 }
459 
460 /**
461  * @param plr           Player being given item.
462  * @param item          Type of item being given.
463  * @param dropped       @c true = the item was dropped by some entity.
464  *
465  * @return              @c true iff the item should be destroyed.
466  */
giveItem(player_t * plr,itemtype_t item,dd_bool dropped)467 static dd_bool giveItem(player_t* plr, itemtype_t item, dd_bool dropped)
468 {
469     if (!plr)
470         return false;
471 
472     switch (item)
473     {
474     case IT_ARMOR_GREEN:
475         if (!P_GiveArmor(plr, armorClass[0],
476                         armorPoints[MINMAX_OF(0, armorClass[0] - 1, 1)]))
477             return false;
478         P_SetMessage(plr, GOTARMOR);
479         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
480         break;
481 
482     case IT_ARMOR_BLUE:
483         if (!P_GiveArmor(plr, armorClass[1],
484                         armorPoints[MINMAX_OF(0, armorClass[1] - 1, 1)]))
485             return false;
486         P_SetMessage(plr, GOTMEGA);
487         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
488         break;
489 
490     case IT_ARMOR_BONUS:
491         if (!plr->armorType)
492             P_PlayerSetArmorType(plr, armorClass[0]);
493         if (plr->armorPoints < armorPoints[1])
494             P_PlayerGiveArmorBonus(plr, 2);
495 
496         P_SetMessage(plr, GOTARMBONUS);
497         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
498 
499         // Maybe unhide the HUD?
500         ST_HUDUnHide(plr - players, HUE_ON_PICKUP_ARMOR);
501         break;
502 
503     case IT_HEALTH_BONUS:
504         //plr->health++;       // Can go over 100%
505         plr->health += 2;      // jd64 Can go over 100%
506         if (plr->health > healthLimit)
507             plr->health = healthLimit;
508         plr->plr->mo->health = plr->health;
509         plr->update |= PSF_HEALTH;
510         P_SetMessage(plr, GOTHTHBONUS);
511         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
512 
513         // Maybe unhide the HUD?
514         ST_HUDUnHide(plr - players, HUE_ON_PICKUP_HEALTH);
515         break;
516 
517     case IT_HEALTH_SOULSPHERE:
518         plr->health += soulSphereHealth;
519         if (plr->health > soulSphereLimit)
520             plr->health = soulSphereLimit;
521         plr->plr->mo->health = plr->health;
522         plr->update |= PSF_HEALTH;
523         P_SetMessage(plr, GOTSUPER);
524         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
525 
526         // Maybe unhide the HUD?
527         ST_HUDUnHide(plr - players, HUE_ON_PICKUP_HEALTH);
528         break;
529 
530     case IT_MEGASPHERE:
531         plr->health = megaSphereHealth;
532         plr->plr->mo->health = plr->health;
533         plr->update |= PSF_HEALTH;
534         P_GiveArmor(plr, armorClass[1], armorPoints[MINMAX_OF(0, armorClass[1] - 1, 1)]);
535         P_SetMessage(plr, GOTMSPHERE);
536         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
537 
538         // Maybe unhide the HUD?
539         ST_HUDUnHide(plr - players, HUE_ON_PICKUP_HEALTH);
540         break;
541 
542     case IT_KEY_BLUE:
543         if (!plr->keys[KT_BLUECARD])
544             P_SetMessage(plr, GOTBLUECARD);
545         P_GiveKey(plr, KT_BLUECARD);
546         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
547         if (IS_NETGAME)
548             return false;
549         break;
550 
551     case IT_KEY_YELLOW:
552         if (!plr->keys[KT_YELLOWCARD])
553             P_SetMessage(plr, GOTYELWCARD);
554         P_GiveKey(plr, KT_YELLOWCARD);
555         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
556         if (IS_NETGAME)
557             return false;
558         break;
559 
560     case IT_KEY_RED:
561         if (!plr->keys[KT_REDCARD])
562             P_SetMessage(plr, GOTREDCARD);
563         P_GiveKey(plr, KT_REDCARD);
564         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
565         if (IS_NETGAME)
566             return false;
567         break;
568 
569     case IT_KEY_BLUESKULL:
570         if (!plr->keys[KT_BLUESKULL])
571             P_SetMessage(plr, GOTBLUESKUL);
572         P_GiveKey(plr, KT_BLUESKULL);
573         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
574         if (IS_NETGAME)
575             return false;
576         break;
577 
578     case IT_KEY_YELLOWSKULL:
579         if (!plr->keys[KT_YELLOWSKULL])
580             P_SetMessage(plr, GOTYELWSKUL);
581         P_GiveKey(plr, KT_YELLOWSKULL);
582         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
583         if (IS_NETGAME)
584             return false;
585         break;
586 
587     case IT_KEY_REDSKULL:
588         if (!plr->keys[KT_REDSKULL])
589             P_SetMessage(plr, GOTREDSKULL);
590         P_GiveKey(plr, KT_REDSKULL);
591         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
592         if (IS_NETGAME)
593             return false;
594         break;
595 
596     case IT_HEALTH_PACK:
597         if (!P_GiveBody(plr, 10))
598             return false;
599         P_SetMessage(plr, GOTSTIM);
600         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
601         break;
602 
603     case IT_HEALTH_KIT: {
604         int oldHealth = plr->health;
605 
606         /**
607          * DOOM bug:
608          * The following test was originaly placed AFTER the call to
609          * P_GiveBody thereby making the first outcome impossible as
610          * the medikit gives 25 points of health. This resulted that
611          * the GOTMEDINEED "Picked up a medikit that you REALLY need"
612          * was never used.
613          */
614         if (!P_GiveBody(plr, 25)) return false;
615 
616         P_SetMessage(plr, GET_TXT((oldHealth < 25)? TXT_GOTMEDINEED : TXT_GOTMEDIKIT));
617         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
618         break; }
619 
620     case IT_INVUL:
621         if (!P_GivePower(plr, PT_INVULNERABILITY))
622             return false;
623         P_SetMessage(plr, GOTINVUL);
624         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
625         break;
626 
627     case IT_BESERK:
628         if (!P_GivePower(plr, PT_STRENGTH))
629             return false;
630         P_SetMessage(plr, GOTBERSERK);
631         if (plr->readyWeapon != WT_FIRST && cfg.berserkAutoSwitch)
632         {
633             plr->pendingWeapon = WT_FIRST;
634             plr->update |= PSF_PENDING_WEAPON | PSF_READY_WEAPON;
635         }
636         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
637         break;
638 
639     case IT_INVIS:
640         if (!P_GivePower(plr, PT_INVISIBILITY))
641             return false;
642         P_SetMessage(plr, GOTINVIS);
643         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
644         break;
645 
646     case IT_SUIT:
647         if (!P_GivePower(plr, PT_IRONFEET))
648             return false;
649         P_SetMessage(plr, GOTSUIT);
650         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
651         break;
652 
653     case IT_ALLMAP:
654         if (!P_GivePower(plr, PT_ALLMAP))
655             return false;
656         P_SetMessage(plr, GOTMAP);
657         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
658         break;
659 
660     case IT_VISOR:
661         if (!P_GivePower(plr, PT_INFRARED))
662             return false;
663         P_SetMessage(plr, GOTVISOR);
664         S_ConsoleSound(SFX_GETPOW, NULL, plr - players);
665         break;
666 
667     case IT_AMMO_CLIP:
668         if (!P_GiveAmmo(plr, AT_CLIP, dropped? 0 : 1))
669             return false;
670         P_SetMessage(plr, GOTCLIP);
671         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
672         break;
673 
674     case IT_AMMO_CLIP_BOX:
675         if (!P_GiveAmmo(plr, AT_CLIP, 5))
676             return false;
677         P_SetMessage(plr, GOTCLIPBOX);
678         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
679         break;
680 
681     case IT_AMMO_ROCKET:
682         if (!P_GiveAmmo(plr, AT_MISSILE, 1))
683             return false;
684         P_SetMessage(plr, GOTROCKET);
685         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
686         break;
687 
688     case IT_AMMO_ROCKET_BOX:
689         if (!P_GiveAmmo(plr, AT_MISSILE, 5))
690             return false;
691         P_SetMessage(plr, GOTROCKBOX);
692         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
693         break;
694 
695     case IT_AMMO_CELL:
696         if (!P_GiveAmmo(plr, AT_CELL, 1))
697             return false;
698         P_SetMessage(plr, GOTCELL);
699         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
700         break;
701 
702     case IT_AMMO_CELL_BOX:
703         if (!P_GiveAmmo(plr, AT_CELL, 5))
704             return false;
705         P_SetMessage(plr, GOTCELLBOX);
706         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
707         break;
708 
709     case IT_AMMO_SHELL:
710         if (!P_GiveAmmo(plr, AT_SHELL, 1))
711             return false;
712         P_SetMessage(plr, GOTSHELLS);
713         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
714         break;
715 
716     case IT_AMMO_SHELL_BOX:
717         if (!P_GiveAmmo(plr, AT_SHELL, 5))
718             return false;
719         P_SetMessage(plr, GOTSHELLBOX);
720         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
721         break;
722 
723     case IT_BACKPACK:
724         P_GiveBackpack(plr);
725         S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
726         break;
727 
728     case IT_WEAPON_BFG:
729         if (!P_GiveWeapon(plr, WT_SEVENTH, dropped))
730             return false;
731         P_SetMessage(plr, GOTBFG9000);
732         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
733         break;
734 
735     case IT_WEAPON_CHAINGUN:
736         if (!P_GiveWeapon(plr, WT_FOURTH, dropped))
737             return false;
738         P_SetMessage(plr, GOTCHAINGUN);
739         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
740         break;
741 
742     case IT_WEAPON_CHAINSAW:
743         if (!P_GiveWeapon(plr, WT_EIGHTH, dropped))
744             return false;
745         P_SetMessage(plr, GOTCHAINSAW);
746         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
747         break;
748 
749     case IT_WEAPON_RLAUNCHER:
750         if (!P_GiveWeapon(plr, WT_FIFTH, dropped))
751             return false;
752         P_SetMessage(plr, GOTLAUNCHER);
753         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
754         break;
755 
756     case IT_WEAPON_PLASMARIFLE:
757         if (!P_GiveWeapon(plr, WT_SIXTH, dropped))
758             return false;
759         P_SetMessage(plr, GOTPLASMA);
760         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
761         break;
762 
763     case IT_WEAPON_SHOTGUN:
764         if (!P_GiveWeapon(plr, WT_THIRD, dropped))
765             return false;
766         P_SetMessage(plr, GOTSHOTGUN);
767         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
768         break;
769 
770     case IT_WEAPON_SSHOTGUN:
771         if (!P_GiveWeapon(plr, WT_NINETH, dropped))
772             return false;
773         P_SetMessage(plr, GOTSHOTGUN2);
774         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
775         break;
776 
777     case IT_WEAPON_LASERGUN:
778         if (!P_GiveWeapon(plr, WT_TENTH, dropped))
779             return false;
780 
781         P_SetMessage(plr, GOTUNMAKER);
782         S_ConsoleSound(SFX_WPNUP, NULL, plr - players);
783         break;
784 
785     case IT_DEMONKEY1:
786         if (P_InventoryCount(plr - players, IIT_DEMONKEY1))
787         {
788             if (!(mapTime & 0x1f))
789                 P_SetMessage(plr, NGOTPOWERUP1);
790             S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
791 
792             return false; //Don't destroy item, can be collected later by other players.
793         }
794         else
795         {
796             P_GiveItem(plr, IIT_DEMONKEY1);
797             P_SetMessage(plr, GOTPOWERUP1);
798             S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
799         }
800         break;
801 
802     case IT_DEMONKEY2:
803         if (P_InventoryCount(plr - players, IIT_DEMONKEY2))
804         {
805             if (!(mapTime & 0x1f))
806                 P_SetMessage(plr, NGOTPOWERUP2);
807             S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
808 
809             return false; //Don't destroy item, can be collected later by other players.
810         }
811         else
812         {
813             P_GiveItem(plr, IIT_DEMONKEY2);
814             P_SetMessage(plr, GOTPOWERUP2);
815             S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
816         }
817         break;
818 
819     case IT_DEMONKEY3:
820         if (P_InventoryCount(plr - players, IIT_DEMONKEY3))
821         {
822             if (!(mapTime & 0x1f))
823                 P_SetMessage(plr, NGOTPOWERUP3);
824 
825             S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
826             return false; //Don't destroy item, can be collected later by other players.
827         }
828         else
829         {
830             P_GiveItem(plr, IIT_DEMONKEY3);
831             P_SetMessage(plr, GOTPOWERUP3);
832             S_ConsoleSound(SFX_ITEMUP, NULL, plr - players);
833         }
834         break;
835 
836     default:
837         Con_Error("giveItem: Unknown item %i.", (int) item);
838     }
839 
840     return true;
841 }
842 
P_TouchSpecialMobj(mobj_t * special,mobj_t * toucher)843 void P_TouchSpecialMobj(mobj_t* special, mobj_t* toucher)
844 {
845     player_t* player;
846     coord_t delta;
847     itemtype_t item;
848 
849     delta = special->origin[VZ] - toucher->origin[VZ];
850     if (delta > toucher->height || delta < -8)
851     {
852         // Out of reach.
853         return;
854     }
855 
856     // Dead thing touching (can happen with a sliding player corpse).
857     if (toucher->health <= 0) return;
858 
859     player = toucher->player;
860 
861     // Identify by sprite.
862     if ((item = getItemTypeBySprite(special->sprite)) != IT_NONE)
863     {
864         if (!giveItem(player, item, (special->flags & MF_DROPPED)? true : false))
865             return; // Don't destroy the item.
866     }
867     else
868     {
869         App_Log(DE2_MAP_WARNING, "P_TouchSpecialMobj: Unknown gettable thing %i",
870                 (int) special->type);
871     }
872 
873     if (special->flags & MF_COUNTITEM)
874     {
875         player->itemCount++;
876         player->update |= PSF_COUNTERS;
877     }
878 
879     P_MobjRemove(special, false);
880 
881     //XXX doom plugin checks value of mapSetup
882     player->bonusCount += BONUSADD;
883 }
884 
P_KillMobj(mobj_t * source,mobj_t * target,dd_bool stomping)885 void P_KillMobj(mobj_t *source, mobj_t *target, dd_bool stomping)
886 {
887     mobjtype_t          item;
888     mobj_t*             mo;
889     unsigned int        an;
890     angle_t             angle;
891     statenum_t          state;
892 
893     if (!target) // nothing to kill
894         return;
895 
896     target->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY);
897 
898     if (target->type != MT_SKULL)
899         target->flags &= ~MF_NOGRAVITY;
900 
901     target->flags |= MF_CORPSE | MF_DROPOFF;
902     target->flags2 &= ~MF2_PASSMOBJ;
903     target->corpseTics = 0;
904     // target->height >>= 2; // jd64
905 
906     if (source && source->player)
907     {
908         // Count for intermission.
909         if (target->flags & MF_COUNTKILL)
910         {
911             source->player->killCount++;
912             source->player->update |= PSF_COUNTERS;
913         }
914 
915         if (target->player)
916         {
917             source->player->frags[target->player - players]++;
918             NetSv_FragsForAll(source->player);
919             NetSv_KillMessage(source->player, target->player, stomping);
920         }
921     }
922     else if (!IS_NETGAME && (target->flags & MF_COUNTKILL))
923     {
924         // Count all monster deaths, even those caused by other monsters.
925         players[0].killCount++;
926     }
927 
928     if (target->player)
929     {
930         // Count environment kills against the player.
931         if (!source)
932         {
933             target->player->frags[target->player - players]++;
934             NetSv_FragsForAll(target->player);
935             NetSv_KillMessage(target->player, target->player, stomping);
936         }
937 
938         target->flags &= ~MF_SOLID;
939         target->flags2 &= ~MF2_FLY;
940         target->player->powers[PT_FLIGHT] = 0;
941         target->player->playerState = PST_DEAD;
942         target->player->rebornWait = PLAYER_REBORN_TICS;
943         target->player->update |= PSF_STATE;
944         target->player->plr->flags |= DDPF_DEAD;
945         P_DropWeapon(target->player);
946 
947         // Don't die with the automap open.
948         ST_CloseAll(target->player - players, false);
949     }
950 
951     if ((state = P_GetState(target->type, SN_XDEATH)) != S_NULL &&
952        target->health < -target->info->spawnHealth)
953     {   // Extreme death.
954         P_MobjChangeState(target, state);
955     }
956     else
957     {   // Normal death.
958         P_MobjChangeState(target, P_GetState(target->type, SN_DEATH));
959     }
960 
961     target->tics -= P_Random() & 3;
962 
963     if (target->tics < 1)
964         target->tics = 1;
965 
966     // Drop stuff.
967     // This determines the kind of object spawned during the death frame
968     // of a thing.
969     switch (target->type)
970     {
971     case MT_POSSESSED:
972     //case MT_TROOP: // jd64
973         item = MT_CLIP;
974         break;
975 
976     case MT_SHOTGUY:
977         item = MT_SHOTGUN;
978         break;
979 
980     default:
981         return;
982     }
983 
984     // Don't drop at the exact same place, causes Z flickering with
985     // 3D sprites.
986     angle = P_Random() << 24;
987     an = angle >> ANGLETOFINESHIFT;
988     if ((mo = P_SpawnMobjXYZ(item, target->origin[VX] + 3 * FIX2FLT(finecosine[an]),
989                                   target->origin[VY] + 3 * FIX2FLT(finesine[an]),
990                                   0, angle, MSF_Z_FLOOR)))
991     {
992         mo->flags |= MF_DROPPED; // Special versions of items.
993     }
994 }
995 
P_DamageMobj(mobj_t * target,mobj_t * inflictor,mobj_t * source,int damageP,dd_bool stomping)996 int P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source,
997     int damageP, dd_bool stomping)
998 {
999     return P_DamageMobj2(target, inflictor, source, damageP, stomping, false);
1000 }
1001 
1002 /**
1003  * Damages both enemies and players
1004  * Source and inflictor are the same for melee attacks.
1005  * Source can be NULL for slime, barrel explosions
1006  * and other environmental stuff.
1007  *
1008  * @param inflictor     Mobj that caused the damage creature or missile,
1009  *                      can be NULL (slime, etc).
1010  * @param source        Mobj to target after taking damage, creature or NULL.
1011  *
1012  * @return              Actual amount of damage done.
1013  */
P_DamageMobj2(mobj_t * target,mobj_t * inflictor,mobj_t * source,int damageP,dd_bool stomping,dd_bool skipNetworkCheck)1014 int P_DamageMobj2(mobj_t *target, mobj_t *inflictor, mobj_t *source,
1015     int damageP, dd_bool stomping, dd_bool skipNetworkCheck)
1016 {
1017 // Follow a player exlusively for 3 seconds.
1018 #define BASETHRESHOLD           (100)
1019 
1020     uint an;
1021     angle_t angle;
1022     int saved;
1023     player_t *player;
1024     float thrust;
1025     //int temp;
1026     int originalHealth;
1027 
1028     // The actual damage (== damageP * netMobDamageModifier for any non-player mobj).
1029     int damage = damageP;
1030 
1031     if (!target)
1032         return 0; // Wha?
1033 
1034     originalHealth = target->health;
1035 
1036     if (!skipNetworkCheck)
1037     {
1038         // Clients can't harm anybody.
1039         if (IS_CLIENT)
1040             return 0;
1041     }
1042 
1043     if (!(target->flags & MF_SHOOTABLE))
1044         return 0; // Shouldn't happen...
1045 
1046     if (target->health <= 0)
1047         return 0;
1048 
1049     // Player specific.
1050     if (target->player)
1051     {
1052         // Check if player-player damage is disabled.
1053         if (source && source->player && source->player != target->player)
1054         {
1055             // Co-op damage disabled?
1056             if (IS_NETGAME && !gfw_Rule(deathmatch) && cfg.noCoopDamage)
1057                 return 0;
1058 
1059             // Same color, no damage?
1060             if (cfg.noTeamDamage &&
1061                cfg.playerColor[target->player - players] ==
1062                cfg.playerColor[source->player - players])
1063                 return 0;
1064         }
1065     }
1066 
1067     if (target->flags & MF_SKULLFLY)
1068     {
1069         target->mom[MX] = target->mom[MY] = target->mom[MZ] = 0;
1070     }
1071 
1072     player = target->player;
1073     if (player && gfw_Rule(skill) == SM_BABY)
1074         damage >>= 1; // take half damage in trainer mode
1075 
1076     // jd64 >
1077 #if 0
1078     if (inflictor && inflictor->type == MT_FIREEND)
1079     {   // Special for Motherdemon attack
1080 #if 0
1081         /** DJS - This was originally in a sub routine called P_TouchMotherFire
1082         *       but due to the fact that @c player, was not initialized
1083         *       this likely does not work the way kaiser expected it to.
1084         *       What would actually happen is not certain but I would guess it
1085         *       would most likely simply return without doing anything at all.
1086         * \todo SHOULD this be fixed? Or is something implemented elsewhere
1087         *       which does what this was attempting to do?
1088         */
1089         int         damage;
1090         player_t   *player;
1091 
1092         if (player = target->player)
1093         {
1094             damage = ((P_Random() % 10) + 1) * 8;
1095 
1096             P_DamageMobj(target, NULL, NULL, damage);
1097             player->plr->mo->momz = 16;
1098             player->jumpTics = 24;
1099         }
1100 #endif
1101         return;
1102     }
1103 #endif
1104     // < d64tc
1105 
1106     // Use the cvar damage multiplier netMobDamageModifier only if the
1107     // inflictor is not a player.
1108     if (inflictor && !inflictor->player &&
1109        (!source || (source && !source->player)))
1110     {
1111         // damage = (int) ((float) damage * netMobDamageModifier);
1112         if (IS_NETGAME)
1113             damage *= cfg.common.netMobDamageModifier;
1114     }
1115 
1116     // Some close combat weapons should not inflict thrust and push the
1117     // victim out of reach, thus kick away unless using the chainsaw.
1118     if (inflictor && !(target->flags & MF_NOCLIP) &&
1119        (!source || !source->player ||
1120         source->player->readyWeapon != WT_EIGHTH) &&
1121        !(inflictor->flags2 & MF2_NODMGTHRUST))
1122     {
1123         angle = M_PointToAngle2(inflictor->origin, target->origin);
1124 
1125         thrust = FIX2FLT(damage * (FRACUNIT>>3) * 100 / target->info->mass);
1126 
1127         // Make fall forwards sometimes.
1128         if (damage < 40 && damage > target->health &&
1129            target->origin[VZ] - inflictor->origin[VZ] > 64 && (P_Random() & 1))
1130         {
1131             angle += ANG180;
1132             thrust *= 4;
1133         }
1134 
1135         an = angle >> ANGLETOFINESHIFT;
1136         target->mom[MX] += thrust * FIX2FLT(finecosine[an]);
1137         target->mom[MY] += thrust * FIX2FLT(finesine[an]);
1138         NetSv_PlayerMobjImpulse(target, thrust * FIX2FLT(finecosine[an]), thrust * FIX2FLT(finesine[an]), 0);
1139 
1140         // $dropoff_fix: thrust objects hanging off ledges.
1141         if (target->intFlags & MIF_FALLING && target->gear >= MAXGEAR)
1142             target->gear = 0;
1143     }
1144 
1145     // Player specific.
1146     if (player)
1147     {
1148         // End of game hell hack.
1149         if (P_ToXSector(Mobj_Sector(target))->special == 11 &&
1150            damage >= target->health)
1151         {
1152             damage = target->health - 1;
1153         }
1154 
1155         // Below certain threshold, ignore damage in GOD mode, or with
1156         // INVUL power.
1157         if (damage < 1000 &&
1158            ((P_GetPlayerCheats(player) & CF_GODMODE) ||
1159             player->powers[PT_INVULNERABILITY]))
1160         {
1161             return 0;
1162         }
1163 
1164         if (player->armorType)
1165         {
1166             if (player->armorType == 1)
1167                 saved = damage / 3;
1168             else
1169                 saved = damage / 2;
1170 
1171             if (player->armorPoints <= saved)
1172             {
1173                 // Armor is used up.
1174                 saved = player->armorPoints;
1175                 player->armorType = 0;
1176             }
1177 
1178             player->armorPoints -= saved;
1179             player->update |= PSF_ARMOR_POINTS;
1180             damage -= saved;
1181         }
1182 
1183         player->health -= damage;
1184         if (player->health < 0)
1185             player->health = 0;
1186         player->update |= PSF_HEALTH;
1187 
1188         player->attacker = source;
1189         player->damageCount += damage; // Add damage after armor / invuln.
1190 
1191         if (player->damageCount > 100)
1192             player->damageCount = 100; // Teleport stomp does 10k points...
1193 
1194         // temp = damage < 100 ? damage : 100; Unused?
1195 
1196         // Maybe unhide the HUD?
1197         ST_HUDUnHide(player - players, HUE_ON_DAMAGE);
1198     }
1199 
1200     Mobj_InflictDamage(target, inflictor, damage);
1201 
1202     if (target->health > 0)
1203     {   // Still alive, phew!
1204         if ((P_Random() < target->info->painChance) &&
1205            !(target->flags & MF_SKULLFLY))
1206         {
1207             statenum_t          state;
1208 
1209             target->flags |= MF_JUSTHIT; // Fight back!
1210 
1211             if ((state = P_GetState(target->type, SN_PAIN)) != S_NULL)
1212                 P_MobjChangeState(target, state);
1213         }
1214 
1215         target->reactionTime = 0; // We're awake now...
1216 
1217         if (source &&
1218            (!target->threshold && !(source->flags3 & MF3_NOINFIGHT)) &&
1219            source != target)
1220         {
1221             statenum_t          state;
1222 
1223             // If not intent on another player, chase after this one.
1224             target->target = source;
1225             target->threshold = BASETHRESHOLD;
1226 
1227             if ((state = P_GetState(target->type, SN_SEE)) != S_NULL &&
1228                target->state == &STATES[P_GetState(target->type, SN_SPAWN)])
1229                 P_MobjChangeState(target, state);
1230         }
1231     }
1232     else
1233     {
1234         P_KillMobj(source, target, stomping);
1235     }
1236 
1237     return originalHealth - target->health;
1238 
1239 #undef BASETHRESHOLD
1240 }
1241