ref: ad3e1a25f971bea771960bb9efee75a277e56e2c
dir: /src/strife/p_inter.c/
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// 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.
//
// DESCRIPTION:
// Handling interactions (i.e., collisions).
//
// Data.
#include "doomdef.h"
#include "dstrings.h"
#include "sounds.h"
#include "deh_main.h"
#include "deh_misc.h"
#include "doomstat.h"
#include "m_misc.h"
#include "m_random.h"
#include "i_system.h"
#include "am_map.h"
#include "p_local.h"
#include "p_dialog.h" // villsa [STRIFE]
#include "s_sound.h"
#include "p_inter.h"
#include "hu_stuff.h" // villsa [STRIFE]
#include "z_zone.h" // villsa [STRIFE]
// haleyjd [STRIFE]
#include "w_wad.h"
#include "p_pspr.h"
#include "p_dialog.h"
#include "f_finale.h"
#define BONUSADD 6
// a weapon is found with two clip loads,
// a big item has five clip loads
// villsa [STRIFE] updated arrays
int maxammo[NUMAMMO] = { 250, 50, 25, 400, 100, 30, 16 };
int clipammo[NUMAMMO] = { 10, 4, 2, 20, 4, 6, 4 };
//
// 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
//
// [STRIFE] Modified for Strife ammo types
//
boolean P_GiveAmmo(player_t* player, ammotype_t ammo, int num)
{
int oldammo;
if(ammo == am_noammo)
return false;
if(ammo > NUMAMMO)
I_Error ("P_GiveAmmo: bad type %i", ammo);
if(player->ammo[ammo] == player->maxammo[ammo])
return false;
if(num)
num *= clipammo[ammo];
else
num = clipammo[ammo]/2;
if(gameskill == sk_baby
|| gameskill == sk_nightmare)
{
// give double ammo in trainer mode,
// you'll need in 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.
// villsa [STRIFE] ammo update
// where's the check for grenades? - haleyjd: verified no switch to grenades
// haleyjd 10/03/10: don't change to electric bow when picking up poison
// arrows.
if(!player->readyweapon)
{
switch(ammo)
{
case am_bullets:
if(player->weaponowned[wp_rifle])
player->pendingweapon = wp_rifle;
break;
case am_elecbolts:
if(player->weaponowned[wp_elecbow])
player->pendingweapon = wp_elecbow;
break;
case am_cell:
if(player->weaponowned[wp_mauler])
player->pendingweapon = wp_mauler;
break;
case am_missiles:
if(player->weaponowned[wp_missile])
player->pendingweapon = wp_missile;
break;
default:
break;
}
}
return true;
}
//
// P_GiveWeapon
// The weapon name may have a MF_DROPPED flag ored in.
//
// villsa [STRIFE] some stuff has been changed/moved around
//
boolean P_GiveWeapon(player_t* player, weapontype_t weapon, boolean dropped)
{
boolean gaveammo;
boolean gaveweapon;
// villsa [STRIFE] new code for giving alternate version
// of the weapon to player
if(player->weaponowned[weapon])
gaveweapon = false;
else
{
gaveweapon = true;
player->weaponowned[weapon] = true;
// Alternate "sister" weapons that you also get as a bonus:
switch(weapon)
{
case wp_elecbow:
player->weaponowned[wp_poisonbow] = true;
break;
case wp_hegrenade:
player->weaponowned[wp_wpgrenade] = true;
break;
case wp_mauler:
player->weaponowned[wp_torpedo] = true;
break;
default:
break;
}
// check for the standard weapons only
if(weapon > player->readyweapon && weapon <= wp_sigil)
player->pendingweapon = weapon;
}
if(netgame && (deathmatch != 2) && !dropped)
{
// leave placed weapons forever on net games
if(!gaveweapon)
return false;
player->bonuscount += BONUSADD;
player->weaponowned[weapon] = true;
if(deathmatch)
P_GiveAmmo(player, weaponinfo[weapon].ammo, 5);
else
P_GiveAmmo(player, weaponinfo[weapon].ammo, 2);
if(player == &players[consoleplayer])
S_StartSound (NULL, sfx_wpnup);
return false;
}
if(weaponinfo[weapon].ammo != am_noammo)
{
// give one clip with a dropped weapon,
// two clips with a found weapon
if(dropped)
gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 1);
else
gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
}
else
gaveammo = false;
return(gaveweapon || gaveammo);
}
//
// P_GiveBody
// Returns false if the body isn't needed at all
//
// villsa [STRIFE] a lot of changes have been added for stamina
//
boolean P_GiveBody(player_t* player, int num)
{
int maxhealth;
int healing;
maxhealth = MAXHEALTH + player->stamina;
if(num >= 0) // haleyjd 20100923: fixed to give proper amount of health
{
mobj_t *mo; // haleyjd 20110225: needed below...
// any healing to do?
if(player->health >= maxhealth)
return false;
// give, and cap to maxhealth
player->health += num;
if(player->health >= maxhealth)
player->health = maxhealth;
// Set mo->health for consistency.
// haleyjd 20110225: Seems Strife can call this on a NULL player->mo
// when giving items to players that are not in the game...
mo = P_SubstNullMobj(player->mo);
mo->health = player->health;
}
else
{
// [STRIFE] handle healing from the Front's medic
// The amount the player's health will be set to scales up with stamina
// increases.
// Ex 1: On the wimpiest skill level, -100 is sent in. This restores
// full health no matter what your stamina.
// (100*100)/100 = 100
// (200*100)/100 = 200
// Ex 2: On the most stringent skill levels, -50 is sent in. This will
// restore at most half of your health.
// (100*50)/100 = 50
// (200*50)/100 = 100
healing = (-num * maxhealth) / MAXHEALTH;
// This is also the "threshold" of healing. You need less health than
// the amount that will be restored in order to get any benefit.
// So on the easiest skill you will always be fully healed.
// On the hardest skill you must have less than 50 health, and will
// only recover to 50 (assuming base stamina stat)
if(player->health >= healing)
return false;
// Set health. BUG: Oddly, mo->health is NOT set here...
player->health = healing;
}
return true;
}
//
// P_GiveArmor
// Returns false if the armor is worse
// than the current armor.
//
// [STRIFE] Modified for Strife armor items
//
boolean P_GiveArmor(player_t* player, int armortype)
{
int hits;
// villsa [STRIFE]
if(armortype < 0)
{
if(player->armorpoints)
return false;
armortype = -armortype;
}
hits = armortype * 100;
if(player->armorpoints >= hits)
return false; // don't pick up
player->armortype = armortype;
player->armorpoints = hits;
return true;
}
//
// P_GiveCard
//
// [STRIFE] Modified to use larger bonuscount
//
boolean P_GiveCard(player_t* player, card_t card)
{
if (player->cards[card])
return false;
// villsa [STRIFE] multiply by 2
player->bonuscount = BONUSADD * 2;
player->cards[card] = true;
return true;
}
//
// P_GivePower
//
// [STRIFE] Modifications for new powerups
//
boolean P_GivePower(player_t* player, powertype_t power)
{
// haleyjd 09/14/10: [STRIFE] moved to top, exception for Shadow Armor
if(player->powers[power] && power != pw_invisibility)
return false; // already got it
// if giving pw_invisibility and player already has MVIS, no can do.
if(power == pw_invisibility && (player->mo->flags & MF_MVIS))
return false;
// villsa [STRIFE]
if(power == pw_targeter)
{
player->powers[power] = TARGTICS;
P_SetPsprite(player, ps_targcenter, S_TRGT_00); // 10
P_SetPsprite(player, ps_targleft, S_TRGT_01); // 11
P_SetPsprite(player, ps_targright, S_TRGT_02); // 12
player->psprites[ps_targcenter].sx = (160*FRACUNIT);
player->psprites[ps_targleft ].sy = (100*FRACUNIT);
player->psprites[ps_targcenter].sy = (100*FRACUNIT);
player->psprites[ps_targright ].sy = (100*FRACUNIT);
return true;
}
if(power == pw_invisibility)
{
// if player already had this power...
if(player->powers[power])
{
// remove SHADOW, give MVIS.
player->mo->flags &= ~MF_SHADOW;
player->mo->flags |= MF_MVIS;
}
else // give SHADOW
player->mo->flags |= MF_SHADOW;
// set tics if giving shadow, or renew them if MVIS.
player->powers[power] = INVISTICS;
return true;
}
if(power == pw_ironfeet)
{
player->powers[power] = IRONTICS;
return true;
}
if(power == pw_strength)
{
P_GiveBody(player, 100);
player->powers[power] = 1;
return true;
}
// villsa [STRIFE]
if(power == pw_allmap)
{
// remember in mapstate
if(gamemap < 40)
player->mapstate[gamemap] = true;
player->powers[power] = 1;
return true;
}
// villsa [STRIFE]
if(power == pw_communicator)
{
player->powers[power] = 1;
return true;
}
// default behavior:
player->powers[power] = 1;
return true;
}
// villsa [STRIFE]
static char pickupmsg[80];
//
// P_TouchSpecialThing
//
// [STRIFE] Rewritten for Strife collectables.
//
void P_TouchSpecialThing(mobj_t* special, mobj_t* toucher)
{
player_t* player;
int i;
fixed_t delta;
int sound;
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;
// villsa [STRIFE] damage toucher if special is spectral
// haleyjd 09/21/10: corrected to test for SPECTRE thingtypes specifically
switch(special->type)
{
case MT_SPECTRE_A:
case MT_SPECTRE_B:
case MT_SPECTRE_C:
case MT_SPECTRE_D:
case MT_SPECTRE_E:
case MT_ENTITY:
case MT_SUBENTITY:
P_DamageMobj(toucher, NULL, NULL, 5);
return;
default:
break;
}
// villsa [STRIFE]
pickupmsg[0] = 0;
// Identify by sprite.
// villsa [STRIFE] new items
switch(special->sprite)
{
// bullets
case SPR_BLIT: // haleyjd: fixed missing MF_DROPPED check
if(!P_GiveAmmo(player, am_bullets, !(special->flags & MF_DROPPED)))
return;
break;
// box of bullets
case SPR_BBOX:
if(!P_GiveAmmo(player, am_bullets, 5))
return;
break;
// missile
case SPR_ROKT:
if(!P_GiveAmmo(player, am_missiles, 1))
return;
break;
// box of missiles
case SPR_MSSL:
if(!P_GiveAmmo(player, am_missiles, 5))
return;
break;
// battery
case SPR_BRY1:
if(!P_GiveAmmo(player, am_cell, 1))
return;
break;
// cell pack
case SPR_CPAC:
if(!P_GiveAmmo(player, am_cell, 5))
return;
break;
// poison bolts
case SPR_PQRL:
if(!P_GiveAmmo(player, am_poisonbolts, 5))
return;
break;
// electric bolts
case SPR_XQRL:
if(!P_GiveAmmo(player, am_elecbolts, 5))
return;
break;
// he grenades
case SPR_GRN1:
if(!P_GiveAmmo(player, am_hegrenades, 1))
return;
break;
// wp grenades
case SPR_GRN2:
if(!P_GiveAmmo(player, am_wpgrenades, 1))
return;
break;
// rifle
case SPR_RIFL:
if(!P_GiveWeapon(player, wp_rifle, special->flags & MF_DROPPED))
return;
sound = sfx_wpnup; // haleyjd: SHK-CHK!
break;
// flame thrower
case SPR_FLAM:
if(!P_GiveWeapon(player, wp_flame, false))
return;
// haleyjd: gives extra ammo.
P_GiveAmmo(player, am_cell, 3);
sound = sfx_wpnup; // haleyjd: SHK-CHK!
break;
// missile launcher
case SPR_MMSL:
if(!P_GiveWeapon(player, wp_missile, false))
return;
sound = sfx_wpnup; // haleyjd: SHK-CHK!
break;
// grenade launcher
case SPR_GRND:
if(!P_GiveWeapon(player, wp_hegrenade, special->flags & MF_DROPPED))
return;
sound = sfx_wpnup; // haleyjd: SHK-CHK!
break;
// mauler
case SPR_TRPD:
if(!P_GiveWeapon(player, wp_mauler, false))
return;
sound = sfx_wpnup; // haleyjd: SHK-CHK!
break;
// electric bolt crossbow
case SPR_CBOW:
if(!P_GiveWeapon(player, wp_elecbow, special->flags & MF_DROPPED))
return;
sound = sfx_wpnup; // haleyjd: SHK-CHK!
break;
// haleyjd 09/21/10: missed case: THE SIGIL!
case SPR_SIGL:
if(!P_GiveWeapon(player, wp_sigil, special->flags & MF_DROPPED))
{
player->sigiltype = special->frame;
return;
}
if(netgame)
player->sigiltype = 4;
player->pendingweapon = wp_sigil;
player->st_update = true;
if(deathmatch)
return;
sound = sfx_wpnup;
break;
// backpack
case SPR_BKPK:
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);
break;
// 1 Gold
case SPR_COIN:
P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
break;
// 10 Gold
case SPR_CRED:
for(i = 0; i < 10; i++)
P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
break;
// 25 Gold
case SPR_SACK:
// haleyjd 09/21/10: missed code: if a SPR_SACK object has negative
// health, it will give that much gold - STRIFE-TODO: verify
if(special->health < 0)
{
for(i = special->health; i != 0; i++)
P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
}
else
{
for(i = 0; i < 25; i++)
P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
}
break;
// 50 Gold
case SPR_CHST:
for(i = 0; i < 50; i++)
P_GiveInventoryItem(player, SPR_COIN, MT_MONY_1);
break;
// Leather Armor
case SPR_ARM1:
if(!P_GiveArmor(player, -2))
if(!P_GiveInventoryItem(player, special->sprite, special->type))
pickupmsg[0] = '!';
break;
// Metal Armor
case SPR_ARM2:
if(!P_GiveArmor(player, -1))
if(!P_GiveInventoryItem(player, special->sprite, special->type))
pickupmsg[0] = '!';
break;
// All-map powerup
case SPR_PMAP:
if(!P_GivePower(player, pw_allmap))
return;
sound = sfx_yeah;
break;
// The Comm Unit - because you need Blackbird whining in your ear the
// whole time and telling you how lost she is :P
case SPR_COMM:
if(!P_GivePower(player, pw_communicator))
return;
sound = sfx_yeah;
break;
// haleyjd 09/21/10: missed case - Shadow Armor; though, I do not know why
// this has a case distinct from generic inventory items... Maybe it started
// out as an auto-use-if-possible item much like regular armor...
case SPR_SHD1:
if(!P_GiveInventoryItem(player, SPR_SHD1, special->type))
pickupmsg[0] = '!';
break;
// villsa [STRIFE] check default items
case SPR_TOKN:
default:
if(special->type >= MT_KEY_BASE && special->type <= MT_NEWKEY5)
{
// haleyjd 09/21/10: Strife player still picks up keys that
// he has already found. (break, not return)
if(!P_GiveCard(player, special->type - MT_KEY_BASE))
break;
}
else
{
if(!P_GiveInventoryItem(player, special->sprite, special->type))
pickupmsg[0] = '!';
}
break;
}
// villsa [STRIFE] set message
if(!pickupmsg[0])
{
if(special->info->name)
{
DEH_snprintf(pickupmsg, sizeof(pickupmsg),
"You picked up the %s.", DEH_String(special->info->name));
}
else
DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You picked up the item.");
}
// use the first character to indicate that the player is full on items
else if(pickupmsg[0] == '!')
{
DEH_snprintf(pickupmsg, sizeof(pickupmsg), "You cannot hold any more.");
player->message = pickupmsg;
return;
}
if(special->flags & MF_GIVEQUEST)
{
// [STRIFE]: Award quest flag based on the thing's speed. Quest 8 was
// apparently at some point given by the Broken Power Coupling, which is
// why they don't want to award it if you have Quest 6 (which is
// acquired by destroying the Front's working power coupling). BUT, the
// broken coupling object's speed is NOT 8... it is 512*FRACUNIT. For
// strict portability beyond the x86, we need to AND the operand by 31.
if(special->info->speed != 8 || !(player->questflags & QF_QUEST6))
player->questflags |= 1 << ((special->info->speed - 1) & 31);
}
// haleyjd 08/30/10: [STRIFE] No itemcount
//if (special->flags & MF_COUNTITEM)
// player->itemcount++;
P_RemoveMobj(special);
player->message = pickupmsg;
player->bonuscount += BONUSADD;
if(player == &players[consoleplayer])
S_StartSound(NULL, sound);
}
// villsa [STRIFE]
static char plrkilledmsg[80];
//
// KillMobj
//
// [STRIFE] Major modifications for drop types, no tic randomization, etc.
//
void P_KillMobj(mobj_t* source, mobj_t* target)
{
mobjtype_t item;
mobj_t* mo;
line_t junk;
int i;
// villsa [STRIFE] corpse and dropoff are removed, but why when these two flags
// are set a few lines later? watcom nonsense perhaps?
target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_BOUNCE|MF_CORPSE|MF_DROPOFF);
// villsa [STRIFE] unused
/*
if (target->type != MT_SKULL)
target->flags &= ~MF_NOGRAVITY;
*/
target->flags |= MF_CORPSE|MF_DROPOFF;
target->height = FRACUNIT; // villsa [STRIFE] set to fracunit instead of >>= 2
if(source && source->player)
{
// count for intermission
if(target->flags & MF_COUNTKILL)
source->player->killcount++;
if(target->player)
{
source->player->frags[target->player-players]++;
// villsa [STRIFE] new messages when fragging players
// haleyjd 20141024: corrected; uses player->allegiance, not mo->miscdata
DEH_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
"%s killed %s",
player_names[source->player->allegiance],
player_names[target->player->allegiance]);
if(netgame)
players[consoleplayer].message = plrkilledmsg;
}
}
else if(!netgame && (target->flags & MF_COUNTKILL))
{
// count all monster deaths,
// even those caused by other monsters
players[0].killcount++;
}
if(target->player)
{
// count environment kills against you
if(!source)
target->player->frags[target->player-players]++;
if(gamemap == 29 && !netgame)
{
// haleyjd 09/13/10: [STRIFE] Give player the bad ending.
F_StartFinale();
return;
}
// villsa [STRIFE] spit out inventory items when killed
if(netgame)
{
int amount = 0;
mobj_t* loot;
int r = 0;
while(1)
{
if(target->player->inventory[0].amount <= 0)
break;
item = target->player->inventory[0].type;
if(item == MT_MONY_1)
{
loot = P_SpawnMobj(target->x, target->y,
target->z + (24*FRACUNIT), MT_MONY_25);
// [STRIFE] TODO - what the hell is it doing here?
loot->health = target->player->inventory[0].amount;
loot->health = -target->player->inventory[0].amount;
amount = target->player->inventory[0].amount;
}
else
{
loot = P_SpawnMobj(target->x, target->y,
target->z + (24*FRACUNIT), item);
amount = 1;
}
P_RemoveInventoryItem(target->player, 0, amount);
r = P_Random();
loot->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS;
loot->momy += ((P_Random() & 7) + 1) << FRACBITS;
loot->flags |= MF_DROPPED;
}
}
target->flags &= ~MF_SOLID;
target->player->playerstate = PST_DEAD;
target->player->mo->momz = 5*FRACUNIT; // [STRIFE]: small hop!
P_DropWeapon(target->player);
if(target->player == &players[consoleplayer]
&& automapactive)
{
// don't die in auto map,
// switch view prior to dying
AM_Stop ();
}
}
// villsa [STRIFE] some modifications to setting states
if(target->state != &states[S_BURN_23])
{
if(target->health == -6666)
P_SetMobjState(target, S_DISR_00); // 373
else
{
if(target->health < -target->info->spawnhealth
&& target->info->xdeathstate)
P_SetMobjState(target, target->info->xdeathstate);
else
P_SetMobjState(target, target->info->deathstate);
}
}
// villsa [STRIFE] no death tics randomization
// Drop stuff.
// villsa [STRIFE] get item from dialog target
item = P_DialogFind(target->type, target->miscdata)->dropitem;
if(!item)
{
// villsa [STRIFE] drop default items
switch(target->type)
{
case MT_ORACLE:
item = MT_MEAT;
break;
case MT_PROGRAMMER:
item = MT_SIGIL_A;
break;
case MT_PRIEST:
item = MT_JUNK;
break;
case MT_BISHOP:
item = MT_AMINIBOX;
break;
case MT_PGUARD:
case MT_CRUSADER:
item = MT_ACELL;
break;
case MT_RLEADER:
item = MT_AAMMOBOX;
break;
case MT_GUARD1:
case MT_REBEL1:
case MT_SHADOWGUARD:
item = MT_ACLIP;
break;
case MT_SPECTRE_B:
item = MT_SIGIL_B;
break;
case MT_SPECTRE_C:
item = MT_SIGIL_C;
break;
case MT_SPECTRE_D:
item = MT_SIGIL_D;
break;
case MT_SPECTRE_E:
item = MT_SIGIL_E;
break;
case MT_COUPLING:
junk.tag = 225;
EV_DoDoor(&junk, vld_close);
junk.tag = 44;
EV_DoFloor(&junk, lowerFloor);
GiveVoiceObjective("VOC13", "LOG13", 0);
item = MT_COUPLING_BROKEN;
players[0].questflags |= (1 << (mobjinfo[MT_COUPLING].speed - 1));
break;
default:
return;
}
}
// handle special case for scripted target's dropped item
switch(item)
{
case MT_TOKEN_SHOPCLOSE:
junk.tag = 222;
EV_DoDoor(&junk, vld_close);
P_NoiseAlert(players[0].mo, players[0].mo);
M_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
"%s", DEH_String("You're dead! You set off the alarm."));
if(!deathmatch)
players[consoleplayer].message = plrkilledmsg;
return;
case MT_TOKEN_PRISON_PASS:
junk.tag = 223;
EV_DoDoor(&junk, vld_open);
return;
case MT_TOKEN_DOOR3:
junk.tag = 224;
EV_DoDoor(&junk, vld_open);
return;
case MT_SIGIL_A:
case MT_SIGIL_B:
case MT_SIGIL_C:
case MT_SIGIL_D:
case MT_SIGIL_E:
for(i = 0; i < MAXPLAYERS; i++)
{
if(!P_GiveWeapon(&players[i], wp_sigil, false))
{
if(players[i].sigiltype++ > 4)
players[i].sigiltype = 4;
}
// haleyjd 20110225: fixed these two assignments which Watcom munged
// up in the assembly by pre-incrementing the pointer into players[]
players[i].st_update = true;
players[i].pendingweapon = wp_sigil;
}
return;
case MT_TOKEN_ALARM:
P_NoiseAlert(players[0].mo, players[0].mo);
M_snprintf(plrkilledmsg, sizeof(plrkilledmsg),
"%s", DEH_String("You Fool! You've set off the alarm"));
if(!deathmatch)
players[consoleplayer].message = plrkilledmsg;
return;
default:
break;
}
// villsa [STRIFE] toss out item
if(!deathmatch || !(mobjinfo[item].flags & MF_NOTDMATCH))
{
int r;
mo = P_SpawnMobj(target->x, target->y, target->z + (24*FRACUNIT), item);
r = P_Random();
mo->momx += ((r & 7) - (P_Random() & 7)) << FRACBITS;
r = P_Random();
mo->momy += ((r & 7) - (P_Random() & 7)) << FRACBITS;
mo->flags |= (MF_SPECIAL|MF_DROPPED); // special versions of items
}
}
//
// P_IsMobjBoss
//
// villsa [STRIFE] new function
//
static boolean P_IsMobjBoss(mobjtype_t type)
{
switch(type)
{
case MT_PROGRAMMER:
case MT_BISHOP:
case MT_RLEADER:
case MT_ORACLE:
case MT_PRIEST:
return true;
default:
return false;
}
}
//
// 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.
//
// [STRIFE] Extensive changes for spectrals, fire damage, disintegration, and
// a plethora of mobjtype-specific hacks.
//
void P_DamageMobj(mobj_t* target, mobj_t* inflictor, mobj_t* source, int damage)
{
angle_t ang;
int saved;
player_t* player;
fixed_t thrust;
int temp;
if(!(target->flags & MF_SHOOTABLE) )
return; // shouldn't happen...
if(target->health <= 0)
return;
player = target->player;
// villsa [STRIFE] unused - skullfly check (removed)
// villsa [STRIFE] handle spectral stuff
// notes on projectile health:
// -2 == enemy spectral projectile
// -1 == player spectral projectile
// haleyjd 20110203: refactored completely
if(inflictor && (inflictor->flags & MF_SPECTRAL))
{
// players aren't damaged by their own (or others???) sigils
// STRIFE-TODO: verify in deathmatch
if(target->type == MT_PLAYER && inflictor->health == -1)
return;
// enemies aren't damaged by enemy sigil attacks
if((target->flags & MF_SPECTRAL) && inflictor->health == -2)
return;
// Macil2, Oracle, and Spectre C cannot be damaged by Sigil A
switch(target->type)
{
case MT_RLEADER2:
case MT_ORACLE:
case MT_SPECTRE_C:
// haleyjd: added source->player validity check for safety...
if(source->player && source->player->sigiltype < 1)
return;
default:
break;
}
}
// villsa [STRIFE] new checks for various actors
if(inflictor)
{
// Fire damage inflictors
if(inflictor->type == MT_SFIREBALL ||
inflictor->type == MT_C_FLAME ||
inflictor->type == MT_PFLAME)
{
temp = damage / 2;
if(P_IsMobjBoss(target->type))
damage /= 2;
else if(inflictor->type == MT_PFLAME)
{
damage /= 2;
// robots take very little damage
if(target->flags & MF_NOBLOOD)
damage = temp / 2;
}
}
else
{
switch(inflictor->type)
{
case MT_HOOKSHOT:
// haleyjd 20110203: should use source, not inflictor
ang = R_PointToAngle2(
target->x,
target->y,
source->x,
source->y) >> ANGLETOFINESHIFT;
target->momx += FixedMul(finecosine[ang], (12750*FRACUNIT) / target->info->mass);
target->momy += FixedMul(finesine[ang], (12750*FRACUNIT) / target->info->mass);
target->reactiontime += 10;
temp = P_AproxDistance(target->x - source->x, target->y - source->y);
temp /= target->info->mass;
if(temp < 1)
temp = 1;
target->momz = (source->z - target->z) / temp;
break;
case MT_POISARROW:
// don't affect robots
if(target->flags & MF_NOBLOOD)
return;
// instant kill
damage = target->health + 10;
break;
default:
// Spectral retaliation, though this may in fact be unreachable
// since non-spectral inflictors are mostly filtered out.
if(target->flags & MF_SPECTRAL
&& !(inflictor->flags & MF_SPECTRAL))
{
P_SetMobjState(target, target->info->missilestate);
return; // take no damage
}
break;
}
}
}
// villsa [STRIFE] special cases for shopkeepers and macil
if((target->type >= MT_SHOPKEEPER_W && target->type <= MT_SHOPKEEPER_M)
|| target->type == MT_RLEADER)
{
if(source)
target->target = source;
P_SetMobjState(target, target->info->painstate);
return;
}
// villsa [STRIFE] handle fieldguard damage
if(target->type == MT_FIELDGUARD)
{
// degnin ores are only allowed to damage the fieldguard
if(!inflictor || inflictor->type != MT_DEGNINORE)
return;
damage = target->health;
}
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_flame))
{
ang = R_PointToAngle2(inflictor->x,
inflictor->y,
target->x,
target->y);
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() & 1))
{
ang += ANG180;
thrust *= 4;
}
ang >>= ANGLETOFINESHIFT;
target->momx += FixedMul (thrust, finecosine[ang]);
target->momy += FixedMul (thrust, finesine[ang]);
}
// 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.
// villsa [STRIFE] removed pw_invulnerability check
if(damage < 1000 && (player->cheats & CF_GODMODE))
return;
// villsa [STRIFE] flame attacks don't damage player if wearing envirosuit
if(player->powers[pw_ironfeet] && inflictor)
{
if(inflictor->type == MT_SFIREBALL ||
inflictor->type == MT_C_FLAME ||
inflictor->type == MT_PFLAME)
{
damage = 0;
}
}
if(player->armortype)
{
if (player->armortype == 1)
saved = damage/3;
else
saved = damage/2;
if(player->armorpoints <= saved)
{
// armor is used up
saved = player->armorpoints;
player->armortype = 0;
// villsa [STRIFE]
P_UseInventoryItem(player, SPR_ARM1);
P_UseInventoryItem(player, SPR_ARM2);
}
player->armorpoints -= saved;
damage -= saved;
}
player->health -= damage; // mirror mobj health here for Dave
// [STRIFE] haleyjd 20130302: bug fix - this is *not* capped here.
//if(player->health < 0)
// player->health = 0;
player->attacker = source;
player->damagecount += damage; // add damage after armor / invuln
// haleyjd 20110203 [STRIFE]: target->target set here
if(target != source)
target->target = source;
if(player->damagecount > 100)
player->damagecount = 100; // teleport stomp does 10k points...
temp = damage < 100 ? damage : 100;
if(player == &players[consoleplayer])
I_Tactile (40,10,40+temp*2);
}
// do the damage
target->health -= damage;
// villsa [STRIFE] auto use medkits
if(player && player->health < 50)
{
if(deathmatch || player->cheats & CF_AUTOHEALTH)
{
while(player->health < 50 && P_UseInventoryItem(player, SPR_MDKT));
while(player->health < 50 && P_UseInventoryItem(player, SPR_STMP));
}
}
if(target->health <= 0)
{
// villsa [STRIFE] grenades hurt... OUCH
if(inflictor && inflictor->type == MT_HEGRENADE)
target->health = -target->info->spawnhealth;
else if(!(target->flags & MF_NOBLOOD))
{
// villsa [STRIFE] disintegration death
if(inflictor &&
(inflictor->type == MT_STRIFEPUFF3 ||
inflictor->type == MT_L_LASER ||
inflictor->type == MT_TORPEDO ||
inflictor->type == MT_TORPEDOSPREAD))
{
S_StartSound(target, sfx_dsrptr);
target->health = -6666;
}
}
// villsa [STRIFE] flame death stuff
if(!(target->flags & MF_NOBLOOD)
&& inflictor
&& (inflictor->type == MT_SFIREBALL ||
inflictor->type == MT_C_FLAME ||
inflictor->type == MT_PFLAME))
{
target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SHADOW|MF_MVIS);
if(target->player)
{
target->player->cheats |= CF_ONFIRE;
target->player->powers[pw_communicator] = false;
target->player->readyweapon = 0;
P_SetPsprite(target->player, ps_weapon, S_WAVE_00); // 02
P_SetPsprite(target->player, ps_flash, S_NULL);
}
P_SetMobjState(target, S_BURN_00); // 349
S_StartSound(target, sfx_burnme);
return;
}
P_KillMobj(source, target);
return;
}
// villsa [STRIFE] set crash state
if(target->health <= 6 && target->info->crashstate)
{
P_SetMobjState(target, target->info->crashstate);
return;
}
if(damage)
{
// villsa [STRIFE] removed unused skullfly flag
if(P_Random() < target->info->painchance)
{
target->flags |= MF_JUSTHIT; // fight back!
P_SetMobjState (target, target->info->painstate);
}
}
target->reactiontime = 0; // we're awake now...
// villsa [STRIFE] new checks for thing types
if (target->type != MT_PROGRAMMER
&& (!target->threshold || target->type == MT_ENTITY)
&& source && source != target
&& source->type != MT_ENTITY
&& ((source->flags & MF_ALLY) != (target->flags & MF_ALLY)))
{
// if not intent on another player,
// chase after this one
target->target = source;
target->threshold = BASETHRESHOLD;
if(target->state == &states[target->info->spawnstate]
&& target->info->seestate != S_NULL)
P_SetMobjState (target, target->info->seestate);
}
}