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