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