2006-03-28 15:44:01 +00:00
|
|
|
/* 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"
|
2006-12-13 04:44:17 +00:00
|
|
|
#include "d_deh.h" // Ty 03/22/98 - externalized strings
|
2006-03-28 15:44:01 +00:00
|
|
|
#include "p_tick.h"
|
|
|
|
#include "i_system.h"
|
2006-12-13 04:44:17 +00:00
|
|
|
|
2006-03-28 15:44:01 +00:00
|
|
|
#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;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTARMOR; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_ARM2:
|
|
|
|
if (!P_GiveArmor (player, blue_armor_class))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTMEGA; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
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;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTHTHBONUS; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
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;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTARMBONUS; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_SOUL:
|
|
|
|
player->health += soul_health;
|
|
|
|
if (player->health > max_soul)
|
|
|
|
player->health = max_soul;
|
|
|
|
player->mo->health = player->health;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTSUPER; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
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);
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTMSPHERE; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_getpow;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// cards
|
|
|
|
// leave cards for everyone
|
|
|
|
case SPR_BKEY:
|
|
|
|
if (!player->cards[it_bluecard])
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTBLUECARD; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
P_GiveCard (player, it_bluecard);
|
|
|
|
if (!netgame)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SPR_YKEY:
|
|
|
|
if (!player->cards[it_yellowcard])
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTYELWCARD; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
P_GiveCard (player, it_yellowcard);
|
|
|
|
if (!netgame)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SPR_RKEY:
|
|
|
|
if (!player->cards[it_redcard])
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTREDCARD; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
P_GiveCard (player, it_redcard);
|
|
|
|
if (!netgame)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SPR_BSKU:
|
|
|
|
if (!player->cards[it_blueskull])
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTBLUESKUL; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
P_GiveCard (player, it_blueskull);
|
|
|
|
if (!netgame)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SPR_YSKU:
|
|
|
|
if (!player->cards[it_yellowskull])
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTYELWSKUL; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
P_GiveCard (player, it_yellowskull);
|
|
|
|
if (!netgame)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SPR_RSKU:
|
|
|
|
if (!player->cards[it_redskull])
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTREDSKULL; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
P_GiveCard (player, it_redskull);
|
|
|
|
if (!netgame)
|
|
|
|
break;
|
|
|
|
return;
|
|
|
|
|
|
|
|
// medikits, heals
|
|
|
|
case SPR_STIM:
|
|
|
|
if (!P_GiveBody (player, 10))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTSTIM; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
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
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTMEDINEED; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
else
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTMEDIKIT; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
// power ups
|
|
|
|
case SPR_PINV:
|
|
|
|
if (!P_GivePower (player, pw_invulnerability))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTINVUL; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_getpow;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_PSTR:
|
|
|
|
if (!P_GivePower (player, pw_strength))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTBERSERK; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
if (player->readyweapon != wp_fist)
|
|
|
|
player->pendingweapon = wp_fist;
|
|
|
|
sound = sfx_getpow;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_PINS:
|
|
|
|
if (!P_GivePower (player, pw_invisibility))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTINVIS; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_getpow;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_SUIT:
|
|
|
|
if (!P_GivePower (player, pw_ironfeet))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTSUIT; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_getpow;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_PMAP:
|
|
|
|
if (!P_GivePower (player, pw_allmap))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTMAP; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_getpow;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_PVIS:
|
|
|
|
if (!P_GivePower (player, pw_infrared))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTVISOR; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
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;
|
|
|
|
}
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTCLIP; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_AMMO:
|
|
|
|
if (!P_GiveAmmo (player, am_clip,5))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTCLIPBOX; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_ROCK:
|
|
|
|
if (!P_GiveAmmo (player, am_misl,1))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTROCKET; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_BROK:
|
|
|
|
if (!P_GiveAmmo (player, am_misl,5))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTROCKBOX; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_CELL:
|
|
|
|
if (!P_GiveAmmo (player, am_cell,1))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTCELL; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_CELP:
|
|
|
|
if (!P_GiveAmmo (player, am_cell,5))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTCELLBOX; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_SHEL:
|
|
|
|
if (!P_GiveAmmo (player, am_shell,1))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTSHELLS; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_SBOX:
|
|
|
|
if (!P_GiveAmmo (player, am_shell,5))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTSHELLBOX; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
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);
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTBACKPACK; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// weapons
|
|
|
|
case SPR_BFUG:
|
|
|
|
if (!P_GiveWeapon (player, wp_bfg, false) )
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTBFG9000; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_wpnup;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_MGUN:
|
|
|
|
if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED)!=0) )
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTCHAINGUN; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_wpnup;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_CSAW:
|
|
|
|
if (!P_GiveWeapon (player, wp_chainsaw, false) )
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTCHAINSAW; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_wpnup;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_LAUN:
|
|
|
|
if (!P_GiveWeapon (player, wp_missile, false) )
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTLAUNCHER; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_wpnup;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_PLAS:
|
|
|
|
if (!P_GiveWeapon (player, wp_plasma, false) )
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTPLASMA; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_wpnup;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_SHOT:
|
|
|
|
if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED)!=0 ) )
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTSHOTGUN; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
sound = sfx_wpnup;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPR_SGN2:
|
|
|
|
if (!P_GiveWeapon(player, wp_supershotgun, (special->flags&MF_DROPPED)!=0))
|
|
|
|
return;
|
2006-12-13 04:44:17 +00:00
|
|
|
player->message = s_GOTSHOTGUN2; // Ty 03/22/98 - externalized
|
2006-03-28 15:44:01 +00:00
|
|
|
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!
|
|
|
|
}
|