/* =========================================================================== 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 . 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 ; imaxammo[i] *= 2; player->backpack = true; } for (i=0 ; imessage = 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); } }