summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/p_inter.c
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2006-03-28 15:44:01 +0000
committerDave Chapman <dave@dchapman.com>2006-03-28 15:44:01 +0000
commit47f4a458d636a889e955e68f896708f1276febc0 (patch)
tree99f770c02ef606f0abbdcd332ac39e69830d8007 /apps/plugins/doom/p_inter.c
parentfff7d6157d56f233cad5c2003475e47a5ff809a7 (diff)
downloadrockbox-47f4a458d636a889e955e68f896708f1276febc0.zip
rockbox-47f4a458d636a889e955e68f896708f1276febc0.tar.gz
rockbox-47f4a458d636a889e955e68f896708f1276febc0.tar.bz2
rockbox-47f4a458d636a889e955e68f896708f1276febc0.tar.xz
Patch #2969 - Doom! Currently only working on the H300.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9312 a1c6a512-1295-4272-9138-f99709370657
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 0000000..f4a0f80
--- /dev/null
+++ b/apps/plugins/doom/p_inter.c
@@ -0,0 +1,904 @@
+/* Emacs style mode select -*- C++ -*-
+ *-----------------------------------------------------------------------------
+ *
+ *
+ * PrBoom a Doom port merged with LxDoom and LSDLDoom
+ * based on BOOM, a modified and improved DOOM engine
+ * Copyright (C) 1999 by
+ * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
+ * Copyright (C) 1999-2000 by
+ * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * DESCRIPTION:
+ * Handling interactions (i.e., collisions).
+ *
+ *-----------------------------------------------------------------------------*/
+
+#include "doomstat.h"
+#include "dstrings.h"
+#include "m_random.h"
+#include "am_map.h"
+#include "r_main.h"
+#include "s_sound.h"
+#include "sounds.h"
+//#include "d_deh.h" // Ty 03/22/98 - externalized strings
+#include "p_tick.h"
+#include "i_system.h"
+#include "p_inter.h"
+#include "p_enemy.h"
+
+#ifdef __GNUG__
+#pragma implementation "p_inter.h"
+#endif
+#include "p_inter.h"
+
+#define BONUSADD 6
+
+// Ty 03/07/98 - add deh externals
+// Maximums and such were hardcoded values. Need to externalize those for
+// dehacked support (and future flexibility). Most var names came from the key
+// strings used in dehacked.
+
+int initial_health = 100;
+int initial_bullets = 50;
+int maxhealth = 100; // was MAXHEALTH as a #define, used only in this module
+int max_armor = 200;
+int green_armor_class = 1; // these are involved with armortype below
+int blue_armor_class = 2;
+int max_soul = 200;
+int soul_health = 100;
+int mega_health = 200;
+int god_health = 100; // these are used in cheats (see st_stuff.c)
+int idfa_armor = 200;
+int idfa_armor_class = 2;
+// not actually used due to pairing of cheat_k and cheat_fa
+int idkfa_armor = 200;
+int idkfa_armor_class = 2;
+
+int bfgcells = 40; // used in p_pspr.c
+// Ty 03/07/98 - end deh externals
+
+// a weapon is found with two clip loads,
+// a big item has five clip loads
+int maxammo[NUMAMMO] = {200, 50, 300, 50};
+int clipammo[NUMAMMO] = { 10, 4, 20, 1};
+
+//
+// GET STUFF
+//
+
+//
+// P_GiveAmmo
+// Num is the number of clip loads,
+// not the individual count (0= 1/2 clip).
+// Returns false if the ammo can't be picked up at all
+//
+
+boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int num)
+{
+ int oldammo;
+
+ if (ammo == am_noammo)
+ return false;
+
+#ifdef RANGECHECK
+ if (ammo < 0 || ammo > NUMAMMO)
+ I_Error ("P_GiveAmmo: bad type %i", ammo);
+#endif
+
+ if ( player->ammo[ammo] == player->maxammo[ammo] )
+ return false;
+
+ if (num)
+ num *= clipammo[ammo];
+ else
+ num = clipammo[ammo]/2;
+
+ // give double ammo in trainer mode, you'll need in nightmare
+ if (gameskill == sk_baby || gameskill == sk_nightmare)
+ num <<= 1;
+
+ oldammo = player->ammo[ammo];
+ player->ammo[ammo] += num;
+
+ if (player->ammo[ammo] > player->maxammo[ammo])
+ player->ammo[ammo] = player->maxammo[ammo];
+
+ // If non zero ammo, don't change up weapons, player was lower on purpose.
+ if (oldammo)
+ return true;
+
+ // We were down to zero, so select a new weapon.
+ // Preferences are not user selectable.
+
+ switch (ammo)
+ {
+ case am_clip:
+ if (player->readyweapon == wp_fist) {
+ if (player->weaponowned[wp_chaingun])
+ player->pendingweapon = wp_chaingun;
+ else
+ player->pendingweapon = wp_pistol;
+ }
+ break;
+
+ case am_shell:
+ if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol)
+ if (player->weaponowned[wp_shotgun])
+ player->pendingweapon = wp_shotgun;
+ break;
+
+ case am_cell:
+ if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol)
+ if (player->weaponowned[wp_plasma])
+ player->pendingweapon = wp_plasma;
+ break;
+
+ case am_misl:
+ if (player->readyweapon == wp_fist)
+ if (player->weaponowned[wp_missile])
+ player->pendingweapon = wp_missile;
+ default:
+ break;
+ }
+ return true;
+}
+
+//
+// P_GiveWeapon
+// The weapon name may have a MF_DROPPED flag ored in.
+//
+
+boolean P_GiveWeapon(player_t *player, weapontype_t weapon, boolean dropped)
+{
+ boolean gaveammo;
+ boolean gaveweapon;
+
+ if (netgame && deathmatch!=2 && !dropped)
+ {
+ // leave placed weapons forever on net games
+ if (player->weaponowned[weapon])
+ return false;
+
+ player->bonuscount += BONUSADD;
+ player->weaponowned[weapon] = true;
+
+ P_GiveAmmo(player, weaponinfo[weapon].ammo, deathmatch ? 5 : 2);
+
+ player->pendingweapon = weapon;
+ /* cph 20028/10 - for old-school DM addicts, allow old behavior
+ * where only consoleplayer's pickup sounds are heard */
+ if (!comp[comp_sound] || player == &players[consoleplayer])
+ S_StartSound (player->mo, sfx_wpnup|PICKUP_SOUND); // killough 4/25/98
+ return false;
+ }
+
+ if (weaponinfo[weapon].ammo != am_noammo)
+ {
+ // give one clip with a dropped weapon,
+ // two clips with a found weapon
+ gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, dropped ? 1 : 2);
+ }
+ else
+ gaveammo = false;
+
+ if (player->weaponowned[weapon])
+ gaveweapon = false;
+ else
+ {
+ gaveweapon = true;
+ player->weaponowned[weapon] = true;
+ player->pendingweapon = weapon;
+ }
+ return gaveweapon || gaveammo;
+}
+
+//
+// P_GiveBody
+// Returns false if the body isn't needed at all
+//
+
+boolean P_GiveBody(player_t *player, int num)
+{
+ if (player->health >= maxhealth)
+ return false; // Ty 03/09/98 externalized MAXHEALTH to maxhealth
+ player->health += num;
+ if (player->health > maxhealth)
+ player->health = maxhealth;
+ player->mo->health = player->health;
+ return true;
+}
+
+//
+// P_GiveArmor
+// Returns false if the armor is worse
+// than the current armor.
+//
+
+boolean P_GiveArmor(player_t *player, int armortype)
+{
+ int hits = armortype*100;
+ if (player->armorpoints >= hits)
+ return false; // don't pick up
+ player->armortype = armortype;
+ player->armorpoints = hits;
+ return true;
+}
+
+//
+// P_GiveCard
+//
+
+void P_GiveCard(player_t *player, card_t card)
+{
+ if (player->cards[card])
+ return;
+ player->bonuscount = BONUSADD;
+ player->cards[card] = 1;
+}
+
+//
+// P_GivePower
+//
+// Rewritten by Lee Killough
+//
+
+boolean P_GivePower(player_t *player, int power)
+{
+ static const int tics[NUMPOWERS] = {
+ INVULNTICS, 1 /* strength */, INVISTICS,
+ IRONTICS, 1 /* allmap */, INFRATICS,
+ };
+
+ switch (power)
+ {
+ case pw_invisibility:
+ player->mo->flags |= MF_SHADOW;
+ break;
+ case pw_allmap:
+ if (player->powers[pw_allmap])
+ return false;
+ break;
+ case pw_strength:
+ P_GiveBody(player,100);
+ break;
+ }
+
+ // Unless player has infinite duration cheat, set duration (killough)
+
+ if (player->powers[power] >= 0)
+ player->powers[power] = tics[power];
+ return true;
+}
+
+//
+// P_TouchSpecialThing
+//
+
+void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher)
+{
+ player_t *player;
+ int i;
+ int sound;
+ fixed_t delta = special->z - toucher->z;
+
+ if (delta > toucher->height || delta < -8*FRACUNIT)
+ return; // out of reach
+
+ sound = sfx_itemup;
+ player = toucher->player;
+
+ // Dead thing touching.
+ // Can happen with a sliding player corpse.
+ if (toucher->health <= 0)
+ return;
+
+ // Identify by sprite.
+ switch (special->sprite)
+ {
+ // armor
+ case SPR_ARM1:
+ if (!P_GiveArmor (player, green_armor_class))
+ return;
+ player->message = GOTARMOR; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_ARM2:
+ if (!P_GiveArmor (player, blue_armor_class))
+ return;
+ player->message = GOTMEGA; // Ty 03/22/98 - externalized
+ break;
+
+ // bonus items
+ case SPR_BON1:
+ player->health++; // can go over 100%
+ if (player->health > (maxhealth * 2))
+ player->health = (maxhealth * 2);
+ player->mo->health = player->health;
+ player->message = GOTHTHBONUS; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_BON2:
+ player->armorpoints++; // can go over 100%
+ if (player->armorpoints > max_armor)
+ player->armorpoints = max_armor;
+ if (!player->armortype)
+ player->armortype = green_armor_class;
+ player->message = GOTARMBONUS; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_SOUL:
+ player->health += soul_health;
+ if (player->health > max_soul)
+ player->health = max_soul;
+ player->mo->health = player->health;
+ player->message = GOTSUPER; // Ty 03/22/98 - externalized
+ sound = sfx_getpow;
+ break;
+
+ case SPR_MEGA:
+ if (gamemode != commercial)
+ return;
+ player->health = mega_health;
+ player->mo->health = player->health;
+ P_GiveArmor (player,blue_armor_class);
+ player->message = GOTMSPHERE; // Ty 03/22/98 - externalized
+ sound = sfx_getpow;
+ break;
+
+ // cards
+ // leave cards for everyone
+ case SPR_BKEY:
+ if (!player->cards[it_bluecard])
+ player->message = GOTBLUECARD; // Ty 03/22/98 - externalized
+ P_GiveCard (player, it_bluecard);
+ if (!netgame)
+ break;
+ return;
+
+ case SPR_YKEY:
+ if (!player->cards[it_yellowcard])
+ player->message = GOTYELWCARD; // Ty 03/22/98 - externalized
+ P_GiveCard (player, it_yellowcard);
+ if (!netgame)
+ break;
+ return;
+
+ case SPR_RKEY:
+ if (!player->cards[it_redcard])
+ player->message = GOTREDCARD; // Ty 03/22/98 - externalized
+ P_GiveCard (player, it_redcard);
+ if (!netgame)
+ break;
+ return;
+
+ case SPR_BSKU:
+ if (!player->cards[it_blueskull])
+ player->message = GOTBLUESKUL; // Ty 03/22/98 - externalized
+ P_GiveCard (player, it_blueskull);
+ if (!netgame)
+ break;
+ return;
+
+ case SPR_YSKU:
+ if (!player->cards[it_yellowskull])
+ player->message = GOTYELWSKUL; // Ty 03/22/98 - externalized
+ P_GiveCard (player, it_yellowskull);
+ if (!netgame)
+ break;
+ return;
+
+ case SPR_RSKU:
+ if (!player->cards[it_redskull])
+ player->message = GOTREDSKULL; // Ty 03/22/98 - externalized
+ P_GiveCard (player, it_redskull);
+ if (!netgame)
+ break;
+ return;
+
+ // medikits, heals
+ case SPR_STIM:
+ if (!P_GiveBody (player, 10))
+ return;
+ player->message = GOTSTIM; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_MEDI:
+ if (!P_GiveBody (player, 25))
+ return;
+
+ if (player->health < 50) // cph - 25 + the 25 just added, thanks to Quasar for reporting this bug
+ player->message = GOTMEDINEED; // Ty 03/22/98 - externalized
+ else
+ player->message = GOTMEDIKIT; // Ty 03/22/98 - externalized
+ break;
+
+
+ // power ups
+ case SPR_PINV:
+ if (!P_GivePower (player, pw_invulnerability))
+ return;
+ player->message = GOTINVUL; // Ty 03/22/98 - externalized
+ sound = sfx_getpow;
+ break;
+
+ case SPR_PSTR:
+ if (!P_GivePower (player, pw_strength))
+ return;
+ player->message = GOTBERSERK; // Ty 03/22/98 - externalized
+ if (player->readyweapon != wp_fist)
+ player->pendingweapon = wp_fist;
+ sound = sfx_getpow;
+ break;
+
+ case SPR_PINS:
+ if (!P_GivePower (player, pw_invisibility))
+ return;
+ player->message = GOTINVIS; // Ty 03/22/98 - externalized
+ sound = sfx_getpow;
+ break;
+
+ case SPR_SUIT:
+ if (!P_GivePower (player, pw_ironfeet))
+ return;
+ player->message = GOTSUIT; // Ty 03/22/98 - externalized
+ sound = sfx_getpow;
+ break;
+
+ case SPR_PMAP:
+ if (!P_GivePower (player, pw_allmap))
+ return;
+ player->message = GOTMAP; // Ty 03/22/98 - externalized
+ sound = sfx_getpow;
+ break;
+
+ case SPR_PVIS:
+ if (!P_GivePower (player, pw_infrared))
+ return;
+ player->message = GOTVISOR; // Ty 03/22/98 - externalized
+ sound = sfx_getpow;
+ break;
+
+ // ammo
+ case SPR_CLIP:
+ if (special->flags & MF_DROPPED)
+ {
+ if (!P_GiveAmmo (player,am_clip,0))
+ return;
+ }
+ else
+ {
+ if (!P_GiveAmmo (player,am_clip,1))
+ return;
+ }
+ player->message = GOTCLIP; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_AMMO:
+ if (!P_GiveAmmo (player, am_clip,5))
+ return;
+ player->message = GOTCLIPBOX; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_ROCK:
+ if (!P_GiveAmmo (player, am_misl,1))
+ return;
+ player->message = GOTROCKET; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_BROK:
+ if (!P_GiveAmmo (player, am_misl,5))
+ return;
+ player->message = GOTROCKBOX; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_CELL:
+ if (!P_GiveAmmo (player, am_cell,1))
+ return;
+ player->message = GOTCELL; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_CELP:
+ if (!P_GiveAmmo (player, am_cell,5))
+ return;
+ player->message = GOTCELLBOX; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_SHEL:
+ if (!P_GiveAmmo (player, am_shell,1))
+ return;
+ player->message = GOTSHELLS; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_SBOX:
+ if (!P_GiveAmmo (player, am_shell,5))
+ return;
+ player->message = GOTSHELLBOX; // Ty 03/22/98 - externalized
+ break;
+
+ case SPR_BPAK:
+ if (!player->backpack)
+ {
+ for (i=0 ; i<NUMAMMO ; i++)
+ player->maxammo[i] *= 2;
+ player->backpack = true;
+ }
+ for (i=0 ; i<NUMAMMO ; i++)
+ P_GiveAmmo (player, i, 1);
+ player->message = GOTBACKPACK; // Ty 03/22/98 - externalized
+ break;
+
+ // weapons
+ case SPR_BFUG:
+ if (!P_GiveWeapon (player, wp_bfg, false) )
+ return;
+ player->message = GOTBFG9000; // Ty 03/22/98 - externalized
+ sound = sfx_wpnup;
+ break;
+
+ case SPR_MGUN:
+ if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED)!=0) )
+ return;
+ player->message = GOTCHAINGUN; // Ty 03/22/98 - externalized
+ sound = sfx_wpnup;
+ break;
+
+ case SPR_CSAW:
+ if (!P_GiveWeapon (player, wp_chainsaw, false) )
+ return;
+ player->message = GOTCHAINSAW; // Ty 03/22/98 - externalized
+ sound = sfx_wpnup;
+ break;
+
+ case SPR_LAUN:
+ if (!P_GiveWeapon (player, wp_missile, false) )
+ return;
+ player->message = GOTLAUNCHER; // Ty 03/22/98 - externalized
+ sound = sfx_wpnup;
+ break;
+
+ case SPR_PLAS:
+ if (!P_GiveWeapon (player, wp_plasma, false) )
+ return;
+ player->message = GOTPLASMA; // Ty 03/22/98 - externalized
+ sound = sfx_wpnup;
+ break;
+
+ case SPR_SHOT:
+ if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED)!=0 ) )
+ return;
+ player->message = GOTSHOTGUN; // Ty 03/22/98 - externalized
+ sound = sfx_wpnup;
+ break;
+
+ case SPR_SGN2:
+ if (!P_GiveWeapon(player, wp_supershotgun, (special->flags&MF_DROPPED)!=0))
+ return;
+ player->message = GOTSHOTGUN2; // Ty 03/22/98 - externalized
+ sound = sfx_wpnup;
+ break;
+
+ default:
+ I_Error ("P_SpecialThing: Unknown gettable thing");
+ }
+
+ if (special->flags & MF_COUNTITEM)
+ player->itemcount++;
+ P_RemoveMobj (special);
+ player->bonuscount += BONUSADD;
+
+ /* cph 20028/10 - for old-school DM addicts, allow old behavior
+ * where only consoleplayer's pickup sounds are heard */
+ if (!comp[comp_sound] || player == &players[consoleplayer])
+ S_StartSound (player->mo, sound | PICKUP_SOUND); // killough 4/25/98
+}
+
+//
+// KillMobj
+//
+// killough 11/98: make static
+static void P_KillMobj(mobj_t *source, mobj_t *target)
+{
+ mobjtype_t item;
+ mobj_t *mo;
+
+ target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY);
+
+ if (target->type != MT_SKULL)
+ target->flags &= ~MF_NOGRAVITY;
+
+ target->flags |= MF_CORPSE|MF_DROPOFF;
+ target->height >>= 2;
+
+ if (!((target->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL)))
+ totallive--;
+
+ if (source && source->player)
+ {
+ // count for intermission
+ if (target->flags & MF_COUNTKILL)
+ source->player->killcount++;
+ if (target->player)
+ source->player->frags[target->player-players]++;
+ }
+ else
+ if (target->flags & MF_COUNTKILL) { /* Add to kills tally */
+ if ((compatibility_level < lxdoom_1_compatibility) || !netgame) {
+ if (!netgame)
+ // count all monster deaths,
+ // even those caused by other monsters
+ players[0].killcount++;
+ } else
+ if (!deathmatch) {
+ // try and find a player to give the kill to, otherwise give the
+ // kill to a random player. this fixes the missing monsters bug
+ // in coop - rain
+ // CPhipps - not a bug as such, but certainly an inconsistency.
+ if (target->lastenemy && target->lastenemy->health > 0
+ && target->lastenemy->player) // Fighting a player
+ target->lastenemy->player->killcount++;
+ else {
+ // cph - randomely choose a player in the game to be credited
+ // and do it uniformly between the active players
+ unsigned int activeplayers = 0, player, i;
+
+ for (player = 0; player<MAXPLAYERS; player++)
+ if (playeringame[player])
+ activeplayers++;
+
+ if (activeplayers) {
+ player = P_Random(pr_friends) % activeplayers;
+
+ for (i=0; i<MAXPLAYERS; i++)
+ if (playeringame[i])
+ if (!player--)
+ players[i].killcount++;
+ }
+ }
+ }
+ }
+
+ if (target->player)
+ {
+ // count environment kills against you
+ if (!source)
+ target->player->frags[target->player-players]++;
+
+ target->flags &= ~MF_SOLID;
+ target->player->playerstate = PST_DEAD;
+ P_DropWeapon (target->player);
+
+ if (target->player == &players[consoleplayer] && (automapmode & am_active))
+ AM_Stop(); // don't die in auto map; switch view prior to dying
+ }
+
+ if (target->health < -target->info->spawnhealth && target->info->xdeathstate)
+ P_SetMobjState (target, target->info->xdeathstate);
+ else
+ P_SetMobjState (target, target->info->deathstate);
+
+ target->tics -= P_Random(pr_killtics)&3;
+
+ if (target->tics < 1)
+ target->tics = 1;
+
+ // Drop stuff.
+ // This determines the kind of object spawned
+ // during the death frame of a thing.
+
+ switch (target->type)
+ {
+ case MT_WOLFSS:
+ case MT_POSSESSED:
+ item = MT_CLIP;
+ break;
+
+ case MT_SHOTGUY:
+ item = MT_SHOTGUN;
+ break;
+
+ case MT_CHAINGUY:
+ item = MT_CHAINGUN;
+ break;
+
+ default:
+ return;
+ }
+
+ mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item);
+ mo->flags |= MF_DROPPED; // special versions of items
+}
+
+//
+// P_DamageMobj
+// Damages both enemies and players
+// "inflictor" is the thing that caused the damage
+// creature or missile, can be NULL (slime, etc)
+// "source" is the thing to target after taking damage
+// creature or NULL
+// Source and inflictor are the same for melee attacks.
+// Source can be NULL for slime, barrel explosions
+// and other environmental stuff.
+//
+
+void P_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage)
+{
+ player_t *player;
+ boolean justhit; /* killough 11/98 */
+
+ /* killough 8/31/98: allow bouncers to take damage */
+ if (!(target->flags & (MF_SHOOTABLE | MF_BOUNCES)))
+ return; // shouldn't happen...
+
+ if (target->health <= 0)
+ return;
+
+ /* proff 11/22/98: Andy Baker's Stealth monsters */
+ if (target->flags & MF_STEALTH)
+ P_BecomeVisible(target);
+
+ if (target->flags & MF_SKULLFLY)
+ target->momx = target->momy = target->momz = 0;
+
+ player = target->player;
+ if (player && gameskill == sk_baby)
+ damage >>= 1; // take half damage in trainer mode
+
+ // Some close combat weapons should not
+ // inflict thrust and push the victim out of reach,
+ // thus kick away unless using the chainsaw.
+
+ if (inflictor && !(target->flags & MF_NOCLIP) &&
+ (!source || !source->player ||
+ source->player->readyweapon != wp_chainsaw))
+ {
+ unsigned ang = R_PointToAngle2 (inflictor->x, inflictor->y,
+ target->x, target->y);
+
+ fixed_t thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
+
+ // make fall forwards sometimes
+ if ( damage < 40 && damage > target->health
+ && target->z - inflictor->z > 64*FRACUNIT
+ && P_Random(pr_damagemobj) & 1)
+ {
+ ang += ANG180;
+ thrust *= 4;
+ }
+
+ ang >>= ANGLETOFINESHIFT;
+ target->momx += FixedMul (thrust, finecosine[ang]);
+ target->momy += FixedMul (thrust, finesine[ang]);
+
+ /* killough 11/98: thrust objects hanging off ledges */
+ if (target->intflags & MIF_FALLING && target->gear >= MAXGEAR)
+ target->gear = 0;
+ }
+
+ // player specific
+ if (player)
+ {
+ // end of game hell hack
+ if (target->subsector->sector->special == 11 && damage >= target->health)
+ damage = target->health - 1;
+
+ // Below certain threshold,
+ // ignore damage in GOD mode, or with INVUL power.
+ // killough 3/26/98: make god mode 100% god mode in non-compat mode
+
+ if ((damage < 1000 || (!comp[comp_god] && (player->cheats&CF_GODMODE))) &&
+ (player->cheats&CF_GODMODE || player->powers[pw_invulnerability]))
+ return;
+
+ if (player->armortype)
+ {
+ int saved = player->armortype == 1 ? damage/3 : damage/2;
+ if (player->armorpoints <= saved)
+ {
+ // armor is used up
+ saved = player->armorpoints;
+ player->armortype = 0;
+ }
+ player->armorpoints -= saved;
+ damage -= saved;
+ }
+
+ player->health -= damage; // mirror mobj health here for Dave
+ if (player->health < 0)
+ player->health = 0;
+
+ player->attacker = source;
+ player->damagecount += damage; // add damage after armor / invuln
+
+ if (player->damagecount > 100)
+ player->damagecount = 100; // teleport stomp does 10k points...
+ }
+
+ // do the damage
+ target->health -= damage;
+ if (target->health <= 0)
+ {
+ P_KillMobj (source, target);
+ return;
+ }
+
+ // killough 9/7/98: keep track of targets so that friends can help friends
+ if (mbf_features)
+ {
+ /* If target is a player, set player's target to source,
+ * so that a friend can tell who's hurting a player
+ */
+ if (player)
+ P_SetTarget(&target->target, source);
+
+ /* killough 9/8/98:
+ * If target's health is less than 50%, move it to the front of its list.
+ * This will slightly increase the chances that enemies will choose to
+ * "finish it off", but its main purpose is to alert friends of danger.
+ */
+ if (target->health*2 < target->info->spawnhealth)
+ {
+ thinker_t *cap = &thinkerclasscap[target->flags & MF_FRIEND ?
+ th_friends : th_enemies];
+ (target->thinker.cprev->cnext = target->thinker.cnext)->cprev =
+ target->thinker.cprev;
+ (target->thinker.cnext = cap->cnext)->cprev = &target->thinker;
+ (target->thinker.cprev = cap)->cnext = &target->thinker;
+ }
+ }
+
+ if ((justhit = (P_Random (pr_painchance) < target->info->painchance &&
+ !(target->flags & MF_SKULLFLY)))) //killough 11/98: see below
+ P_SetMobjState(target, target->info->painstate);
+
+ target->reactiontime = 0; // we're awake now...
+
+ /* killough 9/9/98: cleaned up, made more consistent: */
+
+ if (source && source != target && source->type != MT_VILE &&
+ (!target->threshold || target->type == MT_VILE) &&
+ ((source->flags ^ target->flags) & MF_FRIEND ||
+ monster_infighting ||
+ !mbf_features))
+ {
+ /* if not intent on another player, chase after this one
+ *
+ * killough 2/15/98: remember last enemy, to prevent
+ * sleeping early; 2/21/98: Place priority on players
+ * killough 9/9/98: cleaned up, made more consistent:
+ */
+
+ if (!target->lastenemy || target->lastenemy->health <= 0 ||
+ (!mbf_features ?
+ !target->lastenemy->player :
+ !((target->flags ^ target->lastenemy->flags) & MF_FRIEND) &&
+ target->target != source)) // remember last enemy - killough
+ P_SetTarget(&target->lastenemy, target->target);
+
+ P_SetTarget(&target->target, source); // killough 11/98
+ target->threshold = BASETHRESHOLD;
+ if (target->state == &states[target->info->spawnstate]
+ && target->info->seestate != S_NULL)
+ P_SetMobjState (target, target->info->seestate);
+ }
+
+ /* killough 11/98: Don't attack a friend, unless hit by that friend. */
+ if (justhit && (target->target == source || !target->target ||
+ !(target->flags & target->target->flags & MF_FRIEND)))
+ target->flags |= MF_JUSTHIT; // fight back!
+}