1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 // Handling interactions (i.e., collisions).
17 //
18
19 // Data.
20 #include "doomdef.h"
21 #include "dstrings.h"
22 #include "sounds.h"
23 #include "deh_main.h"
24 #include "deh_misc.h"
25 #include "doomstat.h"
26 #include "m_misc.h"
27 #include "m_random.h"
28 #include "i_system.h"
29 #include "am_map.h"
30 #include "p_local.h"
31 #include "p_dialog.h" // villsa [STRIFE]
32 #include "s_sound.h"
33 #include "p_inter.h"
34
35 #include "hu_stuff.h" // villsa [STRIFE]
36 #include "z_zone.h" // villsa [STRIFE]
37
38 // haleyjd [STRIFE]
39 #include "w_wad.h"
40 #include "p_pspr.h"
41 #include "p_dialog.h"
42 #include "f_finale.h"
43
44
45 #define BONUSADD 6
46
47
48 // a weapon is found with two clip loads,
49 // a big item has five clip loads
50 // villsa [STRIFE] updated arrays
51 int maxammo[NUMAMMO] = { 250, 50, 25, 400, 100, 30, 16 };
52 int clipammo[NUMAMMO] = { 10, 4, 2, 20, 4, 6, 4 };
53
54
55 //
56 // GET STUFF
57 //
58
59 //
60 // P_GiveAmmo
61 // Num is the number of clip loads,
62 // not the individual count (0= 1/2 clip).
63 // Returns false if the ammo can't be picked up at all
64 //
65 // [STRIFE] Modified for Strife ammo types
66 //
P_GiveAmmo(player_t * player,ammotype_t ammo,int num)67 boolean P_GiveAmmo(player_t* player, ammotype_t ammo, int num)
68 {
69 int oldammo;
70
71 if(ammo == am_noammo)
72 return false;
73
74 if(ammo > NUMAMMO)
75 I_Error ("P_GiveAmmo: bad type %i", ammo);
76
77 if(player->ammo[ammo] == player->maxammo[ammo])
78 return false;
79
80 if(num)
81 num *= clipammo[ammo];
82 else
83 num = clipammo[ammo]/2;
84
85 if(gameskill == sk_baby
86 || gameskill == sk_nightmare)
87 {
88 // give double ammo in trainer mode,
89 // you'll need in nightmare
90 num <<= 1;
91 }
92
93 oldammo = player->ammo[ammo];
94 player->ammo[ammo] += num;
95
96 if(player->ammo[ammo] > player->maxammo[ammo])
97 player->ammo[ammo] = player->maxammo[ammo];
98
99 // If non zero ammo,
100 // don't change up weapons,
101 // player was lower on purpose.
102 if(oldammo)
103 return true;
104
105 // We were down to zero,
106 // so select a new weapon.
107 // Preferences are not user selectable.
108
109 // villsa [STRIFE] ammo update
110 // where's the check for grenades? - haleyjd: verified no switch to grenades
111 // haleyjd 10/03/10: don't change to electric bow when picking up poison
112 // arrows.
113 if(!player->readyweapon)
114 {
115 switch(ammo)
116 {
117 case am_bullets:
118 if(player->weaponowned[wp_rifle])
119 player->pendingweapon = wp_rifle;
120 break;
121
122 case am_elecbolts:
123 if(player->weaponowned[wp_elecbow])
124 player->pendingweapon = wp_elecbow;
125 break;
126
127 case am_cell:
128 if(player->weaponowned[wp_mauler])
129 player->pendingweapon = wp_mauler;
130 break;
131
132 case am_missiles:
133 if(player->weaponowned[wp_missile])
134 player->pendingweapon = wp_missile;
135 break;
136
137 default:
138 break;
139 }
140 }
141
142 return true;
143 }
144
145
146 //
147 // P_GiveWeapon
148 // The weapon name may have a MF_DROPPED flag ored in.
149 //
150 // villsa [STRIFE] some stuff has been changed/moved around
151 //
P_GiveWeapon(player_t * player,weapontype_t weapon,boolean dropped)152 boolean P_GiveWeapon(player_t* player, weapontype_t weapon, boolean dropped)
153 {
154 boolean gaveammo;
155 boolean gaveweapon;
156
157 // villsa [STRIFE] new code for giving alternate version
158 // of the weapon to player
159 if(player->weaponowned[weapon])
160 gaveweapon = false;
161 else
162 {
163 gaveweapon = true;
164 player->weaponowned[weapon] = true;
165
166 // Alternate "sister" weapons that you also get as a bonus:
167 switch(weapon)
168 {
169 case wp_elecbow:
170 player->weaponowned[wp_poisonbow] = true;
171 break;
172
173 case wp_hegrenade:
174 player->weaponowned[wp_wpgrenade] = true;
175 break;
176
177 case wp_mauler:
178 player->weaponowned[wp_torpedo] = true;
179 break;
180
181 default:
182 break;
183 }
184
185 // check for the standard weapons only
186 if(weapon > player->readyweapon && weapon <= wp_sigil)
187 player->pendingweapon = weapon;
188
189 }
190
191 if(netgame && (deathmatch != 2) && !dropped)
192 {
193 // leave placed weapons forever on net games
194 if(!gaveweapon)
195 return false;
196
197 player->bonuscount += BONUSADD;
198 player->weaponowned[weapon] = true;
199
200 if(deathmatch)
201 P_GiveAmmo(player, weaponinfo[weapon].ammo, 5);
202 else
203 P_GiveAmmo(player, weaponinfo[weapon].ammo, 2);
204
205 if(player == &players[consoleplayer])
206 S_StartSound (NULL, sfx_wpnup);
207 return false;
208 }
209
210 if(weaponinfo[weapon].ammo != am_noammo)
211 {
212 // give one clip with a dropped weapon,
213 // two clips with a found weapon
214 if(dropped)
215 gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1);
216 else
217 gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
218 }
219 else
220 gaveammo = false;
221
222 return(gaveweapon || gaveammo);
223 }
224
225
226
227 //
228 // P_GiveBody
229 // Returns false if the body isn't needed at all
230 //
231 // villsa [STRIFE] a lot of changes have been added for stamina
232 //
P_GiveBody(player_t * player,int num)233 boolean P_GiveBody(player_t* player, int num)
234 {
235 int maxhealth;
236 int healing;
237
238 maxhealth = MAXHEALTH + player->stamina;
239
240 if(num >= 0) // haleyjd 20100923: fixed to give proper amount of health
241 {
242 mobj_t *mo; // haleyjd 20110225: needed below...
243
244 // any healing to do?
245 if(player->health >= maxhealth)
246 return false;
247
248 // give, and cap to maxhealth
249 player->health += num;
250 if(player->health >= maxhealth)
251 player->health = maxhealth;
252
253 // Set mo->health for consistency.
254 // haleyjd 20110225: Seems Strife can call this on a NULL player->mo
255 // when giving items to players that are not in the game...
256 mo = P_SubstNullMobj(player->mo);
257 mo->health = player->health;
258 }
259 else
260 {
261 // [STRIFE] handle healing from the Front's medic
262 // The amount the player's health will be set to scales up with stamina
263 // increases.
264 // Ex 1: On the wimpiest skill level, -100 is sent in. This restores
265 // full health no matter what your stamina.
266 // (100*100)/100 = 100
267 // (200*100)/100 = 200
268 // Ex 2: On the most stringent skill levels, -50 is sent in. This will
269 // restore at most half of your health.
270 // (100*50)/100 = 50
271 // (200*50)/100 = 100
272 healing = (-num * maxhealth) / MAXHEALTH;
273
274 // This is also the "threshold" of healing. You need less health than
275 // the amount that will be restored in order to get any benefit.
276 // So on the easiest skill you will always be fully healed.
277 // On the hardest skill you must have less than 50 health, and will
278 // only recover to 50 (assuming base stamina stat)
279 if(player->health >= healing)
280 return false;
281
282 // Set health. BUG: Oddly, mo->health is NOT set here...
283 player->health = healing;
284 }
285
286 return true;
287 }
288
289
290
291 //
292 // P_GiveArmor
293 // Returns false if the armor is worse
294 // than the current armor.
295 //
296 // [STRIFE] Modified for Strife armor items
297 //
P_GiveArmor(player_t * player,int armortype)298 boolean P_GiveArmor(player_t* player, int armortype)
299 {
300 int hits;
301
302 // villsa [STRIFE]
303 if(armortype < 0)
304 {
305 if(player->armorpoints)
306 return false;
307
308 armortype = -armortype;
309 }
310
311 hits = armortype * 100;
312 if(player->armorpoints >= hits)
313 return false; // don't pick up
314
315 player->armortype = armortype;
316 player->armorpoints = hits;
317
318 return true;
319 }
320
321
322
323 //
324 // P_GiveCard
325 //
326 // [STRIFE] Modified to use larger bonuscount
327 //
P_GiveCard(player_t * player,card_t card)328 boolean P_GiveCard(player_t* player, card_t card)
329 {
330 if (player->cards[card])
331 return false;
332
333 // villsa [STRIFE] multiply by 2
334 player->bonuscount = BONUSADD * 2;
335 player->cards[card] = true;
336
337 return true;
338 }
339
340
341 //
342 // P_GivePower
343 //
344 // [STRIFE] Modifications for new powerups
345 //
P_GivePower(player_t * player,powertype_t power)346 boolean P_GivePower(player_t* player, powertype_t power)
347 {
348 // haleyjd 09/14/10: [STRIFE] moved to top, exception for Shadow Armor
349 if(player->powers[power] && power != pw_invisibility)
350 return false; // already got it
351
352 // if giving pw_invisibility and player already has MVIS, no can do.
353 if(power == pw_invisibility && (player->mo->flags & MF_MVIS))
354 return false;
355
356 // villsa [STRIFE]
357 if(power == pw_targeter)
358 {
359 player->powers[power] = TARGTICS;
360 P_SetPsprite(player, ps_targcenter, S_TRGT_00); // 10
361 P_SetPsprite(player, ps_targleft, S_TRGT_01); // 11
362 P_SetPsprite(player, ps_targright, S_TRGT_02); // 12
363
364 player->psprites[ps_targcenter].sx = (160*FRACUNIT);
365 player->psprites[ps_targleft ].sy = (100*FRACUNIT);
366 player->psprites[ps_targcenter].sy = (100*FRACUNIT);
367 player->psprites[ps_targright ].sy = (100*FRACUNIT);
368 return true;
369 }
370
371 if(power == pw_invisibility)
372 {
373 // if player already had this power...
374 if(player->powers[power])
375 {
376 // remove SHADOW, give MVIS.
377 player->mo->flags &= ~MF_SHADOW;
378 player->mo->flags |= MF_MVIS;
379 }
380 else // give SHADOW
381 player->mo->flags |= MF_SHADOW;
382
383 // set tics if giving shadow, or renew them if MVIS.
384 player->powers[power] = INVISTICS;
385
386 return true;
387 }
388
389 if(power == pw_ironfeet)
390 {
391 player->powers[power] = IRONTICS;
392 return true;
393 }
394
395 if(power == pw_strength)
396 {
397 P_GiveBody(player, 100);
398 player->powers[power] = 1;
399 return true;
400 }
401
402 // villsa [STRIFE]
403 if(power == pw_allmap)
404 {
405 // remember in mapstate
406 if(gamemap < 40)
407 player->mapstate[gamemap] = true;
408
409 player->powers[power] = 1;
410 return true;
411 }
412
413 // villsa [STRIFE]
414 if(power == pw_communicator)
415 {
416 player->powers[power] = 1;
417 return true;
418 }
419
420 // default behavior:
421 player->powers[power] = 1;
422 return true;
423 }
424
425
426 // villsa [STRIFE]
427 static char pickupmsg[80];
428
429 //
430 // P_TouchSpecialThing
431 //
432 // [STRIFE] Rewritten for Strife collectables.
433 //
P_TouchSpecialThing(mobj_t * special,mobj_t * toucher)434 void P_TouchSpecialThing(mobj_t* special, mobj_t* toucher)
435 {
436 player_t* player;
437 int i;
438 fixed_t delta;
439 int sound;
440
441 delta = special->z - toucher->z;
442
443 if(delta > toucher->height || delta < -8*FRACUNIT)
444 return; // out of reach
445
446 sound = sfx_itemup;
447 player = toucher->player;
448
449 // Dead thing touching.
450 // Can happen with a sliding player corpse.
451 if(toucher->health <= 0)
452 return;
453
454 // villsa [STRIFE] damage toucher if special is spectral
455 // haleyjd 09/21/10: corrected to test for SPECTRE thingtypes specifically
456 switch(special->type)
457 {
458 case MT_SPECTRE_A:
459 case MT_SPECTRE_B:
460 case MT_SPECTRE_C:
461 case MT_SPECTRE_D:
462 case MT_SPECTRE_E:
463 case MT_ENTITY:
464 case MT_SUBENTITY:
465 P_DamageMobj(toucher, NULL, NULL, 5);
466 return;
467 default:
468 break;
469 }
470
471 // villsa [STRIFE]
472 pickupmsg[0] = 0;
473
474 // Identify by sprite.
475 // villsa [STRIFE] new items
476 switch(special->sprite)
477 {
478 // bullets
479 case SPR_BLIT: // haleyjd: fixed missing MF_DROPPED check
480 if(!P_GiveAmmo(player, am_bullets, !(special->flags & MF_DROPPED)))
481 return;
482 break;
483
484 // box of bullets
485 case SPR_BBOX:
486 if(!P_GiveAmmo(player, am_bullets, 5))
487 return;
488 break;
489
490 // missile
491 case SPR_MSSL:
492 if(!P_GiveAmmo(player, am_missiles, 1))
493 return;
494 break;
495
496 // box of missiles
497 case SPR_ROKT:
498 if(!P_GiveAmmo(player, am_missiles, 5))
499 return;
500 break;
501
502 // battery
503 case SPR_BRY1:
504 if(!P_GiveAmmo(player, am_cell, 1))
505 return;
506 break;
507
508 // cell pack
509 case SPR_CPAC:
510 if(!P_GiveAmmo(player, am_cell, 5))
511 return;
512 break;
513
514 // poison bolts
515 case SPR_PQRL:
516 if(!P_GiveAmmo(player, am_poisonbolts, 5))
517 return;
518 break;
519
520 // electric bolts
521 case SPR_XQRL:
522 if(!P_GiveAmmo(player, am_elecbolts, 5))
523 return;
524 break;
525
526 // he grenades
527 case SPR_GRN1:
528 if(!P_GiveAmmo(player, am_hegrenades, 1))
529 return;
530 break;
531
532 // wp grenades
533 case SPR_GRN2:
534 if(!P_GiveAmmo(player, am_wpgrenades, 1))
535 return;
536 break;
537
538 // rifle
539 case SPR_RIFL:
540 if(!P_GiveWeapon(player, wp_rifle, (special->flags & MF_DROPPED) != 0))
541 return;
542 sound = sfx_wpnup; // haleyjd: SHK-CHK!
543 break;
544
545 // flame thrower
546 case SPR_FLAM:
547 if(!P_GiveWeapon(player, wp_flame, false))
548 return;
549 // haleyjd: gives extra ammo.
550 P_GiveAmmo(player, am_cell, 3);
551 sound = sfx_wpnup; // haleyjd: SHK-CHK!
552 break;
553
554 // missile launcher
555 case SPR_MMSL:
556 if(!P_GiveWeapon(player, wp_missile, false))
557 return;
558 sound = sfx_wpnup; // haleyjd: SHK-CHK!
559 break;
560
561 // grenade launcher
562 case SPR_GRND:
563 if(!P_GiveWeapon(player, wp_hegrenade,
564 (special->flags & MF_DROPPED) != 0))
565 return;
566 sound = sfx_wpnup; // haleyjd: SHK-CHK!
567 break;
568
569 // mauler
570 case SPR_TRPD:
571 if(!P_GiveWeapon(player, wp_mauler, false))
572 return;
573 sound = sfx_wpnup; // haleyjd: SHK-CHK!
574 break;
575
576 // electric bolt crossbow
577 case SPR_CBOW:
578 if(!P_GiveWeapon(player, wp_elecbow,
579 (special->flags & MF_DROPPED) != 0))
580 return;
581 sound = sfx_wpnup; // haleyjd: SHK-CHK!
582 break;
583
584 // haleyjd 09/21/10: missed case: THE SIGIL!
585 case SPR_SIGL:
586 if(!P_GiveWeapon(player, wp_sigil, (special->flags & MF_DROPPED) != 0))
587 {
588 player->sigiltype = special->frame;
589 return;
590 }
591
592 if(netgame)
593 player->sigiltype = 4;
594
595 player->pendingweapon = wp_sigil;
596 player->st_update = true;
597 if(deathmatch)
598 return;
599 sound = sfx_wpnup;
600 break;
601
602 // backpack
603 case SPR_BKPK:
604 if(!player->backpack)
605 {
606 for(i = 0; i < NUMAMMO; i++)
607 player->maxammo[i] *= 2;
608
609 player->backpack = true;
610 }
611 for(i = 0; i < NUMAMMO; i++)
612 P_GiveAmmo(player, i, 1);
613 break;
614
615 // 1 Gold
616 case SPR_COIN:
617 P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
618 break;
619
620 // 10 Gold
621 case SPR_CRED:
622 for(i = 0; i < 10; i++)
623 P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
624 break;
625
626 // 25 Gold
627 case SPR_SACK:
628 // haleyjd 09/21/10: missed code: if a SPR_SACK object has negative
629 // health, it will give that much gold - STRIFE-TODO: verify
630 if(special->health < 0)
631 {
632 for(i = special->health; i != 0; i++)
633 P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
634 }
635 else
636 {
637 for(i = 0; i < 25; i++)
638 P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
639 }
640 break;
641
642 // 50 Gold
643 case SPR_CHST:
644 for(i = 0; i < 50; i++)
645 P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
646 break;
647
648 // Leather Armor
649 case SPR_ARM1:
650 if(!P_GiveArmor(player, -2))
651 if(!P_GiveInventoryItem(player, special->sprite, special->type))
652 pickupmsg[0] = '!';
653 break;
654
655 // Metal Armor
656 case SPR_ARM2:
657 if(!P_GiveArmor(player, -1))
658 if(!P_GiveInventoryItem(player, special->sprite, special->type))
659 pickupmsg[0] = '!';
660 break;
661
662 // All-map powerup
663 case SPR_PMAP:
664 if(!P_GivePower(player, pw_allmap))
665 return;
666 sound = sfx_yeah;
667 break;
668
669 // The Comm Unit - because you need Blackbird whining in your ear the
670 // whole time and telling you how lost she is :P
671 case SPR_COMM:
672 if(!P_GivePower(player, pw_communicator))
673 return;
674 sound = sfx_yeah;
675 break;
676
677 // haleyjd 09/21/10: missed case - Shadow Armor; though, I do not know why
678 // this has a case distinct from generic inventory items... Maybe it started
679 // out as an auto-use-if-possible item much like regular armor...
680 case SPR_SHD1:
681 if(!P_GiveInventoryItem(player, SPR_SHD1, special->type))
682 pickupmsg[0] = '!';
683 break;
684
685 // villsa [STRIFE] check default items
686 case SPR_TOKN:
687 default:
688 if(special->type >= MT_KEY_BASE && special->type <= MT_NEWKEY5)
689 {
690 // haleyjd 09/21/10: Strife player still picks up keys that
691 // he has already found. (break, not return)
692 if(!P_GiveCard(player, special->type - MT_KEY_BASE))
693 break;
694 }
695 else
696 {
697 if(!P_GiveInventoryItem(player, special->sprite, special->type))
698 pickupmsg[0] = '!';
699 }
700 break;
701 }
702
703 // villsa [STRIFE] set message
704 if(!pickupmsg[0])
705 {
706 if(special->info->name)
707 {
708 DEH_snprintf(pickupmsg, sizeof(pickupmsg),
709 "You picked up the %s.", DEH_String(special->info->name));
710 }
711 else
712 DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You picked up the item.");
713 }
714 // use the first character to indicate that the player is full on items
715 else if(pickupmsg[0] == '!')
716 {
717 DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You cannot hold any more.");
718 player->message = pickupmsg;
719 return;
720 }
721
722 if(special->flags & MF_GIVEQUEST)
723 {
724 // [STRIFE]: Award quest flag based on the thing's speed. Quest 8 was
725 // apparently at some point given by the Broken Power Coupling, which is
726 // why they don't want to award it if you have Quest 6 (which is
727 // acquired by destroying the Front's working power coupling). BUT, the
728 // broken coupling object's speed is NOT 8... it is 512*FRACUNIT. For
729 // strict portability beyond the x86, we need to AND the operand by 31.
730 if(special->info->speed != 8 || !(player->questflags & QF_QUEST6))
731 player->questflags |= 1 << ((special->info->speed - 1) & 31);
732 }
733
734
735 // haleyjd 08/30/10: [STRIFE] No itemcount
736 //if (special->flags & MF_COUNTITEM)
737 // player->itemcount++;
738
739 P_RemoveMobj(special);
740 player->message = pickupmsg;
741 player->bonuscount += BONUSADD;
742
743 if(player == &players[consoleplayer])
744 S_StartSound(NULL, sound);
745 }
746
747 // villsa [STRIFE]
748 static char plrkilledmsg[80];
749
750 //
751 // KillMobj
752 //
753 // [STRIFE] Major modifications for drop types, no tic randomization, etc.
754 //
P_KillMobj(mobj_t * source,mobj_t * target)755 void P_KillMobj(mobj_t* source, mobj_t* target)
756 {
757 mobjtype_t item;
758 mobj_t* mo;
759 line_t junk;
760 int i;
761
762 // villsa [STRIFE] corpse and dropoff are removed, but why when these two flags
763 // are set a few lines later? watcom nonsense perhaps?
764 target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_BOUNCE|MF_CORPSE|MF_DROPOFF);
765
766 // villsa [STRIFE] unused
767 /*
768 if (target->type != MT_SKULL)
769 target->flags &= ~MF_NOGRAVITY;
770 */
771
772 target->flags |= MF_CORPSE|MF_DROPOFF;
773 target->height = FRACUNIT; // villsa [STRIFE] set to fracunit instead of >>= 2
774
775 if(source && source->player)
776 {
777 // count for intermission
778 if(target->flags & MF_COUNTKILL)
779 source->player->killcount++;
780
781 if(target->player)
782 {
783 source->player->frags[target->player-players]++;
784
785 // villsa [STRIFE] new messages when fragging players
786 // haleyjd 20141024: corrected; uses player->allegiance, not mo->miscdata
787 DEH_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
788 "%s killed %s",
789 player_names[source->player->allegiance],
790 player_names[target->player->allegiance]);
791
792 if(netgame)
793 players[consoleplayer].message = plrkilledmsg;
794 }
795 }
796 else if(!netgame && (target->flags & MF_COUNTKILL))
797 {
798 // count all monster deaths,
799 // even those caused by other monsters
800 players[0].killcount++;
801 }
802
803 if(target->player)
804 {
805 // count environment kills against you
806 if(!source)
807 target->player->frags[target->player-players]++;
808
809 if(gamemap == 29 && !netgame)
810 {
811 // haleyjd 09/13/10: [STRIFE] Give player the bad ending.
812 F_StartFinale();
813 return;
814 }
815
816 // villsa [STRIFE] spit out inventory items when killed
817 if(netgame)
818 {
819 int amount = 0;
820 mobj_t* loot;
821 int r = 0;
822
823 while(1)
824 {
825 if(target->player->inventory[0].amount <= 0)
826 break;
827
828 item = target->player->inventory[0].type;
829 if(item == MT_MONY_1)
830 {
831 loot = P_SpawnMobj(target->x, target->y,
832 target->z + (24*FRACUNIT), MT_MONY_25);
833
834 // [STRIFE] TODO - what the hell is it doing here?
835 loot->health = target->player->inventory[0].amount;
836 loot->health = -target->player->inventory[0].amount;
837
838 amount = target->player->inventory[0].amount;
839 }
840 else
841 {
842 loot = P_SpawnMobj(target->x, target->y,
843 target->z + (24*FRACUNIT), item);
844 amount = 1;
845 }
846
847 P_RemoveInventoryItem(target->player, 0, amount);
848 r = P_Random();
849 loot->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS;
850 loot->momy += ((P_Random() & 7) + 1) << FRACBITS;
851 loot->flags |= MF_DROPPED;
852 }
853 }
854
855 //target->flags &= ~MF_SOLID;
856 target->player->playerstate = PST_DEAD;
857 target->player->mo->momz += 5*FRACUNIT; // [STRIFE]: small hop!
858 P_DropWeapon(target->player);
859
860 if(target->player == &players[consoleplayer]
861 && automapactive)
862 {
863 // don't die in auto map,
864 // switch view prior to dying
865 AM_Stop ();
866 }
867
868 }
869
870 // villsa [STRIFE] some modifications to setting states
871 if(target->state != &states[S_BURN_23])
872 {
873 if(target->health == -6666)
874 P_SetMobjState(target, S_DISR_00); // 373
875 else
876 {
877 // haleyjd [STRIFE] 20160111: Rogue changed check from < to <=
878 if(target->health <= -target->info->spawnhealth
879 && target->info->xdeathstate)
880 P_SetMobjState(target, target->info->xdeathstate);
881 else
882 P_SetMobjState(target, target->info->deathstate);
883 }
884 }
885
886 // villsa [STRIFE] no death tics randomization
887
888 // Drop stuff.
889 // villsa [STRIFE] get item from dialog target
890 item = P_DialogFind(target->type, target->miscdata)->dropitem;
891
892 if(!item)
893 {
894 // villsa [STRIFE] drop default items
895 switch(target->type)
896 {
897 case MT_ORACLE:
898 item = MT_MEAT;
899 break;
900
901 case MT_PROGRAMMER:
902 item = MT_SIGIL_A;
903 break;
904
905 case MT_PRIEST:
906 item = MT_JUNK;
907 break;
908
909 case MT_BISHOP:
910 item = MT_AMINIBOX;
911 break;
912
913 case MT_PGUARD:
914 case MT_CRUSADER:
915 item = MT_ACELL;
916 break;
917
918 case MT_RLEADER:
919 item = MT_AAMMOBOX;
920 break;
921
922 case MT_GUARD1:
923 case MT_REBEL1:
924 case MT_SHADOWGUARD:
925 item = MT_ACLIP;
926 break;
927
928 case MT_SPECTRE_B:
929 item = MT_SIGIL_B;
930 break;
931
932 case MT_SPECTRE_C:
933 item = MT_SIGIL_C;
934 break;
935
936 case MT_SPECTRE_D:
937 item = MT_SIGIL_D;
938 break;
939
940 case MT_SPECTRE_E:
941 item = MT_SIGIL_E;
942 break;
943
944 case MT_COUPLING:
945 junk.tag = 225;
946 EV_DoDoor(&junk, vld_close);
947
948 junk.tag = 44;
949 EV_DoFloor(&junk, lowerFloor);
950
951 GiveVoiceObjective("VOC13", "LOG13", 0);
952
953 item = MT_COUPLING_BROKEN;
954 players[0].questflags |= (1 << (mobjinfo[MT_COUPLING].speed - 1));
955 break;
956
957 default:
958 return;
959 }
960 }
961
962 // handle special case for scripted target's dropped item
963 switch(item)
964 {
965 case MT_TOKEN_SHOPCLOSE:
966 junk.tag = 222;
967 EV_DoDoor(&junk, vld_close);
968 P_NoiseAlert(players[0].mo, players[0].mo);
969
970 M_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
971 "%s", DEH_String("You're dead! You set off the alarm."));
972 if(!deathmatch)
973 players[consoleplayer].message = plrkilledmsg;
974
975 return;
976
977 case MT_TOKEN_PRISON_PASS:
978 junk.tag = 223;
979 EV_DoDoor(&junk, vld_open);
980 return;
981
982 case MT_TOKEN_DOOR3:
983 junk.tag = 224;
984 EV_DoDoor(&junk, vld_open);
985 return;
986
987 case MT_SIGIL_A:
988 case MT_SIGIL_B:
989 case MT_SIGIL_C:
990 case MT_SIGIL_D:
991 case MT_SIGIL_E:
992 for(i = 0; i < MAXPLAYERS; i++)
993 {
994 if(!P_GiveWeapon(&players[i], wp_sigil, false))
995 {
996 if(players[i].sigiltype++ > 4)
997 players[i].sigiltype = 4;
998 }
999
1000 // haleyjd 20110225: fixed these two assignments which Watcom munged
1001 // up in the assembly by pre-incrementing the pointer into players[]
1002 players[i].st_update = true;
1003 players[i].pendingweapon = wp_sigil;
1004 }
1005 return;
1006
1007 case MT_TOKEN_ALARM:
1008 P_NoiseAlert(players[0].mo, players[0].mo);
1009
1010 M_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
1011 "%s", DEH_String("You Fool! You've set off the alarm"));
1012 if(!deathmatch)
1013 players[consoleplayer].message = plrkilledmsg;
1014 return;
1015
1016 default:
1017 break;
1018 }
1019
1020 // villsa [STRIFE] toss out item
1021 if(!deathmatch || !(mobjinfo[item].flags & MF_NOTDMATCH))
1022 {
1023 int r;
1024
1025 mo = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), item);
1026 r = P_Random();
1027 mo->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS;
1028 r = P_Random();
1029 mo->momy += ((r & 7) - (P_Random() & 7)) << FRACBITS;
1030 mo->flags |= (MF_SPECIAL|MF_DROPPED); // special versions of items
1031 }
1032 }
1033
1034 //
1035 // P_IsMobjBoss
1036 //
1037 // villsa [STRIFE] new function
1038 //
P_IsMobjBoss(mobjtype_t type)1039 static boolean P_IsMobjBoss(mobjtype_t type)
1040 {
1041 switch(type)
1042 {
1043 case MT_PROGRAMMER:
1044 case MT_BISHOP:
1045 case MT_RLEADER:
1046 case MT_ORACLE:
1047 case MT_PRIEST:
1048 return true;
1049
1050 default:
1051 return false;
1052 }
1053 }
1054
1055
1056 //
1057 // P_DamageMobj
1058 // Damages both enemies and players
1059 // "inflictor" is the thing that caused the damage
1060 // creature or missile, can be NULL (slime, etc)
1061 // "source" is the thing to target after taking damage
1062 // creature or NULL
1063 // Source and inflictor are the same for melee attacks.
1064 // Source can be NULL for slime, barrel explosions
1065 // and other environmental stuff.
1066 //
1067 // [STRIFE] Extensive changes for spectrals, fire damage, disintegration, and
1068 // a plethora of mobjtype-specific hacks.
1069 //
P_DamageMobj(mobj_t * target,mobj_t * inflictor,mobj_t * source,int damage)1070 void P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage)
1071 {
1072 angle_t ang;
1073 int saved;
1074 player_t* player;
1075 fixed_t thrust;
1076 int temp;
1077
1078 if(!(target->flags & MF_SHOOTABLE) )
1079 return; // shouldn't happen...
1080
1081 if(target->health <= 0)
1082 return;
1083
1084 player = target->player;
1085
1086 // villsa [STRIFE] unused - skullfly check (removed)
1087
1088 // villsa [STRIFE] handle spectral stuff
1089 // notes on projectile health:
1090 // -2 == enemy spectral projectile
1091 // -1 == player spectral projectile
1092
1093 // haleyjd 20110203: refactored completely
1094 if(inflictor && (inflictor->flags & MF_SPECTRAL))
1095 {
1096 // players aren't damaged by their own (or others???) sigils
1097 // STRIFE-TODO: verify in deathmatch
1098 if(target->type == MT_PLAYER && inflictor->health == -1)
1099 return;
1100 // enemies aren't damaged by enemy sigil attacks
1101 if((target->flags & MF_SPECTRAL) && inflictor->health == -2)
1102 return;
1103 // Macil2, Oracle, and Spectre C cannot be damaged by Sigil A
1104 switch(target->type)
1105 {
1106 case MT_RLEADER2:
1107 case MT_ORACLE:
1108 case MT_SPECTRE_C:
1109 // haleyjd: added source->player validity check for safety...
1110 if(source->player && source->player->sigiltype < 1)
1111 return;
1112 default:
1113 break;
1114 }
1115 }
1116
1117 // villsa [STRIFE] new checks for various actors
1118 if(inflictor)
1119 {
1120 // Fire damage inflictors
1121 if(inflictor->type == MT_SFIREBALL ||
1122 inflictor->type == MT_C_FLAME ||
1123 inflictor->type == MT_PFLAME)
1124 {
1125 temp = damage / 2;
1126
1127 if(P_IsMobjBoss(target->type))
1128 damage /= 2;
1129 else if(inflictor->type == MT_PFLAME)
1130 {
1131 damage /= 2;
1132 // robots take very little damage
1133 if(target->flags & MF_NOBLOOD)
1134 damage = temp / 2;
1135 }
1136 }
1137 else
1138 {
1139 switch(inflictor->type)
1140 {
1141 case MT_HOOKSHOT:
1142 // haleyjd 20110203: should use source, not inflictor
1143 ang = R_PointToAngle2(
1144 target->x,
1145 target->y,
1146 source->x,
1147 source->y) >> ANGLETOFINESHIFT;
1148
1149 target->momx += FixedMul(finecosine[ang], (12750*FRACUNIT) / target->info->mass);
1150 target->momy += FixedMul(finesine[ang], (12750*FRACUNIT) / target->info->mass);
1151 target->reactiontime += 10;
1152
1153 temp = P_AproxDistance(target->x - source->x, target->y - source->y);
1154 temp /= target->info->mass;
1155
1156 if(temp < 1)
1157 temp = 1;
1158
1159 target->momz = (source->z - target->z) / temp;
1160 break;
1161
1162 case MT_POISARROW:
1163 // don't affect robots
1164 if(target->flags & MF_NOBLOOD)
1165 return;
1166
1167 // instant kill
1168 damage = target->health + 10;
1169 break;
1170
1171 default:
1172 // Spectral retaliation, though this may in fact be unreachable
1173 // since non-spectral inflictors are mostly filtered out.
1174 if(target->flags & MF_SPECTRAL
1175 && !(inflictor->flags & MF_SPECTRAL))
1176 {
1177 P_SetMobjState(target, target->info->missilestate);
1178 return; // take no damage
1179 }
1180 break;
1181 }
1182 }
1183 }
1184
1185 // villsa [STRIFE] special cases for shopkeepers and macil
1186 if((target->type >= MT_SHOPKEEPER_W && target->type <= MT_SHOPKEEPER_M)
1187 || target->type == MT_RLEADER)
1188 {
1189 if(source)
1190 target->target = source;
1191
1192 P_SetMobjState(target, target->info->painstate);
1193 return;
1194 }
1195
1196 // villsa [STRIFE] handle fieldguard damage
1197 if(target->type == MT_FIELDGUARD)
1198 {
1199 // degnin ores are only allowed to damage the fieldguard
1200 if(!inflictor || inflictor->type != MT_DEGNINORE)
1201 return;
1202
1203 damage = target->health;
1204 }
1205
1206 if(player && gameskill == sk_baby)
1207 damage >>= 1; // take half damage in trainer mode
1208
1209
1210 // Some close combat weapons should not
1211 // inflict thrust and push the victim out of reach,
1212 // thus kick away unless using the chainsaw.
1213 if (inflictor
1214 && !(target->flags & MF_NOCLIP)
1215 && (!source
1216 || !source->player
1217 || source->player->readyweapon != wp_flame))
1218 {
1219 ang = R_PointToAngle2(inflictor->x,
1220 inflictor->y,
1221 target->x,
1222 target->y);
1223
1224 thrust = damage * (FRACUNIT>>3) * 100 / target->info->mass;
1225
1226 // make fall forwards sometimes
1227 if(damage < 40
1228 && damage > target->health
1229 && target->z - inflictor->z > 64*FRACUNIT
1230 && (P_Random() & 1))
1231 {
1232 ang += ANG180;
1233 thrust *= 4;
1234 }
1235
1236 ang >>= ANGLETOFINESHIFT;
1237 target->momx += FixedMul (thrust, finecosine[ang]);
1238 target->momy += FixedMul (thrust, finesine[ang]);
1239 }
1240
1241 // player specific
1242 if(player)
1243 {
1244 // end of game hell hack
1245 if (target->subsector->sector->special == 11
1246 && damage >= target->health)
1247 {
1248 damage = target->health - 1;
1249 }
1250
1251
1252 // Below certain threshold,
1253 // ignore damage in GOD mode.
1254 // villsa [STRIFE] removed pw_invulnerability check
1255 if(damage < 1000 && (player->cheats & CF_GODMODE))
1256 return;
1257
1258 // villsa [STRIFE] flame attacks don't damage player if wearing envirosuit
1259 if(player->powers[pw_ironfeet] && inflictor)
1260 {
1261 if(inflictor->type == MT_SFIREBALL ||
1262 inflictor->type == MT_C_FLAME ||
1263 inflictor->type == MT_PFLAME)
1264 {
1265 damage = 0;
1266 }
1267 }
1268
1269 if(player->armortype)
1270 {
1271 if (player->armortype == 1)
1272 saved = damage/3;
1273 else
1274 saved = damage/2;
1275
1276 if(player->armorpoints <= saved)
1277 {
1278 // armor is used up
1279 saved = player->armorpoints;
1280 player->armortype = 0;
1281
1282 // villsa [STRIFE]
1283 P_UseInventoryItem(player, SPR_ARM1);
1284 P_UseInventoryItem(player, SPR_ARM2);
1285 }
1286 player->armorpoints -= saved;
1287 damage -= saved;
1288 }
1289 player->health -= damage; // mirror mobj health here for Dave
1290
1291 // [STRIFE] haleyjd 20130302: bug fix - this is *not* capped here.
1292 //if(player->health < 0)
1293 // player->health = 0;
1294
1295 player->attacker = source;
1296 player->damagecount += damage; // add damage after armor / invuln
1297
1298 // haleyjd 20110203 [STRIFE]: target->target set here
1299 if(target != source)
1300 target->target = source;
1301
1302 if(player->damagecount > 100)
1303 player->damagecount = 100; // teleport stomp does 10k points...
1304
1305 temp = damage < 100 ? damage : 100;
1306
1307 if(player == &players[consoleplayer])
1308 I_Tactile (40,10,40+temp*2);
1309 }
1310
1311 // do the damage
1312 target->health -= damage;
1313
1314 // villsa [STRIFE] auto use medkits
1315 if(player && player->health < 50)
1316 {
1317 if(deathmatch || player->cheats & CF_AUTOHEALTH)
1318 {
1319 while(player->health < 50 && P_UseInventoryItem(player, SPR_MDKT));
1320 while(player->health < 50 && P_UseInventoryItem(player, SPR_STMP));
1321 }
1322 }
1323
1324
1325 if(target->health <= 0)
1326 {
1327 // villsa [STRIFE] grenades hurt... OUCH
1328 if(inflictor && inflictor->type == MT_HEGRENADE)
1329 target->health = -target->info->spawnhealth;
1330 else if(!(target->flags & MF_NOBLOOD))
1331 {
1332 // villsa [STRIFE] disintegration death
1333 if(inflictor &&
1334 (inflictor->type == MT_STRIFEPUFF3 ||
1335 inflictor->type == MT_L_LASER ||
1336 inflictor->type == MT_TORPEDO ||
1337 inflictor->type == MT_TORPEDOSPREAD))
1338 {
1339 S_StartSound(target, sfx_dsrptr);
1340 target->health = -6666;
1341 }
1342 }
1343
1344 // villsa [STRIFE] flame death stuff
1345 if(!(target->flags & MF_NOBLOOD)
1346 && inflictor
1347 && (inflictor->type == MT_SFIREBALL ||
1348 inflictor->type == MT_C_FLAME ||
1349 inflictor->type == MT_PFLAME))
1350 {
1351 target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SHADOW|MF_MVIS);
1352 if(target->player)
1353 {
1354 target->player->cheats |= CF_ONFIRE;
1355 target->player->powers[pw_invisibility] = false;
1356 target->player->readyweapon = 0;
1357 P_SetPsprite(target->player, ps_weapon, S_WAVE_00); // 02
1358 P_SetPsprite(target->player, ps_flash, S_NULL);
1359 }
1360
1361 P_SetMobjState(target, S_BURN_00); // 349
1362 S_StartSound(target, sfx_burnme);
1363
1364 return;
1365 }
1366
1367 P_KillMobj(source, target);
1368 return;
1369 }
1370
1371 // villsa [STRIFE] set crash state
1372 if(target->health <= 6 && target->info->crashstate)
1373 {
1374 P_SetMobjState(target, target->info->crashstate);
1375 return;
1376 }
1377
1378 if(damage)
1379 {
1380 // villsa [STRIFE] removed unused skullfly flag
1381 if(P_Random() < target->info->painchance)
1382 {
1383 target->flags |= MF_JUSTHIT; // fight back!
1384 P_SetMobjState (target, target->info->painstate);
1385 }
1386 }
1387
1388 target->reactiontime = 0; // we're awake now...
1389
1390 // villsa [STRIFE] new checks for thing types
1391 if (target->type != MT_PROGRAMMER
1392 && (!target->threshold || target->type == MT_ENTITY)
1393 && source && source != target
1394 && source->type != MT_ENTITY
1395 && ((source->flags & MF_ALLY) != (target->flags & MF_ALLY)))
1396 {
1397 // if not intent on another player,
1398 // chase after this one
1399 target->target = source;
1400 target->threshold = BASETHRESHOLD;
1401
1402 if(target->state == &states[target->info->spawnstate]
1403 && target->info->seestate != S_NULL)
1404 P_SetMobjState (target, target->info->seestate);
1405 }
1406 }
1407
1408