mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-11-22 04:12:09 +00:00
1073 lines
22 KiB
C++
1073 lines
22 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition Source Code 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "Precompiled.h"
|
|
#include "globaldata.h"
|
|
|
|
|
|
// Data.
|
|
#include "doomdef.h"
|
|
#include "dstrings.h"
|
|
#include "sounds.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "m_random.h"
|
|
#include "i_system.h"
|
|
|
|
#include "am_map.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#ifdef __GNUG__
|
|
#pragma implementation "p_inter.h"
|
|
#endif
|
|
#include "p_inter.h"
|
|
|
|
#include "Main.h"
|
|
|
|
#include "sys/sys_signin.h"
|
|
|
|
#include "../../neo/d3xp/Game_Local.h"
|
|
|
|
// a weapon is found with two clip loads,
|
|
// a big item has five clip loads
|
|
const int maxammo[NUMAMMO] = {200, 50, 300, 50};
|
|
const 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
|
|
//
|
|
|
|
qboolean
|
|
P_GiveAmmo
|
|
( player_t* player,
|
|
ammotype_t ammo,
|
|
int num )
|
|
{
|
|
int oldammo;
|
|
|
|
if (ammo == am_noammo)
|
|
return false;
|
|
|
|
if (ammo < 0 || 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 (::g->gameskill == sk_baby
|
|
|| ::g->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.
|
|
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.
|
|
//
|
|
qboolean
|
|
P_GiveWeapon
|
|
( player_t* player,
|
|
weapontype_t weapon,
|
|
qboolean dropped )
|
|
{
|
|
qboolean gaveammo;
|
|
qboolean gaveweapon;
|
|
|
|
if (::g->netgame
|
|
&& (::g->deathmatch!=2)
|
|
&& !dropped )
|
|
{
|
|
// leave placed weapons forever on net games
|
|
if (player->weaponowned[weapon])
|
|
return false;
|
|
|
|
player->bonuscount += BONUSADD;
|
|
player->weaponowned[weapon] = true;
|
|
|
|
if (::g->deathmatch)
|
|
P_GiveAmmo (player, weaponinfo[weapon].ammo, 5);
|
|
else
|
|
P_GiveAmmo (player, weaponinfo[weapon].ammo, 2);
|
|
player->pendingweapon = weapon;
|
|
|
|
if (player == &::g->players[::g->consoleplayer])
|
|
S_StartSound (player->mo, 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;
|
|
|
|
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
|
|
//
|
|
qboolean
|
|
P_GiveBody
|
|
( player_t* player,
|
|
int num )
|
|
{
|
|
if (player->health >= MAXHEALTH)
|
|
return false;
|
|
|
|
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.
|
|
//
|
|
qboolean
|
|
P_GiveArmor
|
|
( player_t* player,
|
|
int armortype )
|
|
{
|
|
int hits;
|
|
|
|
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, const char *pickup_message ) {
|
|
|
|
if ( ( ::g->demoplayback && ::g->netgame ) || common->IsMultiplayer() ) {
|
|
for ( int i=0; i < MAXPLAYERS; i++ ) {
|
|
if ( ::g->playeringame[i] ) {
|
|
player_t *thePlayer = &::g->players[i];
|
|
|
|
if (thePlayer->cards[card])
|
|
continue;
|
|
|
|
thePlayer->bonuscount = BONUSADD;
|
|
thePlayer->message = pickup_message;
|
|
thePlayer->cards[card] = 1;
|
|
}
|
|
}
|
|
} else {
|
|
if (player->cards[card])
|
|
return;
|
|
|
|
player->bonuscount = BONUSADD;
|
|
player->message = pickup_message;
|
|
player->cards[card] = 1;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// P_GivePower
|
|
//
|
|
qboolean
|
|
P_GivePower
|
|
( player_t* player,
|
|
int /*powertype_t*/ power )
|
|
{
|
|
if (power == pw_invulnerability)
|
|
{
|
|
player->powers[power] = INVULNTICS;
|
|
return true;
|
|
}
|
|
|
|
if (power == pw_invisibility)
|
|
{
|
|
player->powers[power] = INVISTICS;
|
|
player->mo->flags |= MF_SHADOW;
|
|
return true;
|
|
}
|
|
|
|
if (power == pw_infrared)
|
|
{
|
|
player->powers[power] = INFRATICS;
|
|
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;
|
|
}
|
|
|
|
if (player->powers[power])
|
|
return false; // already got it
|
|
|
|
player->powers[power] = 1;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// P_TouchSpecialThing
|
|
//
|
|
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)
|
|
{
|
|
// out of reach
|
|
return;
|
|
}
|
|
|
|
|
|
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, 1))
|
|
return;
|
|
player->message = GOTARMOR;
|
|
break;
|
|
|
|
case SPR_ARM2:
|
|
if (!P_GiveArmor (player, 2))
|
|
return;
|
|
player->message = GOTMEGA;
|
|
break;
|
|
|
|
// bonus items
|
|
case SPR_BON1:
|
|
player->health++; // can go over 100%
|
|
if (player->health > 200)
|
|
player->health = 200;
|
|
player->mo->health = player->health;
|
|
player->message = GOTHTHBONUS;
|
|
break;
|
|
|
|
case SPR_BON2:
|
|
player->armorpoints++; // can go over 100%
|
|
if (player->armorpoints > 200)
|
|
player->armorpoints = 200;
|
|
if (!player->armortype)
|
|
player->armortype = 1;
|
|
player->message = GOTARMBONUS;
|
|
break;
|
|
|
|
case SPR_SOUL:
|
|
player->health += 100;
|
|
if (player->health > 200)
|
|
player->health = 200;
|
|
player->mo->health = player->health;
|
|
player->message = GOTSUPER;
|
|
sound = sfx_getpow;
|
|
break;
|
|
|
|
case SPR_MEGA:
|
|
if (::g->gamemode != commercial)
|
|
return;
|
|
player->health = 200;
|
|
player->mo->health = player->health;
|
|
P_GiveArmor (player,2);
|
|
player->message = GOTMSPHERE;
|
|
sound = sfx_getpow;
|
|
break;
|
|
|
|
// cards
|
|
// leave cards for everyone
|
|
case SPR_BKEY:
|
|
//if (!player->cards[it_bluecard])
|
|
//player->message = GOTBLUECARD;
|
|
P_GiveCard (player, it_bluecard, GOTBLUECARD);
|
|
if (!::g->netgame)
|
|
break;
|
|
return;
|
|
|
|
case SPR_YKEY:
|
|
//if (!player->cards[it_yellowcard])
|
|
//player->message = GOTYELWCARD;
|
|
P_GiveCard (player, it_yellowcard, GOTYELWCARD);
|
|
if (!::g->netgame)
|
|
break;
|
|
return;
|
|
|
|
case SPR_RKEY:
|
|
//if (!player->cards[it_redcard])
|
|
//player->message = GOTREDCARD;
|
|
P_GiveCard (player, it_redcard, GOTREDCARD);
|
|
if (!::g->netgame)
|
|
break;
|
|
return;
|
|
|
|
case SPR_BSKU:
|
|
//if (!player->cards[it_blueskull])
|
|
//player->message = GOTBLUESKUL;
|
|
P_GiveCard (player, it_blueskull, GOTBLUESKUL);
|
|
if (!::g->netgame)
|
|
break;
|
|
return;
|
|
|
|
case SPR_YSKU:
|
|
//if (!player->cards[it_yellowskull])
|
|
//player->message = GOTYELWSKUL;
|
|
P_GiveCard (player, it_yellowskull, GOTYELWSKUL);
|
|
if (!::g->netgame)
|
|
break;
|
|
return;
|
|
|
|
case SPR_RSKU:
|
|
//if (!player->cards[it_redskull])
|
|
//player->message = GOTREDSKULL;
|
|
P_GiveCard (player, it_redskull, GOTREDSKULL);
|
|
if (!::g->netgame)
|
|
break;
|
|
return;
|
|
|
|
// medikits, heals
|
|
case SPR_STIM:
|
|
if (!P_GiveBody (player, 10))
|
|
return;
|
|
player->message = GOTSTIM;
|
|
break;
|
|
|
|
case SPR_MEDI:
|
|
if (!P_GiveBody (player, 25))
|
|
return;
|
|
|
|
if (player->health < 25)
|
|
player->message = GOTMEDINEED;
|
|
else
|
|
player->message = GOTMEDIKIT;
|
|
break;
|
|
|
|
|
|
// power ups
|
|
case SPR_PINV:
|
|
if (!P_GivePower (player, pw_invulnerability))
|
|
return;
|
|
player->message = GOTINVUL;
|
|
sound = sfx_getpow;
|
|
break;
|
|
|
|
case SPR_PSTR:
|
|
if (!P_GivePower (player, pw_strength))
|
|
return;
|
|
player->message = GOTBERSERK;
|
|
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;
|
|
sound = sfx_getpow;
|
|
break;
|
|
|
|
case SPR_SUIT:
|
|
if (!P_GivePower (player, pw_ironfeet))
|
|
return;
|
|
player->message = GOTSUIT;
|
|
sound = sfx_getpow;
|
|
break;
|
|
|
|
case SPR_PMAP:
|
|
if (!P_GivePower (player, pw_allmap))
|
|
return;
|
|
player->message = GOTMAP;
|
|
sound = sfx_getpow;
|
|
break;
|
|
|
|
case SPR_PVIS:
|
|
if (!P_GivePower (player, pw_infrared))
|
|
return;
|
|
player->message = GOTVISOR;
|
|
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;
|
|
break;
|
|
|
|
case SPR_AMMO:
|
|
if (!P_GiveAmmo (player, am_clip,5))
|
|
return;
|
|
player->message = GOTCLIPBOX;
|
|
break;
|
|
|
|
case SPR_ROCK:
|
|
if (!P_GiveAmmo (player, am_misl,1))
|
|
return;
|
|
player->message = GOTROCKET;
|
|
break;
|
|
|
|
case SPR_BROK:
|
|
if (!P_GiveAmmo (player, am_misl,5))
|
|
return;
|
|
player->message = GOTROCKBOX;
|
|
break;
|
|
|
|
case SPR_CELL:
|
|
if (!P_GiveAmmo (player, am_cell,1))
|
|
return;
|
|
player->message = GOTCELL;
|
|
break;
|
|
|
|
case SPR_CELP:
|
|
if (!P_GiveAmmo (player, am_cell,5))
|
|
return;
|
|
player->message = GOTCELLBOX;
|
|
break;
|
|
|
|
case SPR_SHEL:
|
|
if (!P_GiveAmmo (player, am_shell,1))
|
|
return;
|
|
player->message = GOTSHELLS;
|
|
break;
|
|
|
|
case SPR_SBOX:
|
|
if (!P_GiveAmmo (player, am_shell,5))
|
|
return;
|
|
player->message = GOTSHELLBOX;
|
|
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, (ammotype_t)i, 1);
|
|
player->message = GOTBACKPACK;
|
|
break;
|
|
|
|
// weapons
|
|
case SPR_BFUG:
|
|
if (!P_GiveWeapon (player, wp_bfg, false) )
|
|
return;
|
|
|
|
// DHM - Nerve :: Give achievement
|
|
if ( !common->IsMultiplayer() ) {
|
|
switch( DoomLib::GetGameSKU() ) {
|
|
case GAME_SKU_DOOM2_BFG: {
|
|
idAchievementManager::LocalUser_CompleteAchievement( ACHIEVEMENT_DOOM2_REALLY_BIG_GUN_FIND_BFG_SINGLEPLAYER );
|
|
}
|
|
default: {
|
|
// No unlocks for other SKUs.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
player->message = GOTBFG9000;
|
|
sound = sfx_wpnup;
|
|
break;
|
|
|
|
case SPR_MGUN:
|
|
if (!P_GiveWeapon (player, wp_chaingun, special->flags&MF_DROPPED) )
|
|
return;
|
|
player->message = GOTCHAINGUN;
|
|
sound = sfx_wpnup;
|
|
break;
|
|
|
|
case SPR_CSAW:
|
|
if (!P_GiveWeapon (player, wp_chainsaw, false) )
|
|
return;
|
|
player->message = GOTCHAINSAW;
|
|
sound = sfx_wpnup;
|
|
break;
|
|
|
|
case SPR_LAUN:
|
|
if (!P_GiveWeapon (player, wp_missile, false) )
|
|
return;
|
|
player->message = GOTLAUNCHER;
|
|
sound = sfx_wpnup;
|
|
break;
|
|
|
|
case SPR_PLAS:
|
|
if (!P_GiveWeapon (player, wp_plasma, false) )
|
|
return;
|
|
player->message = GOTPLASMA;
|
|
sound = sfx_wpnup;
|
|
break;
|
|
|
|
case SPR_SHOT:
|
|
if (!P_GiveWeapon (player, wp_shotgun, special->flags&MF_DROPPED ) )
|
|
return;
|
|
player->message = GOTSHOTGUN;
|
|
sound = sfx_wpnup;
|
|
break;
|
|
|
|
case SPR_SGN2:
|
|
if (!P_GiveWeapon (player, wp_supershotgun, special->flags&MF_DROPPED ) )
|
|
return;
|
|
|
|
player->message = GOTSHOTGUN2;
|
|
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;
|
|
if (player == &::g->players[::g->consoleplayer])
|
|
S_StartSound (player->mo, sound);
|
|
}
|
|
|
|
//
|
|
// IsOnlineDeathmatchWithLocalProfile
|
|
//
|
|
// Helper to simplify the online frag stat tracking. Returns the
|
|
// master user's profile if successful, NULL if not.
|
|
//
|
|
idPlayerProfile * IsOnlineDeathmatchWithLocalProfile() {
|
|
if ( !MatchTypeIsOnline( session->GetGameLobbyBase().GetMatchParms().matchFlags ) ) {
|
|
return NULL;
|
|
}
|
|
|
|
if ( !::g ) {
|
|
return NULL;
|
|
}
|
|
|
|
if ( !::g->deathmatch ) {
|
|
return NULL;
|
|
}
|
|
|
|
// Assume that the master local user is the one playing.
|
|
idLocalUser * user = session->GetSignInManager().GetMasterLocalUser();
|
|
if ( user == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
idPlayerProfile * profile = user->GetProfile();
|
|
|
|
if ( profile == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
return profile;
|
|
}
|
|
|
|
//
|
|
// KillMobj
|
|
//
|
|
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 (source && source->player)
|
|
{
|
|
// count for intermission
|
|
if (target->flags & MF_COUNTKILL)
|
|
source->player->killcount++;
|
|
|
|
if (target->player) {
|
|
source->player->frags[target->player-::g->players]++;
|
|
|
|
// Keep track of the local player's total frags for trophy awards.
|
|
|
|
// Make sure the killing player is the local player
|
|
if ( source->player == &(::g->players[::g->consoleplayer]) ) {
|
|
// Make sure this is an online game.
|
|
// TODO: PC
|
|
}
|
|
}
|
|
|
|
// DHM - Nerve :: Check for killing cyberdemon with fists achievement
|
|
// JAF TROPHY int port = gameLocal->GetPortForPlayer( DoomLib::GetPlayer() );
|
|
|
|
if ( source->player->readyweapon == wp_fist && target->type == MT_CYBORG && !common->IsMultiplayer() ) {
|
|
switch( DoomLib::GetGameSKU() ) {
|
|
case GAME_SKU_DOOM2_BFG: {
|
|
// Removing trophies for DOOM and DOOM II BFG due to point limit.
|
|
//gameLocal->UnlockAchievement( Doom2BFG_Trophies::YOU_HAVE_HUGE_GUTS_KILL_CYBERDEMON_WITH_FISTS );
|
|
break;
|
|
}
|
|
case GAME_SKU_DCC: {
|
|
// Not for PC.
|
|
//session->GetAchievementSystem().AchievementUnlock( session->GetSignInManager().GetMasterLocalUser(), DOOM_ACHIEVEMENT_KILL_CYBER_DEMON_WITH_FISTS );
|
|
break;
|
|
}
|
|
default: {
|
|
// No unlocks for other SKUs.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// DHM - Nerve :: Chainsaw kills
|
|
if ( source->player->readyweapon == wp_chainsaw && !common->IsMultiplayer() ) {
|
|
source->player->chainsawKills++;
|
|
if ( source->player->chainsawKills == 20 ) {
|
|
switch( DoomLib::GetGameSKU() ) {
|
|
case GAME_SKU_DOOM2_BFG: {
|
|
// Removing trophies for DOOM and DOOM II BFG due to point limit.
|
|
//gameLocal->UnlockAchievement( Doom2BFG_Trophies::GREAT_COMMUNICATOR_20_CHAINSAW_KILLS );
|
|
break;
|
|
}
|
|
case GAME_SKU_DCC: {
|
|
// Not for PC.
|
|
//gameLocal->UnlockAchievement( DOOM_ACHIEVEMENT_20KILLS_CHAINSAW );
|
|
break;
|
|
}
|
|
default: {
|
|
// No unlocks for other SKUs.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// DHM - Nerve :: Berserker kills
|
|
if ( source->player->readyweapon == wp_fist && source->player->powers[pw_strength] && !common->IsMultiplayer()) {
|
|
source->player->berserkKills++;
|
|
idLib::Printf( "Player has %d berserk kills\n", source->player->berserkKills );
|
|
if ( source->player->berserkKills == 20 ) {
|
|
switch( DoomLib::GetGameSKU() ) {
|
|
case GAME_SKU_DOOM2_BFG: {
|
|
// Removing trophies for DOOM and DOOM II BFG due to point limit.
|
|
//gameLocal->UnlockAchievement( Doom2BFG_Trophies::MAN_AND_A_HALF_20_BERSERK_KILLS );
|
|
break;
|
|
}
|
|
case GAME_SKU_DCC: {
|
|
// Not for PC.
|
|
//gameLocal->UnlockAchievement( DOOM_ACHIEVEMENT_20KILLS_BERSERKER );
|
|
break;
|
|
}
|
|
default: {
|
|
// No unlocks for other SKUs.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!::g->netgame && (target->flags & MF_COUNTKILL) )
|
|
{
|
|
// count all monster deaths,
|
|
// even those caused by other monsters
|
|
::g->players[0].killcount++;
|
|
}
|
|
|
|
if (target->player)
|
|
{
|
|
// count environment kills against you
|
|
if (!source)
|
|
target->player->frags[target->player-::g->players]++;
|
|
|
|
target->flags &= ~MF_SOLID;
|
|
target->player->playerstate = PST_DEAD;
|
|
P_DropWeapon (target->player);
|
|
|
|
if (target->player == &::g->players[::g->consoleplayer]
|
|
&& ::g->automapactive)
|
|
{
|
|
// don't die in auto map,
|
|
// switch view prior to dying
|
|
AM_Stop ();
|
|
}
|
|
|
|
}
|
|
|
|
if (target->health < -target->info->spawnhealth
|
|
&& target->info->xdeathstate)
|
|
{
|
|
P_SetMobjState (target, (statenum_t)target->info->xdeathstate);
|
|
}
|
|
else
|
|
P_SetMobjState (target, (statenum_t)target->info->deathstate);
|
|
target->tics -= P_Random()&3;
|
|
|
|
if (target->tics < 1)
|
|
target->tics = 1;
|
|
|
|
// I_StartSound (&actor->r, actor->info->deathsound);
|
|
|
|
|
|
// 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 ::g->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 )
|
|
{
|
|
unsigned 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;
|
|
|
|
if ( target->flags & MF_SKULLFLY )
|
|
{
|
|
target->momx = target->momy = target->momz = 0;
|
|
}
|
|
|
|
player = target->player;
|
|
if (player && ::g->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))
|
|
{
|
|
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;
|
|
}
|
|
|
|
float baseShake_High = 0.5f;
|
|
int baseShake_High_Dur = 100;
|
|
float baseShake_Low = 0.5f;
|
|
int baseShake_Low_Dur = 100;
|
|
int damageClamp = Min( damage, 100 );
|
|
float damageFloat = std::min( (float)damageClamp / 100.0f, 100.0f );
|
|
float additional = 0.5f * damageFloat;
|
|
int additional_time = 500.0f * damageFloat;
|
|
|
|
if( ::g->plyr == player ) {
|
|
}
|
|
|
|
|
|
// Below certain threshold,
|
|
// ignore damage in GOD mode, or with INVUL power.
|
|
if ( damage < 1000
|
|
&& ( (player->cheats&CF_GODMODE)
|
|
|| player->powers[pw_invulnerability] ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
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...
|
|
|
|
temp = damage < 100 ? damage : 100;
|
|
}
|
|
|
|
// do the damage
|
|
target->health -= damage;
|
|
if (target->health <= 0)
|
|
{
|
|
P_KillMobj (source, target);
|
|
return;
|
|
}
|
|
|
|
if ( (P_Random () < target->info->painchance)
|
|
&& !(target->flags&MF_SKULLFLY) )
|
|
{
|
|
target->flags |= MF_JUSTHIT; // fight back!
|
|
|
|
P_SetMobjState (target, (statenum_t)target->info->painstate);
|
|
}
|
|
|
|
target->reactiontime = 0; // we're awake now...
|
|
|
|
if ( (!target->threshold || target->type == MT_VILE)
|
|
&& source && source != target
|
|
&& source->type != MT_VILE)
|
|
{
|
|
// if not intent on another player,
|
|
// chase after this one
|
|
target->target = source;
|
|
target->threshold = BASETHRESHOLD;
|
|
if (target->state == &::g->states[target->info->spawnstate]
|
|
&& target->info->seestate != S_NULL)
|
|
P_SetMobjState (target, (statenum_t)target->info->seestate);
|
|
}
|
|
|
|
}
|
|
|
|
|