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