aboutsummaryrefslogtreecommitdiff
path: root/src/p_inter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/p_inter.c')
-rw-r--r--src/p_inter.c913
1 files changed, 913 insertions, 0 deletions
diff --git a/src/p_inter.c b/src/p_inter.c
new file mode 100644
index 0000000..2688e95
--- /dev/null
+++ b/src/p_inter.c
@@ -0,0 +1,913 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 * Handling interactions (i.e., collisions).
31 *
32 *-----------------------------------------------------------------------------*/
33
34#include "doomstat.h"
35#include "dstrings.h"
36#include "m_random.h"
37#include "am_map.h"
38#include "r_main.h"
39#include "s_sound.h"
40#include "sounds.h"
41#include "d_deh.h" // Ty 03/22/98 - externalized strings
42#include "p_tick.h"
43#include "lprintf.h"
44
45#include "p_inter.h"
46#include "p_enemy.h"
47
48#ifdef __GNUG__
49#pragma implementation "p_inter.h"
50#endif
51#include "p_inter.h"
52
53#define BONUSADD 6
54
55// Ty 03/07/98 - add deh externals
56// Maximums and such were hardcoded values. Need to externalize those for
57// dehacked support (and future flexibility). Most var names came from the key
58// strings used in dehacked.
59
60int initial_health = 100;
61int initial_bullets = 50;
62int maxhealth = 100; // was MAXHEALTH as a #define, used only in this module
63int max_armor = 200;
64int green_armor_class = 1; // these are involved with armortype below
65int blue_armor_class = 2;
66int max_soul = 200;
67int soul_health = 100;
68int mega_health = 200;
69int god_health = 100; // these are used in cheats (see st_stuff.c)
70int idfa_armor = 200;
71int idfa_armor_class = 2;
72// not actually used due to pairing of cheat_k and cheat_fa
73int idkfa_armor = 200;
74int idkfa_armor_class = 2;
75
76int bfgcells = 40; // used in p_pspr.c
77int monsters_infight = 0; // e6y: Dehacked support - monsters infight
78// Ty 03/07/98 - end deh externals
79
80// a weapon is found with two clip loads,
81// a big item has five clip loads
82int maxammo[NUMAMMO] = {200, 50, 300, 50};
83int clipammo[NUMAMMO] = { 10, 4, 20, 1};
84
85//
86// GET STUFF
87//
88
89//
90// P_GiveAmmo
91// Num is the number of clip loads,
92// not the individual count (0= 1/2 clip).
93// Returns false if the ammo can't be picked up at all
94//
95
96static boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int num)
97{
98 int oldammo;
99
100 if (ammo == am_noammo)
101 return false;
102
103#ifdef RANGECHECK
104 if (ammo < 0 || ammo > NUMAMMO)
105 I_Error ("P_GiveAmmo: bad type %i", ammo);
106#endif
107
108 if ( player->ammo[ammo] == player->maxammo[ammo] )
109 return false;
110
111 if (num)
112 num *= clipammo[ammo];
113 else
114 num = clipammo[ammo]/2;
115
116 // give double ammo in trainer mode, you'll need in nightmare
117 if (gameskill == sk_baby || gameskill == sk_nightmare)
118 num <<= 1;
119
120 oldammo = player->ammo[ammo];
121 player->ammo[ammo] += num;
122
123 if (player->ammo[ammo] > player->maxammo[ammo])
124 player->ammo[ammo] = player->maxammo[ammo];
125
126 // If non zero ammo, don't change up weapons, player was lower on purpose.
127 if (oldammo)
128 return true;
129
130 // We were down to zero, so select a new weapon.
131 // Preferences are not user selectable.
132
133 switch (ammo)
134 {
135 case am_clip:
136 if (player->readyweapon == wp_fist) {
137 if (player->weaponowned[wp_chaingun])
138 player->pendingweapon = wp_chaingun;
139 else
140 player->pendingweapon = wp_pistol;
141 }
142 break;
143
144 case am_shell:
145 if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol)
146 if (player->weaponowned[wp_shotgun])
147 player->pendingweapon = wp_shotgun;
148 break;
149
150 case am_cell:
151 if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol)
152 if (player->weaponowned[wp_plasma])
153 player->pendingweapon = wp_plasma;
154 break;
155
156 case am_misl:
157 if (player->readyweapon == wp_fist)
158 if (player->weaponowned[wp_missile])
159 player->pendingweapon = wp_missile;
160 default:
161 break;
162 }
163 return true;
164}
165
166//
167// P_GiveWeapon
168// The weapon name may have a MF_DROPPED flag ored in.
169//
170
171static boolean P_GiveWeapon(player_t *player, weapontype_t weapon, boolean dropped)
172{
173 boolean gaveammo;
174 boolean gaveweapon;
175
176 if (netgame && deathmatch!=2 && !dropped)
177 {
178 // leave placed weapons forever on net games
179 if (player->weaponowned[weapon])
180 return false;
181
182 player->bonuscount += BONUSADD;
183 player->weaponowned[weapon] = true;
184
185 P_GiveAmmo(player, weaponinfo[weapon].ammo, deathmatch ? 5 : 2);
186
187 player->pendingweapon = weapon;
188 /* cph 20028/10 - for old-school DM addicts, allow old behavior
189 * where only consoleplayer's pickup sounds are heard */
190 // displayplayer, not consoleplayer, for viewing multiplayer demos
191 if (!comp[comp_sound] || player == &players[displayplayer])
192 S_StartSound (player->mo, sfx_wpnup|PICKUP_SOUND); // killough 4/25/98
193 return false;
194 }
195
196 if (weaponinfo[weapon].ammo != am_noammo)
197 {
198 // give one clip with a dropped weapon,
199 // two clips with a found weapon
200 gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, dropped ? 1 : 2);
201 }
202 else
203 gaveammo = false;
204
205 if (player->weaponowned[weapon])
206 gaveweapon = false;
207 else
208 {
209 gaveweapon = true;
210 player->weaponowned[weapon] = true;
211 player->pendingweapon = weapon;
212 }
213 return gaveweapon || gaveammo;
214}
215
216//
217// P_GiveBody
218// Returns false if the body isn't needed at all
219//
220
221static boolean P_GiveBody(player_t *player, int num)
222{
223 if (player->health >= maxhealth)
224 return false; // Ty 03/09/98 externalized MAXHEALTH to maxhealth
225 player->health += num;
226 if (player->health > maxhealth)
227 player->health = maxhealth;
228 player->mo->health = player->health;
229 return true;
230}
231
232//
233// P_GiveArmor
234// Returns false if the armor is worse
235// than the current armor.
236//
237
238static boolean P_GiveArmor(player_t *player, int armortype)
239{
240 int hits = armortype*100;
241 if (player->armorpoints >= hits)
242 return false; // don't pick up
243 player->armortype = armortype;
244 player->armorpoints = hits;
245 return true;
246}
247
248//
249// P_GiveCard
250//
251
252static void P_GiveCard(player_t *player, card_t card)
253{
254 if (player->cards[card])
255 return;
256 player->bonuscount = BONUSADD;
257 player->cards[card] = 1;
258}
259
260//
261// P_GivePower
262//
263// Rewritten by Lee Killough
264//
265
266boolean P_GivePower(player_t *player, int power)
267{
268 static const int tics[NUMPOWERS] = {
269 INVULNTICS, 1 /* strength */, INVISTICS,
270 IRONTICS, 1 /* allmap */, INFRATICS,
271 };
272
273 switch (power)
274 {
275 case pw_invisibility:
276 player->mo->flags |= MF_SHADOW;
277 break;
278 case pw_allmap:
279 if (player->powers[pw_allmap])
280 return false;
281 break;
282 case pw_strength:
283 P_GiveBody(player,100);
284 break;
285 }
286
287 // Unless player has infinite duration cheat, set duration (killough)
288
289 if (player->powers[power] >= 0)
290 player->powers[power] = tics[power];
291 return true;
292}
293
294//
295// P_TouchSpecialThing
296//
297
298void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
299{
300 player_t *player;
301 int i;
302 int sound;
303 fixed_t delta = special->z - toucher->z;
304
305 if (delta > toucher->height || delta < -8*FRACUNIT)
306 return; // out of reach
307
308 sound = sfx_itemup;
309 player = toucher->player;
310
311 // Dead thing touching.
312 // Can happen with a sliding player corpse.
313 if (toucher->health <= 0)
314 return;
315
316 // Identify by sprite.
317 switch (special->sprite)
318 {
319 // armor
320 case SPR_ARM1:
321 if (!P_GiveArmor (player, green_armor_class))
322 return;
323 player->message = s_GOTARMOR; // Ty 03/22/98 - externalized
324 break;
325
326 case SPR_ARM2:
327 if (!P_GiveArmor (player, blue_armor_class))
328 return;
329 player->message = s_GOTMEGA; // Ty 03/22/98 - externalized
330 break;
331
332 // bonus items
333 case SPR_BON1:
334 player->health++; // can go over 100%
335 if (player->health > (maxhealth * 2))
336 player->health = (maxhealth * 2);
337 player->mo->health = player->health;
338 player->message = s_GOTHTHBONUS; // Ty 03/22/98 - externalized
339 break;
340
341 case SPR_BON2:
342 player->armorpoints++; // can go over 100%
343 if (player->armorpoints > max_armor)
344 player->armorpoints = max_armor;
345 if (!player->armortype)
346 player->armortype = green_armor_class;
347 player->message = s_GOTARMBONUS; // Ty 03/22/98 - externalized
348 break;
349
350 case SPR_SOUL:
351 player->health += soul_health;
352 if (player->health > max_soul)
353 player->health = max_soul;
354 player->mo->health = player->health;
355 player->message = s_GOTSUPER; // Ty 03/22/98 - externalized
356 sound = sfx_getpow;
357 break;
358
359 case SPR_MEGA:
360 if (gamemode != commercial)
361 return;
362 player->health = mega_health;
363 player->mo->health = player->health;
364 P_GiveArmor (player,blue_armor_class);
365 player->message = s_GOTMSPHERE; // Ty 03/22/98 - externalized
366 sound = sfx_getpow;
367 break;
368
369 // cards
370 // leave cards for everyone
371 case SPR_BKEY:
372 if (!player->cards[it_bluecard])
373 player->message = s_GOTBLUECARD; // Ty 03/22/98 - externalized
374 P_GiveCard (player, it_bluecard);
375 if (!netgame)
376 break;
377 return;
378
379 case SPR_YKEY:
380 if (!player->cards[it_yellowcard])
381 player->message = s_GOTYELWCARD; // Ty 03/22/98 - externalized
382 P_GiveCard (player, it_yellowcard);
383 if (!netgame)
384 break;
385 return;
386
387 case SPR_RKEY:
388 if (!player->cards[it_redcard])
389 player->message = s_GOTREDCARD; // Ty 03/22/98 - externalized
390 P_GiveCard (player, it_redcard);
391 if (!netgame)
392 break;
393 return;
394
395 case SPR_BSKU:
396 if (!player->cards[it_blueskull])
397 player->message = s_GOTBLUESKUL; // Ty 03/22/98 - externalized
398 P_GiveCard (player, it_blueskull);
399 if (!netgame)
400 break;
401 return;
402
403 case SPR_YSKU:
404 if (!player->cards[it_yellowskull])
405 player->message = s_GOTYELWSKUL; // Ty 03/22/98 - externalized
406 P_GiveCard (player, it_yellowskull);
407 if (!netgame)
408 break;
409 return;
410
411 case SPR_RSKU:
412 if (!player->cards[it_redskull])
413 player->message = s_GOTREDSKULL; // Ty 03/22/98 - externalized
414 P_GiveCard (player, it_redskull);
415 if (!netgame)
416 break;
417 return;
418
419 // medikits, heals
420 case SPR_STIM:
421 if (!P_GiveBody (player, 10))
422 return;
423 player->message = s_GOTSTIM; // Ty 03/22/98 - externalized
424 break;
425
426 case SPR_MEDI:
427 if (!P_GiveBody (player, 25))
428 return;
429
430 if (player->health < 50) // cph - 25 + the 25 just added, thanks to Quasar for reporting this bug
431 player->message = s_GOTMEDINEED; // Ty 03/22/98 - externalized
432 else
433 player->message = s_GOTMEDIKIT; // Ty 03/22/98 - externalized
434 break;
435
436
437 // power ups
438 case SPR_PINV:
439 if (!P_GivePower (player, pw_invulnerability))
440 return;
441 player->message = s_GOTINVUL; // Ty 03/22/98 - externalized
442 sound = sfx_getpow;
443 break;
444
445 case SPR_PSTR:
446 if (!P_GivePower (player, pw_strength))
447 return;
448 player->message = s_GOTBERSERK; // Ty 03/22/98 - externalized
449 if (player->readyweapon != wp_fist)
450 player->pendingweapon = wp_fist;
451 sound = sfx_getpow;
452 break;
453
454 case SPR_PINS:
455 if (!P_GivePower (player, pw_invisibility))
456 return;
457 player->message = s_GOTINVIS; // Ty 03/22/98 - externalized
458 sound = sfx_getpow;
459 break;
460
461 case SPR_SUIT:
462 if (!P_GivePower (player, pw_ironfeet))
463 return;
464 player->message = s_GOTSUIT; // Ty 03/22/98 - externalized
465 sound = sfx_getpow;
466 break;
467
468 case SPR_PMAP:
469 if (!P_GivePower (player, pw_allmap))
470 return;
471 player->message = s_GOTMAP; // Ty 03/22/98 - externalized
472 sound = sfx_getpow;
473 break;
474
475 case SPR_PVIS:
476 if (!P_GivePower (player, pw_infrared))
477 return;
478 player->message = s_GOTVISOR; // Ty 03/22/98 - externalized
479 sound = sfx_getpow;
480 break;
481
482 // ammo
483 case SPR_CLIP:
484 if (special->flags & MF_DROPPED)
485 {
486 if (!P_GiveAmmo (player,am_clip,0))
487 return;
488 }
489 else
490 {
491 if (!P_GiveAmmo (player,am_clip,1))
492 return;
493 }
494 player->message = s_GOTCLIP; // Ty 03/22/98 - externalized
495 break;
496
497 case SPR_AMMO:
498 if (!P_GiveAmmo (player, am_clip,5))
499 return;
500 player->message = s_GOTCLIPBOX; // Ty 03/22/98 - externalized
501 break;
502
503 case SPR_ROCK:
504 if (!P_GiveAmmo (player, am_misl,1))
505 return;
506 player->message = s_GOTROCKET; // Ty 03/22/98 - externalized
507 break;
508
509 case SPR_BROK:
510 if (!P_GiveAmmo (player, am_misl,5))
511 return;
512 player->message = s_GOTROCKBOX; // Ty 03/22/98 - externalized
513 break;
514
515 case SPR_CELL:
516 if (!P_GiveAmmo (player, am_cell,1))
517 return;
518 player->message = s_GOTCELL; // Ty 03/22/98 - externalized
519 break;
520
521 case SPR_CELP:
522 if (!P_GiveAmmo (player, am_cell,5))
523 return;
524 player->message = s_GOTCELLBOX; // Ty 03/22/98 - externalized
525 break;
526
527 case SPR_SHEL:
528 if (!P_GiveAmmo (player, am_shell,1))
529 return;
530 player->message = s_GOTSHELLS; // Ty 03/22/98 - externalized
531 break;
532
533 case SPR_SBOX:
534 if (!P_GiveAmmo (player, am_shell,5))
535 return;
536 player->message = s_GOTSHELLBOX; // Ty 03/22/98 - externalized
537 break;
538
539 case SPR_BPAK:
540 if (!player->backpack)
541 {
542 for (i=0 ; i<NUMAMMO ; i++)
543 player->maxammo[i] *= 2;
544 player->backpack = true;
545 }
546 for (i=0 ; i<NUMAMMO ; i++)
547 P_GiveAmmo (player, i, 1);
548 player->message = s_GOTBACKPACK; // Ty 03/22/98 - externalized
549 break;
550
551 // weapons
552 case SPR_BFUG:
553 if (!P_GiveWeapon (player, wp_bfg, false) )
554 return;
555 player->message = s_GOTBFG9000; // Ty 03/22/98 - externalized
556 sound = sfx_wpnup;
557 break;
558
559 case SPR_MGUN:
560 if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED)!=0) )
561 return;
562 player->message = s_GOTCHAINGUN; // Ty 03/22/98 - externalized
563 sound = sfx_wpnup;
564 break;
565
566 case SPR_CSAW:
567 if (!P_GiveWeapon (player, wp_chainsaw, false) )
568 return;
569 player->message = s_GOTCHAINSAW; // Ty 03/22/98 - externalized
570 sound = sfx_wpnup;
571 break;
572
573 case SPR_LAUN:
574 if (!P_GiveWeapon (player, wp_missile, false) )
575 return;
576 player->message = s_GOTLAUNCHER; // Ty 03/22/98 - externalized
577 sound = sfx_wpnup;
578 break;
579
580 case SPR_PLAS:
581 if (!P_GiveWeapon (player, wp_plasma, false) )
582 return;
583 player->message = s_GOTPLASMA; // Ty 03/22/98 - externalized
584 sound = sfx_wpnup;
585 break;
586
587 case SPR_SHOT:
588 if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED)!=0 ) )
589 return;
590 player->message = s_GOTSHOTGUN; // Ty 03/22/98 - externalized
591 sound = sfx_wpnup;
592 break;
593
594 case SPR_SGN2:
595 if (!P_GiveWeapon(player, wp_supershotgun, (special->flags&MF_DROPPED)!=0))
596 return;
597 player->message = s_GOTSHOTGUN2; // Ty 03/22/98 - externalized
598 sound = sfx_wpnup;
599 break;
600
601 default:
602 I_Error ("P_SpecialThing: Unknown gettable thing");
603 }
604
605 if (special->flags & MF_COUNTITEM)
606 player->itemcount++;
607 P_RemoveMobj (special);
608 player->bonuscount += BONUSADD;
609
610 /* cph 20028/10 - for old-school DM addicts, allow old behavior
611 * where only consoleplayer's pickup sounds are heard */
612 // displayplayer, not consoleplayer, for viewing multiplayer demos
613 if (!comp[comp_sound] || player == &players[displayplayer])
614 S_StartSound (player->mo, sound | PICKUP_SOUND); // killough 4/25/98
615}
616
617//
618// KillMobj
619//
620// killough 11/98: make static
621static void P_KillMobj(mobj_t *source, mobj_t *target)
622{
623 mobjtype_t item;
624 mobj_t *mo;
625
626 target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);
627
628 if (target->type != MT_SKULL)
629 target->flags &= ~MF_NOGRAVITY;
630
631 target->flags |= MF_CORPSE|MF_DROPOFF;
632 target->height >>= 2;
633
634 if (!((target->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL)))
635 totallive--;
636
637 if (source && source->player)
638 {
639 // count for intermission
640 if (target->flags & MF_COUNTKILL)
641 source->player->killcount++;
642 if (target->player)
643 source->player->frags[target->player-players]++;
644 }
645 else
646 if (target->flags & MF_COUNTKILL) { /* Add to kills tally */
647 if ((compatibility_level < lxdoom_1_compatibility) || !netgame) {
648 if (!netgame)
649 // count all monster deaths,
650 // even those caused by other monsters
651 players[0].killcount++;
652 } else
653 if (!deathmatch) {
654 // try and find a player to give the kill to, otherwise give the
655 // kill to a random player. this fixes the missing monsters bug
656 // in coop - rain
657 // CPhipps - not a bug as such, but certainly an inconsistency.
658 if (target->lastenemy && target->lastenemy->health > 0
659 && target->lastenemy->player) // Fighting a player
660 target->lastenemy->player->killcount++;
661 else {
662 // cph - randomely choose a player in the game to be credited
663 // and do it uniformly between the active players
664 unsigned int activeplayers = 0, player, i;
665
666 for (player = 0; player<MAXPLAYERS; player++)
667 if (playeringame[player])
668 activeplayers++;
669
670 if (activeplayers) {
671 player = P_Random(pr_friends) % activeplayers;
672
673 for (i=0; i<MAXPLAYERS; i++)
674 if (playeringame[i])
675 if (!player--)
676 players[i].killcount++;
677 }
678 }
679 }
680 }
681
682 if (target->player)
683 {
684 // count environment kills against you
685 if (!source)
686 target->player->frags[target->player-players]++;
687
688 target->flags &= ~MF_SOLID;
689 target->player->playerstate = PST_DEAD;
690 P_DropWeapon (target->player);
691
692 if (target->player == &players[consoleplayer] && (automapmode & am_active))
693 AM_Stop(); // don't die in auto map; switch view prior to dying
694 }
695
696 if (target->health < -target->info->spawnhealth && target->info->xdeathstate)
697 P_SetMobjState (target, target->info->xdeathstate);
698 else
699 P_SetMobjState (target, target->info->deathstate);
700
701 target->tics -= P_Random(pr_killtics)&3;
702
703 if (target->tics < 1)
704 target->tics = 1;
705
706 // Drop stuff.
707 // This determines the kind of object spawned
708 // during the death frame of a thing.
709
710 switch (target->type)
711 {
712 case MT_WOLFSS:
713 case MT_POSSESSED:
714 item = MT_CLIP;
715 break;
716
717 case MT_SHOTGUY:
718 item = MT_SHOTGUN;
719 break;
720
721 case MT_CHAINGUY:
722 item = MT_CHAINGUN;
723 break;
724
725 default:
726 return;
727 }
728
729 mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item);
730 mo->flags |= MF_DROPPED; // special versions of items
731}
732
733//
734// P_DamageMobj
735// Damages both enemies and players
736// "inflictor" is the thing that caused the damage
737// creature or missile, can be NULL (slime, etc)
738// "source" is the thing to target after taking damage
739// creature or NULL
740// Source and inflictor are the same for melee attacks.
741// Source can be NULL for slime, barrel explosions
742// and other environmental stuff.
743//
744
745void P_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage)
746{
747 player_t *player;
748 boolean justhit = false; /* killough 11/98 */
749
750 /* killough 8/31/98: allow bouncers to take damage */
751 if (!(target->flags & (MF_SHOOTABLE | MF_BOUNCES)))
752 return; // shouldn't happen...
753
754 if (target->health <= 0)
755 return;
756
757 if (target->flags & MF_SKULLFLY)
758 target->momx = target->momy = target->momz = 0;
759
760 player = target->player;
761 if (player && gameskill == sk_baby)
762 damage >>= 1; // take half damage in trainer mode
763
764 // Some close combat weapons should not
765 // inflict thrust and push the victim out of reach,
766 // thus kick away unless using the chainsaw.
767
768 if (inflictor && !(target->flags & MF_NOCLIP) &&
769 (!source || !source->player ||
770 source->player->readyweapon != wp_chainsaw))
771 {
772 unsigned ang = R_PointToAngle2 (inflictor->x, inflictor->y,
773 target->x, target->y);
774
775 fixed_t thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
776
777 // make fall forwards sometimes
778 if ( damage < 40 && damage > target->health
779 && target->z - inflictor->z > 64*FRACUNIT
780 && P_Random(pr_damagemobj) & 1)
781 {
782 ang += ANG180;
783 thrust *= 4;
784 }
785
786 ang >>= ANGLETOFINESHIFT;
787 target->momx += FixedMul (thrust, finecosine[ang]);
788 target->momy += FixedMul (thrust, finesine[ang]);
789
790 /* killough 11/98: thrust objects hanging off ledges */
791 if (target->intflags & MIF_FALLING && target->gear >= MAXGEAR)
792 target->gear = 0;
793 }
794
795 // player specific
796 if (player)
797 {
798 // end of game hell hack
799 if (target->subsector->sector->special == 11 && damage >= target->health)
800 damage = target->health - 1;
801
802 // Below certain threshold,
803 // ignore damage in GOD mode, or with INVUL power.
804 // killough 3/26/98: make god mode 100% god mode in non-compat mode
805
806 if ((damage < 1000 || (!comp[comp_god] && (player->cheats&CF_GODMODE))) &&
807 (player->cheats&CF_GODMODE || player->powers[pw_invulnerability]))
808 return;
809
810 if (player->armortype)
811 {
812 int saved = player->armortype == 1 ? damage/3 : damage/2;
813 if (player->armorpoints <= saved)
814 {
815 // armor is used up
816 saved = player->armorpoints;
817 player->armortype = 0;
818 }
819 player->armorpoints -= saved;
820 damage -= saved;
821 }
822
823 player->health -= damage; // mirror mobj health here for Dave
824 if (player->health < 0)
825 player->health = 0;
826
827 player->attacker = source;
828 player->damagecount += damage; // add damage after armor / invuln
829
830 if (player->damagecount > 100)
831 player->damagecount = 100; // teleport stomp does 10k points...
832 }
833
834 // do the damage
835 target->health -= damage;
836 if (target->health <= 0)
837 {
838 P_KillMobj (source, target);
839 return;
840 }
841
842 // killough 9/7/98: keep track of targets so that friends can help friends
843 if (mbf_features)
844 {
845 /* If target is a player, set player's target to source,
846 * so that a friend can tell who's hurting a player
847 */
848 if (player)
849 P_SetTarget(&target->target, source);
850
851 /* killough 9/8/98:
852 * If target's health is less than 50%, move it to the front of its list.
853 * This will slightly increase the chances that enemies will choose to
854 * "finish it off", but its main purpose is to alert friends of danger.
855 */
856 if (target->health*2 < target->info->spawnhealth)
857 {
858 thinker_t *cap = &thinkerclasscap[target->flags & MF_FRIEND ?
859 th_friends : th_enemies];
860 (target->thinker.cprev->cnext = target->thinker.cnext)->cprev =
861 target->thinker.cprev;
862 (target->thinker.cnext = cap->cnext)->cprev = &target->thinker;
863 (target->thinker.cprev = cap)->cnext = &target->thinker;
864 }
865 }
866
867 if (P_Random (pr_painchance) < target->info->painchance &&
868 !(target->flags & MF_SKULLFLY)) { //killough 11/98: see below
869 if (mbf_features)
870 justhit = true;
871 else
872 target->flags |= MF_JUSTHIT; // fight back!
873
874 P_SetMobjState(target, target->info->painstate);
875 }
876
877 target->reactiontime = 0; // we're awake now...
878
879 /* killough 9/9/98: cleaned up, made more consistent: */
880
881 if (source && source != target && source->type != MT_VILE &&
882 (!target->threshold || target->type == MT_VILE) &&
883 ((source->flags ^ target->flags) & MF_FRIEND ||
884 monster_infighting ||
885 !mbf_features))
886 {
887 /* if not intent on another player, chase after this one
888 *
889 * killough 2/15/98: remember last enemy, to prevent
890 * sleeping early; 2/21/98: Place priority on players
891 * killough 9/9/98: cleaned up, made more consistent:
892 */
893
894 if (!target->lastenemy || target->lastenemy->health <= 0 ||
895 (!mbf_features ?
896 !target->lastenemy->player :
897 !((target->flags ^ target->lastenemy->flags) & MF_FRIEND) &&
898 target->target != source)) // remember last enemy - killough
899 P_SetTarget(&target->lastenemy, target->target);
900
901 P_SetTarget(&target->target, source); // killough 11/98
902 target->threshold = BASETHRESHOLD;
903 if (target->state == &states[target->info->spawnstate]
904 && target->info->seestate != S_NULL)
905 P_SetMobjState (target, target->info->seestate);
906 }
907
908 /* killough 11/98: Don't attack a friend, unless hit by that friend.
909 * cph 2006/04/01 - implicitly this is only if mbf_features */
910 if (justhit && (target->target == source || !target->target ||
911 !(target->flags & target->target->flags & MF_FRIEND)))
912 target->flags |= MF_JUSTHIT; // fight back!
913}