From 546b43e70ab7d0ddad8b0235cd3b5cebfa3d3e52 Mon Sep 17 00:00:00 2001 From: ineedbots Date: Thu, 3 Jun 2021 14:33:51 -0600 Subject: [PATCH] Fun stuffs --- .../maps/mp/gametypes/_globallogic_player.gsc | 2666 +++++++++++++++++ main_shared/maps/mp/gametypes/_hardpoints.gsc | 1141 +++++++ 2 files changed, 3807 insertions(+) create mode 100644 main_shared/maps/mp/gametypes/_globallogic_player.gsc create mode 100644 main_shared/maps/mp/gametypes/_hardpoints.gsc diff --git a/main_shared/maps/mp/gametypes/_globallogic_player.gsc b/main_shared/maps/mp/gametypes/_globallogic_player.gsc new file mode 100644 index 0000000..8693ea9 --- /dev/null +++ b/main_shared/maps/mp/gametypes/_globallogic_player.gsc @@ -0,0 +1,2666 @@ +#include maps\mp\_utility; +#include common_scripts\utility; + +freezePlayerForRoundEnd() +{ + self clearLowerMessage(); + + self closeMenu(); + self closeInGameMenu(); + + self freeze_player_controls( true ); + currentWeapon = self GetCurrentWeapon(); + if ( maps\mp\gametypes\_hardpoints::isKillstreakWeapon( currentWeapon ) && !maps\mp\gametypes\_killstreak_weapons::isHeldKillstreakWeapon( currentWeapon ) ) + self takeWeapon( currentWeapon ); +// self _disableWeapon(); +} + + +Callback_PlayerConnect() +{ + thread notifyConnecting(); + + self.statusicon = "hud_status_connecting"; + self waittill( "begin" ); + waittillframeend; + self.statusicon = ""; + + level notify( "connected", self ); + +// self thread maps\mp\gametypes\_globallogic_utils::fakeLag(); + if ( level.console && self IsHost() ) + self thread maps\mp\gametypes\_globallogic::listenForGameEnd(); + + // only print that we connected if we haven't connected in a previous round + if( !level.splitscreen && !isdefined( self.pers["score"] ) ) + { + iPrintLn(&"MP_CONNECTED", self); + } + + if( !isdefined( self.pers["score"] ) ) + { + self thread maps\mp\gametypes\_persistence::adjustRecentStats(); + self maps\mp\gametypes\_persistence::setAfterActionReportStat( "valid", 0 ); + if( level.console ) + { + if ( GetDvarInt( #"xblive_wagermatch" ) == 1 && !( self IsHost() ) ) + self maps\mp\gametypes\_persistence::setAfterActionReportStat( "wagerMatchFailed", 1 ); + else + self maps\mp\gametypes\_persistence::setAfterActionReportStat( "wagerMatchFailed", 0 ); + } + else + { + /# + PrintLn("level.wagermatch: " + level.wagermatch ); + #/ + if ( GetDvarInt( #"xblive_wagermatch" ) == 1 ) + { + if ( !self is_bot() && !self isdemoclient() ) + { + codPoints = self maps\mp\gametypes\_persistence::statGet( "CODPOINTS" ); + if( codPoints < level.wagerBet && !self IsHost() ) + { + /# + PrintLn("kick " + self.name + "; not enought codpoints: " + codPoints ); + #/ + kick( self getEntityNumber(), "PLATFORM_WAGER_DEADBEAT_TITLE" ); + return; + } + } + + // set this flag to notify player of possible refund in case the match does not end well + // self maps\mp\gametypes\_persistence::setAfterActionReportStat( "wagerMatchFailed", 1 ); + } + else + self maps\mp\gametypes\_persistence::setAfterActionReportStat( "wagerMatchFailed", 0 ); + } + } + + // track match and hosting stats once per match + if ( !IsDefined( self.pers["matchesPlayedStatsTracked"] ) ) + { + self maps\mp\gametypes\_persistence::statAdd( "MATCHES_PLAYED", 1, false ); + self.pers["MATCHES_PLAYED_COMPLETED_STREAK"] = self maps\mp\gametypes\_persistence::statGet( "MATCHES_PLAYED_COMPLETED_STREAK" ) + 1; + self maps\mp\gametypes\_persistence::statSet( "MATCHES_PLAYED_COMPLETED_STREAK", 0, false ); + + if ( !IsDefined( self.pers["matchesHostedStatsTracked"] ) && self IsLocalToHost() ) + { + self maps\mp\gametypes\_persistence::statAdd( "MATCHES_HOSTED", 1, false ); + self.pers["MATCHES_HOSTED_COMPLETED_STREAK"] = self maps\mp\gametypes\_persistence::statGet( "MATCHES_HOSTED_COMPLETED_STREAK" ) + 1; + self maps\mp\gametypes\_persistence::statSet( "MATCHES_HOSTED_COMPLETED_STREAK", 0, false ); + self.pers["matchesHostedStatsTracked"] = true; + } + + self.pers["matchesPlayedStatsTracked"] = true; + self thread maps\mp\gametypes\_persistence::uploadStatsSoon(); + } + + self maps\mp\_gamerep::gameRepPlayerConnected(); + + lpselfnum = self getEntityNumber(); + lpGuid = self getGuid(); + logPrint("J;" + lpGuid + ";" + lpselfnum + ";" + self.name + "\n"); + bbPrint( "mpjoins: name %s client %s", self.name, lpselfnum ); + + self setClientUIVisibilityFlag( "hud_visible", 1 ); + self setClientUIVisibilityFlag( "g_compassShowEnemies", GetDvarInt( #"scr_game_forceradar" ) ); + + self setClientDvars( "player_sprintTime", GetDvar( #"scr_player_sprinttime" ), + "ui_radar_client", GetDvar( #"ui_radar_client" ), + "scr_numLives", level.numLives, + "ui_pregame", isPregame() ); + + self CameraActivate( false ); + + makeDvarServerInfo( "cg_drawTalk", 1 ); + + if ( level.hardcoreMode ) + { + self setClientDvars( "cg_drawTalk", 3 ); + } + + if ( GetDvarInt( #"player_sprintUnlimited" ) ) + { + self setClientDvar( "player_sprintUnlimited", 1 ); + } + +/# + if ( GetDvarInt( #"scr_hitloc_debug") ) + { + for ( i = 0; i < 6; i++ ) + { + self setClientDvar( "ui_hitloc_" + i, "" ); + } + self.hitlocInited = true; + } +#/ + + self maps\mp\gametypes\_globallogic_score::initPersStat( "score" ); + if ( level.resetPlayerScoreEveryRound ) + { + self.pers["score"] = 0; + } + self.score = self.pers["score"]; + + self maps\mp\gametypes\_globallogic_score::initPersStat( "suicides" ); + self.suicides = self maps\mp\gametypes\_globallogic_score::getPersStat( "suicides" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "headshots" ); + self.headshots = self maps\mp\gametypes\_globallogic_score::getPersStat( "headshots" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "challenges" ); + self.challenges = self maps\mp\gametypes\_globallogic_score::getPersStat( "challenges" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "kills" ); + self.kills = self maps\mp\gametypes\_globallogic_score::getPersStat( "kills" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "deaths" ); + self.deaths = self maps\mp\gametypes\_globallogic_score::getPersStat( "deaths" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "assists" ); + self.assists = self maps\mp\gametypes\_globallogic_score::getPersStat( "assists" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "defends", false ); + self.defends = self maps\mp\gametypes\_globallogic_score::getPersStat( "defends" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "offends", false ); + self.offends = self maps\mp\gametypes\_globallogic_score::getPersStat( "offends" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "plants", false ); + self.plants = self maps\mp\gametypes\_globallogic_score::getPersStat( "plants" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "defuses", false ); + self.defuses = self maps\mp\gametypes\_globallogic_score::getPersStat( "defuses" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "returns", false ); + self.returns = self maps\mp\gametypes\_globallogic_score::getPersStat( "returns" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "captures", false ); + self.captures = self maps\mp\gametypes\_globallogic_score::getPersStat( "captures" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "destructions", false ); + self.destructions = self maps\mp\gametypes\_globallogic_score::getPersStat( "destructions" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "backstabs" ); + self.backstabs = self maps\mp\gametypes\_globallogic_score::getPersStat( "backstabs" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "longshots" ); + self.longshots = self maps\mp\gametypes\_globallogic_score::getPersStat( "longshots" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "survived" ); + self.survived = self maps\mp\gametypes\_globallogic_score::getPersStat( "survived" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "stabs" ); + self.stabs = self maps\mp\gametypes\_globallogic_score::getPersStat( "stabs" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "tomahawks" ); + self.tomahawks = self maps\mp\gametypes\_globallogic_score::getPersStat( "tomahawks" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "humiliated" ); + self.humiliated = self maps\mp\gametypes\_globallogic_score::getPersStat( "humiliated" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "x2score" ); + self.x2score = self maps\mp\gametypes\_globallogic_score::getPersStat( "x2score" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "sessionbans" ); + self.sessionbans = self maps\mp\gametypes\_globallogic_score::getPersStat( "sessionbans" ); + self maps\mp\gametypes\_globallogic_score::initPersStat( "gametypeban" ); + self maps\mp\gametypes\_globallogic_score::initPersStat( "time_played_total" ); + self maps\mp\gametypes\_globallogic_score::initPersStat( "time_played_alive" ); + + self maps\mp\gametypes\_globallogic_score::initPersStat( "teamkills", false ); + self maps\mp\gametypes\_globallogic_score::initPersStat( "teamkills_nostats" ); + self.teamKillPunish = false; + if ( level.minimumAllowedTeamKills >= 0 && self.pers["teamkills_nostats"] > level.minimumAllowedTeamKills ) + self thread reduceTeamKillsOverTime(); + + if( GetDvar( #"r_reflectionProbeGenerate" ) == "1" ) + level waittill( "eternity" ); + + + self.killedPlayersCurrent = []; + + if( !isDefined( self.pers["best_kill_streak"] ) ) + { + self.pers["killed_players"] = []; + self.pers["killed_by"] = []; + self.pers["nemesis_tracking"] = []; + self.pers["artillery_kills"] = 0; + self.pers["dog_kills"] = 0; + self.pers["nemesis_name"] = ""; + self.pers["nemesis_rank"] = 0; + self.pers["nemesis_rankIcon"] = 0; + self.pers["nemesis_xp"] = 0; + self.pers["nemesis_xuid"] = ""; + + + /*self.killstreakKills["artillery"] = 0; + self.killstreakKills["dogs"] = 0; + self.killstreaksUsed["radar"] = 0; + self.killstreaksUsed["artillery"] = 0; + self.killstreaksUsed["dogs"] = 0;*/ + self.pers["best_kill_streak"] = 0; + } + +// Adding Music tracking per player CDC + if( !isDefined( self.pers["music"] ) ) + { + self.pers["music"] = spawnstruct(); + self.pers["music"].spawn = false; + self.pers["music"].inque = false; + self.pers["music"].currentState = "SILENT"; + self.pers["music"].previousState = "SILENT"; + self.pers["music"].nextstate = "UNDERSCORE"; + self.pers["music"].returnState = "UNDERSCORE"; + + } + self.leaderDialogQueue = []; + self.leaderDialogActive = false; + self.leaderDialogGroups = []; + self.leaderDialogGroup = ""; + + if ( !isdefined( self.pers["cur_kill_streak"] ) ) + self.pers["cur_kill_streak"] = 0; + if ( !isdefined( self.pers["totalKillstreakCount"] ) ) + self.pers["totalKillstreakCount"] = 0; + + //Keep track of how many killstreaks have been earned in the current streak + if ( !isdefined( self.pers["killstreaksEarnedThisKillstreak"] ) ) + self.pers["killstreaksEarnedThisKillstreak"] = 0; + + self.lastKillTime = 0; + + self.cur_death_streak = 0; + self disabledeathstreak(); + self.death_streak = 0; + self.kill_streak = 0; + self.gametype_kill_streak = 0; + + if ( level.onlineGame ) + { + self.death_streak = self getDStat( "HighestStats", "death_streak" ); + self.kill_streak = self getDStat( "HighestStats", "kill_streak" ); + self.gametype_kill_streak = self maps\mp\gametypes\_persistence::statGetWithGameType( "kill_streak" ); + } + + + self.lastGrenadeSuicideTime = -1; + + self.teamkillsThisRound = 0; + + if ( !isDefined( level.livesDoNotReset ) || !level.livesDoNotReset || !isDefined( self.pers["lives"] ) ) + self.pers["lives"] = level.numLives; + + // multi round FFA games in custom game mode should maintain team in-between rounds + if ( !level.teamBased && !maps\mp\gametypes\_customClasses::isCustomGame() ) + { + self.pers["team"] = undefined; + } + + self.hasSpawned = false; + self.waitingToSpawn = false; + self.wantSafeSpawn = false; + self.deathCount = 0; + + self.wasAliveAtMatchStart = false; + + self thread maps\mp\_flashgrenades::monitorFlash(); + + level.players[level.players.size] = self; + + if( level.splitscreen ) + setdvar( "splitscreen_playerNum", level.players.size ); + // removed underscore for debug CDC + //maps\mp\gametypes\_globallogic_audio::set_music_on_team( "UNDERSCORE", "both", true );; + // When joining a game in progress, if the game is at the post game state (scoreboard) the connecting player should spawn into intermission + if ( game["state"] == "postgame" ) + { + self.pers["needteam"] = 1; + self.pers["team"] = "spectator"; + self.team = "spectator"; + self setClientUIVisibilityFlag( "hud_visible", 0 ); + + self [[level.spawnIntermission]](); + self closeMenu(); + self closeInGameMenu(); + return; + } + + // don't count losses for CTF and S&D and War at each round. + if ( !isDefined( self.pers["lossAlreadyReported"] ) ) + { + maps\mp\gametypes\_globallogic_score::updateLossStats( self ); + self.pers["lossAlreadyReported"] = true; + } + // don't redo winstreak save to pers array for each round of round based games. + if ( !isDefined( self.pers["winstreakAlreadyCleared"] ) ) + { + // self maps\mp\gametypes\_globallogic_score::backupAndClearWinStreaks(); + self.pers["winstreakAlreadyCleared"] = true; + } + + if( self isdemoclient() ) + { + spawnpoint = maps\mp\gametypes\_spawnlogic::getRandomIntermissionPoint(); + setDemoIntermissionPoint( spawnpoint.origin, spawnpoint.angles ); + self.pers["team"] = ""; + self [[level.spectator]](); + return; + } + + if( self istestclient() ) + { + self.pers[ "isBot" ] = true; + } + + if ( level.rankedMatch ) + { + self maps\mp\gametypes\_persistence::setAfterActionReportStat( "demoFileID", "0" ); + } + + level endon( "game_ended" ); + + if ( isDefined( level.hostMigrationTimer ) ) + self thread maps\mp\gametypes\_hostmigration::hostMigrationTimerThink(); + + if ( level.oldschool ) + { + self.pers["class"] = undefined; + self.class = self.pers["class"]; + } + + if ( isDefined( self.pers["team"] ) ) + self.team = self.pers["team"]; + + if ( isDefined( self.pers["class"] ) ) + self.class = self.pers["class"]; + + if ( !isDefined( self.pers["team"] ) || IsDefined( self.pers["needteam"] ) ) + { + // Don't set .sessionteam until we've gotten the assigned team from code, + // because it overrides the assigned team. + self.pers["needteam"] = undefined; + self.pers["team"] = "spectator"; + self.team = "spectator"; + self.sessionstate = "dead"; + + self maps\mp\gametypes\_globallogic_ui::updateObjectiveText(); + + [[level.spawnSpectator]](); + + if ( level.rankedMatch ) + { + [[level.autoassign]](); + + //self thread maps\mp\gametypes\_globallogic_spawn::forceSpawn(); + self thread maps\mp\gametypes\_globallogic_spawn::kickIfDontSpawn(); + } + else if ( !level.teamBased ) + { + [[level.autoassign]](); + } + else + { + if( ( isDefined( level.forceAutoAssign ) && level.forceAutoAssign ) || level.allow_teamchange != "1" ) + { + [[level.autoassign]](); + } + else + { + self setclientdvar( "g_scriptMainMenu", game["menu_team"] ); + self openMenu( game["menu_team"] ); + } + } + + if ( self.pers["team"] == "spectator" ) + { + self.sessionteam = "spectator"; + if ( !level.teamBased ) + self.ffateam = "spectator"; + } + + if ( level.teamBased ) + { + // set team and spectate permissions so the map shows waypoint info on connect + self.sessionteam = self.pers["team"]; + if ( !isAlive( self ) ) + self.statusicon = "hud_status_dead"; + self thread maps\mp\gametypes\_spectating::setSpectatePermissions(); + } + } + else if ( self.pers["team"] == "spectator" ) + { + self setclientdvar( "g_scriptMainMenu", game["menu_team"] ); + [[level.spawnSpectator]](); + self.sessionteam = "spectator"; + self.sessionstate = "spectator"; + if ( !level.teamBased ) + self.ffateam = "spectator"; + self thread maps\mp\gametypes\_spectating::setSpectatePermissions(); + } + else + { + self.sessionteam = self.pers["team"]; + self.sessionstate = "dead"; + + if ( !level.teamBased ) + self.ffateam = self.pers["team"]; + + self maps\mp\gametypes\_globallogic_ui::updateObjectiveText(); + + [[level.spawnSpectator]](); + + if ( maps\mp\gametypes\_globallogic_utils::isValidClass( self.pers["class"] ) ) + { + self thread [[level.spawnClient]](); + } + else + { + self maps\mp\gametypes\_globallogic_ui::showMainMenuForTeam(); + } + + self thread maps\mp\gametypes\_spectating::setSpectatePermissions(); + } + + if( maps\mp\gametypes\_customClasses::isUsingCustomGameModeClasses() ) + { + self thread maps\mp\gametypes\_customClasses::sprintSpeedModifier(); + } + + if ( isDefined( self.pers["isBot"] ) ) + return; +} + +Callback_PlayerMigrated() +{ + println( "Player " + self.name + " finished migrating at time " + gettime() ); + + if ( isDefined( self.connected ) && self.connected ) + { + self maps\mp\gametypes\_globallogic_ui::updateObjectiveText(); +// self updateObjectiveText(); +// self updateMainMenu(); + +// if ( level.teambased ) +// self updateScores(); + } + + level.hostMigrationReturnedPlayerCount++; + if ( level.hostMigrationReturnedPlayerCount >= level.players.size * 2 / 3 ) + { + println( "2/3 of players have finished migrating" ); + level notify( "hostmigration_enoughplayers" ); + } +} + +Callback_PlayerDisconnect() +{ + self removePlayerOnDisconnect(); + + if ( !level.gameEnded ) + self maps\mp\gametypes\_globallogic_score::logXPGains(); + + if ( level.splitscreen ) + { + players = level.players; + + if ( players.size <= 1 ) + level thread maps\mp\gametypes\_globallogic::forceEnd(); + + // passing number of players to menus in splitscreen to display leave or end game option + setdvar( "splitscreen_playerNum", players.size ); + } + + if ( isDefined( self.score ) && isDefined( self.pers["team"] ) ) + { + setPlayerTeamRank( self, level.dropTeam, self.score - 5 * self.deaths ); + self logString( "team: score " + self.pers["team"] + ":" + self.score ); + level.dropTeam += 1; + } + + [[level.onPlayerDisconnect]](); + + lpselfnum = self getEntityNumber(); + lpGuid = self getGuid(); + logPrint("Q;" + lpGuid + ";" + lpselfnum + ";" + self.name + "\n"); + bbPrint( "mpquits: name %s client %d", self.name, lpselfnum ); + + self maps\mp\_gamerep::gameRepPlayerDisconnected(); + + for ( entry = 0; entry < level.players.size; entry++ ) + { + if ( level.players[entry] == self ) + { + while ( entry < level.players.size-1 ) + { + level.players[entry] = level.players[entry+1]; + entry++; + } + level.players[entry] = undefined; + break; + } + } + for ( entry = 0; entry < level.players.size; entry++ ) + { + if ( isDefined( level.players[entry].pers["killed_players"][self.name] ) ) + level.players[entry].pers["killed_players"][self.name] = undefined; + + if ( isDefined( level.players[entry].killedPlayersCurrent[self.name] ) ) + level.players[entry].killedPlayersCurrent[self.name] = undefined; + + if ( isDefined( level.players[entry].pers["killed_by"][self.name] ) ) + level.players[entry].pers["killed_by"][self.name] = undefined; + + if ( isDefined( level.players[entry].pers["nemesis_tracking"][self.name] ) ) + level.players[entry].pers["nemesis_tracking"][self.name] = undefined; + + // player that disconnected was our nemesis + if ( level.players[entry].pers["nemesis_name"] == self.name ) + { + level.players[entry] chooseNextBestNemesis(); + } + } + + if ( level.gameEnded ) + self maps\mp\gametypes\_globallogic::removeDisconnectedPlayerFromPlacement(); + + level thread maps\mp\gametypes\_globallogic::updateTeamStatus(); +} + +chooseNextBestNemesis() +{ + nemesisArray = self.pers["nemesis_tracking"]; + nemesisArrayKeys = getArrayKeys( nemesisArray ); + nemesisAmount = 0; + nemesisName = ""; + + if ( nemesisArrayKeys.size > 0 ) + { + for ( i = 0; i < nemesisArrayKeys.size; i++ ) + { + nemesisArrayKey = nemesisArrayKeys[i]; + if ( nemesisArray[nemesisArrayKey] > nemesisAmount ) + { + nemesisName = nemesisArrayKey; + nemesisAmount = nemesisArray[nemesisArrayKey]; + } + + } + } + + self.pers["nemesis_name"] = nemesisName; + + if ( nemesisName != "" ) + { + playerIndex = 0; + for( ; playerIndex < level.players.size; playerIndex++ ) + { + if ( level.players[playerIndex].name == nemesisName ) + { + nemesisPlayer = level.players[playerIndex]; + self.pers["nemesis_rank"] = nemesisPlayer.pers["rank"]; + self.pers["nemesis_rankIcon"] = nemesisPlayer.pers["rankxp"]; + self.pers["nemesis_xp"] = nemesisPlayer.pers["prestige"]; + self.pers["nemesis_xuid"] = nemesisPlayer GetXUID(true); + break; + } + } + } + else + { + self.pers["nemesis_xuid"] = ""; + } +} + +removePlayerOnDisconnect() +{ + for ( entry = 0; entry < level.players.size; entry++ ) + { + if ( level.players[entry] == self ) + { + while ( entry < level.players.size-1 ) + { + level.players[entry] = level.players[entry+1]; + entry++; + } + level.players[entry] = undefined; + break; + } + } +} + +custom_gamemodes_modified_damage( victim, eAttacker, iDamage, sMeansOfDeath, sWeapon, eInflictor, sHitLoc ) +{ + // regular public matches should early out + if ( level.onlinegame && !GetDvarInt( #"xblive_privatematch" ) ) + { + return iDamage; + } + + if( maps\mp\gametypes\_customClasses::isUsingCustomGameModeClasses() && isDefined( eAttacker ) ) + { + if( maps\mp\gametypes\_class::isExplosiveDamage( sMeansOfDeath, sWeapon ) ) + { + iDamage *= eAttacker maps\mp\gametypes\_customClasses::getExplosiveDamageModifier(); + } + else + { + iDamage *= eAttacker maps\mp\gametypes\_customClasses::getDamageModifier(); + } + } + if( isdefined( eAttacker) && isDefined( eAttacker.damageModifier ) ) + { + iDamage *= eAttacker.damageModifier; + } + if ( ( sMeansOfDeath == "MOD_PISTOL_BULLET" ) || ( sMeansOfDeath == "MOD_RIFLE_BULLET" ) ) + { + iDamage = int( iDamage * GetDvarFloat( #"scr_game_bulletdamage" ) ); + } + + return iDamage; +} + +custom_gamemodes_vampirism_health( iDamage, eAttacker ) +{ + // regular public matches should early out + if ( level.onlinegame && !GetDvarInt( #"xblive_privatematch" ) ) + { + return 0; + } + + return Int(iDamage * eAttacker maps\mp\gametypes\_customClasses::getHealthVampirismModifier()); +} + +figureOutAttacker( eAttacker ) +{ + if ( isdefined(eAttacker) ) + { + if( isai(eAttacker) && isDefined( eAttacker.script_owner ) ) + { + team = self.team; + + if ( IsAi( self ) && IsDefined( self.aiteam ) ) + { + team = self.aiteam; + } + + if ( eAttacker.script_owner.team != team ) + eAttacker = eAttacker.script_owner; + } + + if( eAttacker.classname == "script_vehicle" && isDefined( eAttacker.owner ) ) + eAttacker = eAttacker.owner; + else if( eAttacker.classname == "auto_turret" && isDefined( eAttacker.owner ) ) + eAttacker = eAttacker.owner; + } + + return eAttacker; +} + +figureOutWeapon( sWeapon, eInflictor ) +{ + // explosive barrel/car detection + if ( sWeapon == "none" && isDefined( eInflictor ) ) + { + if ( isDefined( eInflictor.targetname ) && eInflictor.targetname == "explodable_barrel" ) + { + sWeapon = "explodable_barrel_mp"; + } + else if ( isDefined( eInflictor.destructible_type ) && isSubStr( eInflictor.destructible_type, "vehicle_" ) ) + { + sWeapon = "destructible_car_mp"; + } + } + + return sWeapon; +} + + +handleFlameDamage( eAttacker, eInflictor, iDamage, sWeapon, sMeansOfDeath) +{ + switch( sWeapon ) + { + case "none": + if ( !self hasperk( "specialty_fireproof" ) ) + { + self thread maps\mp\_burnplayer::walkedThroughFlames( eAttacker, eInflictor, sWeapon ); + } + break; + case "m2_flamethrower_mp": + if ( !self hasperk( "specialty_fireproof" ) ) + { + self thread maps\mp\_burnplayer::burnedWithFlameThrower( sWeapon ); + } + break; + case "napalm_mp": + if ( !self hasperk( "specialty_fireproof" ) ) + { + if (isdefined (level.minDamageRequiredForNapalmBurn) && iDamage > level.minDamageRequiredForNapalmBurn) + { + self thread maps\mp\_burnplayer::hitWithNapalmStrike(eAttacker, eInflictor, "MOD_BURNED" ); + } + else + { + self thread maps\mp\_burnplayer::walkedThroughFlames( eAttacker, eInflictor, sWeapon ); + } + } + break; + case "rottweil72_mp": + //if ( !self hasperk( "specialty_fireproof" ) ) + //{ + // self thread maps\mp\_burnplayer::burnedWithDragonsBreath( eAttacker, eInflictor, sWeapon ); + //} + break; + + default: + if( GetSubStr( sWeapon, 0, 3 ) == "ft_" ) + { + if ( !self hasperk( "specialty_fireproof" ) ) + { + self thread maps\mp\_burnplayer::burnedWithFlameThrower( eAttacker, eInflictor, sWeapon ); + } + } + break; + } +} + +Callback_PlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime ) +{ + // create a class specialty checks; CAC:bulletdamage, CAC:armorvest + iDamage = maps\mp\gametypes\_class::cac_modified_damage( self, eAttacker, iDamage, sMeansOfDeath, sWeapon, eInflictor, sHitLoc ); + iDamage = custom_gamemodes_modified_damage( self, eAttacker, iDamage, sMeansOfDeath, sWeapon, eInflictor, sHitLoc ); + + iDamage = int(iDamage); + self.iDFlags = iDFlags; + self.iDFlagsTime = getTime(); + + if ( game["state"] == "postgame" ) + return; + + if ( self.sessionteam == "spectator" ) + return; + + if ( isDefined( self.canDoCombat ) && !self.canDoCombat ) + return; + + if ( isDefined( eAttacker ) && isPlayer( eAttacker ) ) + { + if( isDefined( eAttacker.canDoCombat ) && !eAttacker.canDoCombat ) + return; + + if( eAttacker.team == "spectator" || ( isDefined( eAttacker.teamSwitchExploit ) && eAttacker.teamSwitchExploit ) ) + { +/# + println( "teamSwitchExploit prevented damage from " + eAttacker.name + ".\n" ); +#/ + return; + } + } + + if ( isDefined( level.hostMigrationTimer ) ) + return; + + eAttacker = figureOutAttacker( eAttacker ); + + pixbeginevent( "PlayerDamage flags/tweaks" ); + + // Don't do knockback if the damage direction was not specified + if( !isDefined( vDir ) ) + iDFlags |= level.iDFLAGS_NO_KNOCKBACK; + + self maps\mp\gametypes\_bot::bot_damage_callback( eAttacker, iDamage, sMeansOfDeath, sWeapon, eInflictor, sHitLoc ); + + friendly = false; + // Todo MGordon - Fix this stat collection + //self thread maps\mp\gametypes\_globallogic_score::threadedSetStatLBByName( sWeapon, 1, "hits by", 2 ); + + if ( ((self.health == self.maxhealth)) || !isDefined( self.attackers ) ) + { + self.attackers = []; + self.attackerData = []; + self.attackerDamage = []; + self.firstTimeDamaged = getTime(); + } + // added check to notify chatter to play pain vo + if (self.health != self.maxhealth) + { + self notify( "snd_pain_player" ); + } + + if ( IsDefined( eInflictor) && IsDefined( eInflictor.script_noteworthy) && eInflictor.script_noteworthy == "ragdoll_now" ) + { + sMeansOfDeath = "MOD_FALLING"; + } + + if ( maps\mp\gametypes\_globallogic_utils::isHeadShot( sWeapon, sHitLoc, sMeansOfDeath ) && isPlayer(eAttacker) ) + { + //Turning off damage headshot sounds to avoid confusion from the killing headshot sound. + //if (self.team != eAttacker.team) + //{ + // eAttacker playLocalSound( "prj_bullet_impact_headshot_helmet_nodie_2d" ); + //} + sMeansOfDeath = "MOD_HEAD_SHOT"; + } + + modifiedDamage = [[level.onPlayerDamage]]( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime ); + if ( isDefined( modifiedDamage ) ) + iDamage = modifiedDamage; + + if ( maps\mp\gametypes\_tweakables::getTweakableValue( "game", "onlyheadshots" ) ) + { + if ( sMeansOfDeath == "MOD_PISTOL_BULLET" || sMeansOfDeath == "MOD_RIFLE_BULLET" ) + return; + else if ( sMeansOfDeath == "MOD_HEAD_SHOT" ) + iDamage = 150; + } + + // Make all vehicle drivers invulnerable to bullets + if ( self maps\mp\_vehicles::player_is_occupant_invulnerable( sMeansOfDeath ) ) + return; + + if (isdefined (eAttacker) && isPlayer(eAttacker) && (self.team != eAttacker.team)) + { + self.lastAttackWeapon = sWeapon; + + if ( eAttacker maps\mp\_vehicles::player_is_driver() ) + { + vehicle = eAttacker GetVehicleOccupied(); + self.lastTankThatAttacked = vehicle; + self thread maps\mp\gametypes\_globallogic_vehicle::clearLastTankAttacker(); + } + + // rottweil72 shoots dragon's breath rounds, so show a little fire on the player who got damaged + if( sMeansOfDeath == "MOD_BURNED" || sWeapon == "rottweil72_mp" ) + { + handleFlameDamage( eAttacker, eInflictor, iDamage, sWeapon, sMeansOfDeath); + } + } + + sWeapon = figureOutWeapon( sWeapon, eInflictor ); + + pixendevent( "END: PlayerDamage flags/tweaks" ); + + if( iDFlags & level.iDFLAGS_PENETRATION && isplayer ( eAttacker ) && eAttacker hasPerk( "specialty_bulletpenetration" ) ) + self thread maps\mp\gametypes\_battlechatter_mp::perkSpecificBattleChatter( "deepimpact", true ); + + // check for completely getting out of the damage + if( !(iDFlags & level.iDFLAGS_NO_PROTECTION) ) + { + if( ( isSubStr( sMeansOfDeath, "MOD_GRENADE" ) || isSubStr( sMeansOfDeath, "MOD_EXPLOSIVE" ) || isSubStr( sMeansOfDeath, "MOD_PROJECTILE" ) || isSubStr( sMeansOfDeath, "MOD_GAS" ) ) && + isDefined( eInflictor ) ) + { + // protect players from spawnkill grenades, tabun and incendiary + if ( ( eInflictor.classname == "grenade" || sweapon == "tabun_gas_mp" ) && (self.lastSpawnTime + 3500) > getTime() && distance( eInflictor.origin, self.lastSpawnPoint.origin ) < 250 ) + { +// pixmarker( "END: Callback_PlayerDamage player" ); + return; + } + + self.explosiveInfo = []; + self.explosiveInfo["damageTime"] = getTime(); + self.explosiveInfo["damageId"] = eInflictor getEntityNumber(); + self.explosiveInfo["returnToSender"] = false; + self.explosiveInfo["bulletPenetrationKill"] = false; + self.explosiveInfo["chainKill"] = false; + self.explosiveInfo["counterKill"] = false; + self.explosiveInfo["chainKill"] = false; + self.explosiveInfo["cookedKill"] = false; + self.explosiveInfo["weapon"] = sWeapon; + self.explosiveInfo["originalowner"] = eInflictor.originalowner; + + isFrag = isSubStr( sWeapon, "frag_" ); + + if ( eAttacker != self ) + { + if ( (isSubStr( sWeapon, "satchel_" ) || isSubStr( sWeapon, "claymore_" ) ) && isDefined( eAttacker ) && isDefined( eInflictor.owner ) ) + { + self.explosiveInfo["returnToSender"] = (eInflictor.owner == self); + self.explosiveInfo["counterKill"] = isDefined( eInflictor.wasDamaged ); + self.explosiveInfo["chainKill"] = isDefined( eInflictor.wasChained ); + self.explosiveInfo["ohnoyoudontKill"] = isDefined( eInflictor.wasJustPlanted ); + self.explosiveInfo["bulletPenetrationKill"] = isDefined( eInflictor.wasDamagedFromBulletPenetration ); + self.explosiveInfo["cookedKill"] = false; + } + if ( ( sWeapon == "sticky_grenade_mp" || sWeapon == "explosive_bolt_mp" ) && isDefined( eInflictor ) && isdefined( eInflictor.stuckToPlayer ) ) + { + self.explosiveInfo["stuckToPlayer"] = eInflictor.stuckToPlayer; + } + if ( isDefined( eAttacker.lastGrenadeSuicideTime ) && eAttacker.lastGrenadeSuicideTime >= gettime() - 50 && isFrag ) + { + self.explosiveInfo["suicideGrenadeKill"] = true; + } + else + { + self.explosiveInfo["suicideGrenadeKill"] = false; + } + } + + if ( isFrag ) + { + self.explosiveInfo["cookedKill"] = isDefined( eInflictor.isCooked ); + self.explosiveInfo["throwbackKill"] = isDefined( eInflictor.threwBack ); + } + + if( isPlayer( eAttacker ) && eAttacker != self ) + { + self maps\mp\gametypes\_globallogic_score::setInflictorStat( eInflictor, eAttacker, sWeapon ); + } + } + + if( isSubStr( sMeansOfDeath, "MOD_IMPACT" ) && isDefined( eAttacker ) && isPlayer( eAttacker ) && eAttacker != self ) + { + if ( sWeapon != "knife_ballistic_mp" ) + { + self maps\mp\gametypes\_globallogic_score::setInflictorStat( eInflictor, eAttacker, sWeapon ); + } + + if ( sWeapon == "hatchet_mp" && isDefined( eInflictor ) ) + { + self.explosiveInfo["projectile_bounced"] = isDefined( eInflictor.bounced ); + } + } + + if ( isPlayer( eAttacker ) ) + eAttacker.pers["participation"]++; + + prevHealthRatio = self.health / self.maxhealth; + + if ( level.teamBased && isPlayer( eAttacker ) && (self != eAttacker) && (self.team == eAttacker.team) ) + { + pixmarker( "BEGIN: PlayerDamage player" ); // profs automatically end when the function returns + if ( level.friendlyfire == 0 ) // no one takes damage + { + if ( sWeapon == "artillery_mp" || sWeapon == "airstrike_mp" || sWeapon == "napalm_mp" || sWeapon == "mortar_mp" ) + self damageShellshockAndRumble( eAttacker, eInflictor, sWeapon, sMeansOfDeath, iDamage ); + return; + } + else if ( level.friendlyfire == 1 ) // the friendly takes damage + { + // Make sure at least one point of damage is done + if ( iDamage < 1 ) + iDamage = 1; + + //check for friendly fire at the begining of the match. apply the damage to the attacker only + if( level.friendlyFireDelay && level.friendlyFireDelayTime >= ( ( ( gettime() - level.startTime ) - level.discardTime ) / 1000 ) ) + { + eAttacker.lastDamageWasFromEnemy = false; + + eAttacker.friendlydamage = true; + eAttacker finishPlayerDamageWrapper(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime); + eAttacker.friendlydamage = undefined; + } + else + { + self.lastDamageWasFromEnemy = false; + + self finishPlayerDamageWrapper(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime); + } + } + else if ( level.friendlyfire == 2 && isAlive( eAttacker ) ) // only the attacker takes damage + { + iDamage = int(iDamage * .5); + + // Make sure at least one point of damage is done + if(iDamage < 1) + iDamage = 1; + + eAttacker.lastDamageWasFromEnemy = false; + + eAttacker.friendlydamage = true; + eAttacker finishPlayerDamageWrapper(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime); + eAttacker.friendlydamage = undefined; + } + else if ( level.friendlyfire == 3 && isAlive( eAttacker ) ) // both friendly and attacker take damage + { + iDamage = int(iDamage * .5); + + // Make sure at least one point of damage is done + if ( iDamage < 1 ) + iDamage = 1; + + self.lastDamageWasFromEnemy = false; + eAttacker.lastDamageWasFromEnemy = false; + + self finishPlayerDamageWrapper(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime); + eAttacker.friendlydamage = true; + eAttacker finishPlayerDamageWrapper(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime); + eAttacker.friendlydamage = undefined; + } + + friendly = true; + pixmarker( "END: PlayerDamage player" ); + } + else + { + // Make sure at least one point of damage is done + if(iDamage < 1) + iDamage = 1; + + if ( isDefined( eAttacker ) && isPlayer( eAttacker ) && allowedAssistWeapon( sWeapon ) ) + { + trackAttackerDamage( eAttacker, iDamage, sMeansOfDeath, sWeapon ); + } + + giveInflictorOwnerAssist( eAttacker, eInflictor, iDamage, sMeansOfDeath, sWeapon ); + + if ( isdefined( eAttacker ) ) + level.lastLegitimateAttacker = eAttacker; + + if ( isdefined( eAttacker ) && isPlayer( eAttacker ) && isDefined( sWeapon ) && !issubstr( sMeansOfDeath, "MOD_MELEE" ) ) + eAttacker thread maps\mp\gametypes\_weapons::checkHit( sWeapon ); + + if ( issubstr( sMeansOfDeath, "MOD_GRENADE" ) && isDefined( eInflictor.isCooked ) ) + self.wasCooked = getTime(); + else + self.wasCooked = undefined; + + self.lastDamageWasFromEnemy = (isDefined( eAttacker ) && (eAttacker != self)); + + if ( self.lastDamageWasFromEnemy ) + eAttacker.damagedPlayers[ self.clientId ] = getTime(); + + self finishPlayerDamageWrapper(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime); + + self thread maps\mp\gametypes\_missions::playerDamaged(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, sHitLoc ); + + if ( isdefined ( eAttacker ) ) + { + // if vampirism is on, give the attacker health as a % of damage done to the player + eAttacker.health += custom_gamemodes_vampirism_health( iDamage, eAttacker ); + } + } + + if ( isdefined(eAttacker) && isplayer( eAttacker ) && eAttacker != self ) + { + if ( doDamageFeedback( sWeapon, eInflictor ) ) + { + hasBodyArmor = false; + + if ( iDamage > 0 ) + { + // if the attacker has tactical mask pro then we show the special yellow indicator + if( IsPlayer( eAttacker ) && eAttacker HasPerk( "specialty_shades" ) && eAttacker HasPerk( "specialty_stunprotection" ) && eAttacker HasPerk( "specialty_gas_mask" ) ) + { + // show the yellow indicator if this is a flash or concussion grenade and they don't have tactical mask on + if( sMeansOfDeath == "MOD_GRENADE_SPLASH" && + ( sWeapon == "flash_grenade_mp" || sWeapon == "concussion_grenade_mp" ) && + ( !self HasPerk( "specialty_shades" ) || !self HasPerk( "specialty_stunprotection" ) ) ) + { + eAttacker thread maps\mp\gametypes\_damagefeedback::updateSpecialDamageFeedback( self ); + } + } + + eAttacker thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback( hasBodyArmor, sMeansOfDeath ); + } + } + } + + self.hasDoneCombat = true; + } + + if(self.sessionstate != "dead") + self maps\mp\gametypes\_gametype_variants::onPlayerTakeDamage( eAttacker, eInflictor, sWeapon, iDamage, sMeansOfDeath ); + + if ( isdefined( eAttacker ) && eAttacker != self && !friendly ) + level.useStartSpawns = false; + + pixbeginevent( "PlayerDamage log" ); + + // Do debug print if it's enabled + if(GetDvarInt( #"g_debugDamage")) + println("client:" + self getEntityNumber() + " health:" + self.health + " attacker:" + eAttacker.clientid + " inflictor is player:" + isPlayer(eInflictor) + " damage:" + iDamage + " hitLoc:" + sHitLoc); + + if(self.sessionstate != "dead") + { + lpselfnum = self getEntityNumber(); + lpselfname = self.name; + lpselfteam = self.team; + lpselfGuid = self getGuid(); + lpattackerteam = ""; + lpattackerorigin = ( 0, 0, 0 ); + + if(isPlayer(eAttacker)) + { + lpattacknum = eAttacker getEntityNumber(); + lpattackGuid = eAttacker getGuid(); + lpattackname = eAttacker.name; + lpattackerteam = eAttacker.team; + lpattackerorigin = eAttacker.origin; + bbPrint( "mpattacks: gametime %d attackerspawnid %d attackerweapon %s attackerx %f attackery %f attackerz %f victimspawnid %d victimx %f victimy %f victimz %f damage %d damagetype %s damagelocation %s death 0", + gettime(), getplayerspawnid( eAttacker ), sWeapon, lpattackerorigin, getplayerspawnid( self ), self.origin, iDamage, sMeansOfDeath, sHitLoc ); + } + else + { + lpattacknum = -1; + lpattackGuid = ""; + lpattackname = ""; + lpattackerteam = "world"; + bbPrint( "mpattacks: gametime %d attackerweapon %s victimspawnid %d victimx %f victimy %f victimz %f damage %d damagetype %s damagelocation %s death 0", + gettime(), sWeapon, getplayerspawnid( self ), self.origin, iDamage, sMeansOfDeath, sHitLoc ); + } + logPrint("D;" + lpselfGuid + ";" + lpselfnum + ";" + lpselfteam + ";" + lpselfname + ";" + lpattackGuid + ";" + lpattacknum + ";" + lpattackerteam + ";" + lpattackname + ";" + sWeapon + ";" + iDamage + ";" + sMeansOfDeath + ";" + sHitLoc + "\n"); + } + +/# + if ( GetDvarInt( #"scr_hitloc_debug") ) + { + if ( !isdefined( eAttacker.hitlocInited ) ) + { + for ( i = 0; i < 6; i++ ) + { + eAttacker setClientDvar( "ui_hitloc_" + i, "" ); + } + eAttacker.hitlocInited = true; + } + + if ( isPlayer( eAttacker ) && !level.splitscreen ) + { + colors = []; + colors[0] = 2; + colors[1] = 3; + colors[2] = 5; + colors[3] = 7; + + elemcount = 6; + if ( !isdefined( eAttacker.damageInfo ) ) + { + eAttacker.damageInfo = []; + for ( i = 0; i < elemcount; i++ ) + { + eAttacker.damageInfo[i] = spawnstruct(); + eAttacker.damageInfo[i].damage = 0; + eAttacker.damageInfo[i].hitloc = ""; + eAttacker.damageInfo[i].bp = false; + eAttacker.damageInfo[i].jugg = false; + eAttacker.damageInfo[i].colorIndex = 0; + } + eAttacker.damageInfoColorIndex = 0; + eAttacker.damageInfoVictim = undefined; + } + + for ( i = elemcount-1; i > 0; i-- ) + { + eAttacker.damageInfo[i].damage = eAttacker.damageInfo[i - 1].damage; + eAttacker.damageInfo[i].hitloc = eAttacker.damageInfo[i - 1].hitloc; + eAttacker.damageInfo[i].bp = eAttacker.damageInfo[i - 1].bp; + eAttacker.damageInfo[i].jugg = eAttacker.damageInfo[i - 1].jugg; + eAttacker.damageInfo[i].colorIndex = eAttacker.damageInfo[i - 1].colorIndex; + } + eAttacker.damageInfo[0].damage = iDamage; + eAttacker.damageInfo[0].hitloc = sHitLoc; + eAttacker.damageInfo[0].bp = (iDFlags & level.iDFLAGS_PENETRATION); + eAttacker.damageInfo[0].jugg = false; + if ( isdefined( eAttacker.damageInfoVictim ) && eAttacker.damageInfoVictim != self ) + { + eAttacker.damageInfoColorIndex++; + if ( eAttacker.damageInfoColorIndex == colors.size ) + eAttacker.damageInfoColorIndex = 0; + } + eAttacker.damageInfoVictim = self; + eAttacker.damageInfo[0].colorIndex = eAttacker.damageInfoColorIndex; + + for ( i = 0; i < elemcount; i++ ) + { + color = "^" + colors[ eAttacker.damageInfo[i].colorIndex ]; + if ( eAttacker.damageInfo[i].hitloc != "" ) + { + val = color + eAttacker.damageInfo[i].hitloc; + if ( eAttacker.damageInfo[i].bp ) + val += " (BP)"; + if ( eAttacker.damageInfo[i].jugg ) + val += " (Jugg)"; + eAttacker setClientDvar( "ui_hitloc_" + i, val ); + } + eAttacker setClientDvar( "ui_hitloc_damage_" + i, color + eAttacker.damageInfo[i].damage ); + } + } + } +#/ + pixendevent( "END: PlayerDamage log" ); +} + +resetAttackerList() +{ + self endon( "disconnect" ); + self endon( "death" ); + level endon( "game_ended" ); + + self.attackers = []; + self.attackerData = []; + self.attackerDamage = []; +} + +doDamageFeedback( sWeapon, eInflictor ) +{ + if ( !IsDefined( sWeapon ) ) + return false; + + switch(sWeapon) + { + case "artillery_mp": + case "airstrike_mp": + case "napalm_mp": + case "mortar_mp": + case "tow_turret_mp": + case "auto_gun_turret_mp": + case "cobra_20mm_comlink_mp": + return false; + } + + if ( IsDefined( eInflictor ) ) + { + if ( IsAI(eInflictor) ) + { + return false; + } + } + + return true; +} + +doPrintDamage(dmg, hitloc) +{ + huddamage = newclienthudelem(self); + huddamage.alignx = "center"; + huddamage.horzalign = "center"; + huddamage.x = 10; + huddamage.y = 235; + huddamage.fontscale = 1.6; + huddamage.font = "objective"; + huddamage setvalue(dmg); + + if (hitloc == "head") + huddamage.color = (1, 1, 0.25); + + huddamage moveovertime(1); + huddamage fadeovertime(1); + huddamage.alpha = 0; + huddamage.x = randomIntRange(25, 70); + + val = 1; + if (cointoss()) + val = -1; + + huddamage.y = 235 + randomIntRange(25, 70) * val; + + wait 1; + + huddamage destroy(); +} + +finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime ) +{ + pixbeginevent("finishPlayerDamageWrapper"); + + if ( isDefined( eAttacker ) && isPlayer( eAttacker ) ) + eAttacker thread doPrintDamage(iDamage, sHitLoc); + else if( isDefined( eAttacker.owner ) && isPlayer( eAttacker.owner ) ) + eAttacker.owner thread doPrintDamage(iDamage, sHitLoc); + + surface = "flesh"; + + if ( self.cac_body_type == "body_armor_mp" ) + { + surface = "metal"; + } + + self finishPlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, surface ); + + if ( GetDvar( #"scr_csmode" ) != "" ) + self shellShock( "damage_mp", 0.2 ); + + self damageShellshockAndRumble( eAttacker, eInflictor, sWeapon, sMeansOfDeath, iDamage ); + pixendevent(); +} + +allowedAssistWeapon( weapon ) +{ + if ( !maps\mp\gametypes\_hardpoints::isKillstreakWeapon( weapon ) ) + return true; + + if (maps\mp\gametypes\_hardpoints::isKillstreakWeaponAssistAllowed( weapon ) ) + return true; + + return false; +} + +GiveCustomGameModePlayerKilledScore( attacker, sMeansOfDeath ) +{ + if( !maps\mp\gametypes\_customClasses::isCustomGame() ) + return; + + if( level.gameType != "tdm" && level.gameType != "dm" ) + return; + + if( isDefined( attacker ) && ( self == attacker || ( attacker.classname == "trigger_hurt" || attacker.classname == "worldspawn" ) ) ) + { + maps\mp\gametypes\_globallogic_score::givePlayerScore( "suicide", self, self ); + maps\mp\gametypes\_globallogic_score::giveTeamScore( "suicide", self.team, self, self ); + return; // no other bonuses if you're suicidin' + } + + if( sMeansOfDeath == "MOD_HEAD_SHOT" ) + { + maps\mp\gametypes\_globallogic_score::givePlayerScore( "headshot", attacker, self ); + maps\mp\gametypes\_globallogic_score::giveTeamScore( "headshot", attacker.team, attacker, self ); + } + if( isDefined( level.placement ) ) + { + maps\mp\gametypes\_globallogic::updatePlacement(); + if( attacker maps\mp\gametypes\_customClasses::shouldGiveLeaderBonus() ) + { + leaderbonus = getDvarInt( "scr_" + level.gameType + "_bonus_leader" ); + if( isDefined( leaderBonus ) ) + { + maps\mp\gametypes\_globallogic_score::_setPlayerScore( attacker, attacker.pers["score"] + leaderBonus ); + maps\mp\gametypes\_globallogic_score::onTeamScore( leaderBonus, attacker.team, attacker, self ); + maps\mp\gametypes\_globallogic_score::updateTeamScores( attacker.team ); + } + } + } + maps\mp\gametypes\_globallogic_score::givePlayerScore( "death", self, self ); + maps\mp\gametypes\_globallogic_score::giveTeamScore( "death", self.team, self, self ); + +} + +Callback_PlayerKilled(eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration) +{ + self endon( "spawned" ); + self notify( "killed_player" ); + + if ( self.sessionteam == "spectator" ) + return; + + if ( game["state"] == "postgame" ) + return; + + self needsRevive( false ); + + if ( isdefined( self.burning ) && self.burning == true ) + { + self setburn( 0 ); + } + + self.suicide = false; + + if ( isDefined( level.takeLivesOnDeath ) && ( level.takeLivesOnDeath == true ) ) + { + if ( self.pers["lives"] ) + { + self.pers["lives"]--; + if ( self.pers["lives"] == 0 ) + { + level notify( "player_eliminated" ); + self notify( "player_eliminated" ); + } + + } + } + + sWeapon = updateWeapon( eInflictor, sWeapon ); + + pixbeginevent( "PlayerKilled pre constants" ); + + wasInLastStand = false; + deathTimeOffset = 0; + lastWeaponBeforeDroppingIntoLastStand = undefined; + attackerStance = undefined; + self.lastStandThisLife = undefined; + self.vAttackerOrigin = undefined; + + if ( isdefined( self.useLastStandParams ) ) + { + self.useLastStandParams = undefined; + + assert( isdefined( self.lastStandParams ) ); + if ( !level.teamBased || ( !isDefined( attacker ) || !isplayer( attacker ) || attacker.team != self.team || attacker == self ) ) + { + eInflictor = self.lastStandParams.eInflictor; + attacker = self.lastStandParams.attacker; + attackerStance = self.lastStandParams.attackerStance; + iDamage = self.lastStandParams.iDamage; + sMeansOfDeath = self.lastStandParams.sMeansOfDeath; + sWeapon = self.lastStandParams.sWeapon; + vDir = self.lastStandParams.vDir; + sHitLoc = self.lastStandParams.sHitLoc; + self.vAttackerOrigin = self.lastStandParams.vAttackerOrigin; + deathTimeOffset = (gettime() - self.lastStandParams.lastStandStartTime) / 1000; + + self thread maps\mp\gametypes\_battlechatter_mp::perkSpecificBattleChatter( "secondchance" ); + + if ( isDefined( self.previousPrimary ) ) + { + wasInLastStand = true; + lastWeaponBeforeDroppingIntoLastStand = self.previousPrimary; + } + } + self.lastStandParams = undefined; + } + + bestPlayer = undefined; + bestPlayerMeansOfDeath = undefined; + obituaryMeansOfDeath = undefined; + bestPlayerWeapon = undefined; + obituaryWeapon = undefined; + + if ( (!isDefined( attacker ) || attacker.classname == "trigger_hurt" || attacker.classname == "worldspawn" || ( isdefined( attacker.isMagicBullet ) && attacker.isMagicBullet == true ) || attacker == self ) && isDefined( self.attackers ) ) + { + if ( !isDefined(bestPlayer) ) + { + for ( i = 0; i < self.attackers.size; i++ ) + { + player = self.attackers[i]; + if ( !isDefined( player ) ) + continue; + + if (!isDefined( self.attackerDamage[ player.clientId ] ) || ! isDefined( self.attackerDamage[ player.clientId ].damage ) ) + continue; + + if ( player == self || (level.teamBased && player.team == self.team ) ) + continue; + + if ( self.attackerDamage[ player.clientId ].lasttimedamaged + 2500 < getTime() ) + continue; + + if ( !allowedAssistWeapon( self.attackerDamage[ player.clientId ].weapon ) ) + continue; + + if ( self.attackerDamage[ player.clientId ].damage > 1 && ! isDefined( bestPlayer ) ) + { + bestPlayer = player; + bestPlayerMeansOfDeath = self.attackerDamage[ player.clientId ].meansOfDeath; + bestPlayerWeapon = self.attackerDamage[ player.clientId ].weapon; + } + else if ( isDefined( bestPlayer ) && self.attackerDamage[ player.clientId ].damage > self.attackerDamage[ bestPlayer.clientId ].damage ) + { + bestPlayer = player; + bestPlayerMeansOfDeath = self.attackerDamage[ player.clientId ].meansOfDeath; + bestPlayerWeapon = self.attackerDamage[ player.clientId ].weapon; + } + } + } + if ( isdefined ( bestPlayer ) ) + bestPlayer maps\mp\_medals::assistedSuicide(bestPlayerWeapon); + } + + if ( isdefined ( bestPlayer ) ) + { + attacker = bestPlayer; + obituaryMeansOfDeath = bestPlayerMeansOfDeath; + obituaryWeapon = bestPlayerWeapon; + } + + if ( isplayer( attacker ) ) + attacker.damagedPlayers[self.clientid] = undefined; + + if( maps\mp\gametypes\_globallogic_utils::isHeadShot( sWeapon, sHitLoc, sMeansOfDeath ) && isPlayer( attacker ) ) + { + attacker playLocalSound( "prj_bullet_impact_headshot_helmet_nodie_2d" ); + //attacker playLocalSound( "prj_bullet_impact_headshot_2d" ); + + sMeansOfDeath = "MOD_HEAD_SHOT"; + } + + self.deathTime = getTime(); + + attacker = updateAttacker( attacker ); + eInflictor = updateInflictor( eInflictor ); + + sMeansOfDeath = updateMeansOfDeath( sWeapon, sMeansOfDeath ); + + self thread updateGlobalBotKilledCounter(); + if ( maps\mp\gametypes\_hardpoints::isKillstreakWeapon( sWeapon ) ) + { + level.globalKillstreaksDeathsFrom++; + } + + // Don't increment weapon stats for team kills or deaths + if ( isPlayer( attacker ) && attacker != self && ( !level.teamBased || ( level.teamBased && self.team != attacker.team ) ) ) + { + self thread maps\mp\gametypes\_globallogic_score::trackLeaderBoardDeathStats( sWeapon, sMeansOfDeath ); + + if ( wasInLastStand && isDefined( lastWeaponBeforeDroppingIntoLastStand ) ) + weaponName = lastWeaponBeforeDroppingIntoLastStand; + else + weaponName = self.lastdroppableweapon; + + if ( isDefined( weaponName ) && ( isSubStr( weaponName, "gl_" ) || isSubStr( weaponName, "mk_" ) || isSubStr( weaponName, "ft_" ) ) ) + weaponName = self.currentWeapon; + + if ( isDefined( weaponName ) ) + self thread maps\mp\gametypes\_globallogic_score::trackLeaderBoardDeathsDuringUseStats( weaponName ); + + attacker thread maps\mp\gametypes\_globallogic_score::trackAttackerLeaderBoardDeathStats( sWeapon, sMeansOfDeath ); + } + + if ( !isdefined( obituaryMeansOfDeath ) ) + obituaryMeansOfDeath = sMeansOfDeath; + if ( !isdefined( obituaryWeapon ) ) + obituaryWeapon = sWeapon; + + // send out an obituary message to all clients about the kill + if( level.teamBased && isDefined( attacker.pers ) && self.team == attacker.team && obituaryMeansOfDeath == "MOD_GRENADE" && level.friendlyfire == 0 ) + { + obituary(self, self, obituaryWeapon, obituaryMeansOfDeath); + maps\mp\_demo::bookmark( "kill", gettime(), self, self ); + } + else + { + obituary(self, attacker, obituaryWeapon, obituaryMeansOfDeath); + maps\mp\_demo::bookmark( "kill", gettime(), self, attacker ); + } + +// self maps\mp\gametypes\_weapons::updateWeaponUsageStats(); + if ( !level.inGracePeriod ) + { + self maps\mp\gametypes\_weapons::dropScavengerForDeath( attacker ); + self maps\mp\gametypes\_weapons::dropWeaponForDeath( attacker ); + self maps\mp\gametypes\_weapons::dropOffhand(); + } + + maps\mp\gametypes\_spawnlogic::deathOccured(self, attacker); + + self.sessionstate = "dead"; + self.statusicon = "hud_status_dead"; + + self.pers["weapon"] = undefined; + + self.killedPlayersCurrent = []; + + self.deathCount++; + +/# + println( "players("+self.clientId+") death count ++: " + self.deathCount ); +#/ + + if( !isDefined( self.switching_teams ) ) + { + // if team killed we reset kill streak, but dont count death and death streak + if ( isPlayer( attacker ) && level.teamBased && ( attacker != self ) && ( self.team == attacker.team ) ) + { + + self.pers["cur_kill_streak"] = 0; + self.pers["totalKillstreakCount"] = 0; + self.pers["killstreaksEarnedThisKillstreak"] = 0; + } + else + { + self maps\mp\gametypes\_globallogic_score::incPersStat( "deaths", 1, true, true ); + self.deaths = self maps\mp\gametypes\_globallogic_score::getPersStat( "deaths" ); + self maps\mp\gametypes\_globallogic_score::updatePersRatio( "kdratio", "kills", "deaths" ); + + if( self.pers["cur_kill_streak"] > self.pers["best_kill_streak"] ) + self.pers["best_kill_streak"] = self.pers["cur_kill_streak"]; + + // need to keep the current killstreak to see if this was a buzzkill later + self.pers["kill_streak_before_death"] = self.pers["cur_kill_streak"]; + + self.pers["cur_kill_streak"] = 0; + self.pers["totalKillstreakCount"] = 0; + self.pers["killstreaksEarnedThisKillstreak"] = 0; + + self.cur_death_streak++; + + if ( self.cur_death_streak > self.death_streak ) + { + self setDStat( "HighestStats", "death_streak", self.cur_death_streak ); + self.death_streak = self.cur_death_streak; + } + + if( self.cur_death_streak >= GetDvarInt( #"perk_deathStreakCountRequired" ) ) + { + self enabledeathstreak(); + } + } + } + else + { + self.pers["totalKillstreakCount"] = 0; + self.pers["killstreaksEarnedThisKillstreak"] = 0; + } + + lpselfnum = self getEntityNumber(); + lpselfname = self.name; + lpattackGuid = ""; + lpattackname = ""; + lpselfteam = self.team; + lpselfguid = self getGuid(); + lpattackteam = ""; + lpattackorigin = ( 0, 0, 0 ); + + lpattacknum = -1; + + //check if we should award assist points + awardAssists = false; + + pixendevent(); // "PlayerKilled pre constants" ); + + self GiveCustomGameModePlayerKilledScore( attacker, sMeansOfDeath ); + + if( isPlayer( attacker ) ) + { + lpattackGuid = attacker getGuid(); + lpattackname = attacker.name; + lpattackteam = attacker.team; + lpattackorigin = attacker.origin; + + if ( attacker == self ) // killed himself + { + doKillcam = false; + + // switching teams + if ( isDefined( self.switching_teams ) ) + { + if ( !level.teamBased && ((self.leaving_team == "allies" && self.joining_team == "axis") || (self.leaving_team == "axis" && self.joining_team == "allies")) ) + { + playerCounts = self maps\mp\gametypes\_teams::CountPlayers(); + playerCounts[self.leaving_team]--; + playerCounts[self.joining_team]++; + + if( (playerCounts[self.joining_team] - playerCounts[self.leaving_team]) > 1 ) + { + self thread [[level.onXPEvent]]( "suicide" ); + self maps\mp\gametypes\_globallogic_score::incPersStat( "suicides", 1 ); + self.suicides = self maps\mp\gametypes\_globallogic_score::getPersStat( "suicides" ); + } + } + } + else + { + self thread [[level.onXPEvent]]( "suicide" ); + self maps\mp\gametypes\_globallogic_score::incPersStat( "suicides", 1 ); + self.suicides = self maps\mp\gametypes\_globallogic_score::getPersStat( "suicides" ); + + if ( sMeansOfDeath == "MOD_SUICIDE" && sHitLoc == "none" && self.throwingGrenade ) + { + self.lastGrenadeSuicideTime = gettime(); + } + + //Check for player death related battlechatter + thread maps\mp\gametypes\_battlechatter_mp::onPlayerSuicideOrTeamKill( self, "suicide" ); //Play suicide battlechatter + + //check if assist points should be awarded + awardAssists = true; + self.suicide = true; + } + + if( isDefined( self.friendlydamage ) ) + { + self iPrintLn(&"MP_FRIENDLY_FIRE_WILL_NOT"); + if ( maps\mp\gametypes\_tweakables::getTweakableValue( "team", "teamkillpointloss" ) ) + { + scoreSub = self [[level.getTeamKillScore]]( eInflictor, attacker, sMeansOfDeath, sWeapon); + maps\mp\gametypes\_globallogic_score::_setPlayerScore( attacker,maps\mp\gametypes\_globallogic_score::_getPlayerScore( attacker ) - scoreSub ); + } + } + } + else + { + pixbeginevent( "PlayerKilled attacker" ); + + lpattacknum = attacker getEntityNumber(); + + doKillcam = true; + + self thread maps\mp\gametypes\_gametype_variants::playerKilled( attacker ); + + if ( level.teamBased && self.team == attacker.team && sMeansOfDeath == "MOD_GRENADE" && level.friendlyfire == 0 ) + { + } + else if ( level.teamBased && self.team == attacker.team ) // killed by a friendly + { + attacker thread [[level.onXPEvent]]( "teamkill" ); + + if ( !IgnoreTeamKills( sWeapon, sMeansOfDeath ) ) + { + teamkill_penalty = self [[level.getTeamKillPenalty]]( eInflictor, attacker, sMeansOfDeath, sWeapon); + + attacker maps\mp\gametypes\_globallogic_score::incPersStat( "teamkills_nostats", teamkill_penalty, false ); + attacker maps\mp\gametypes\_globallogic_score::incPersStat( "teamkills", 1 ); //save team kills to player stats + attacker.teamkillsThisRound++; + + if ( maps\mp\gametypes\_tweakables::getTweakableValue( "team", "teamkillpointloss" ) ) + { + scoreSub = self [[level.getTeamKillScore]]( eInflictor, attacker, sMeansOfDeath, sWeapon); + maps\mp\gametypes\_globallogic_score::_setPlayerScore( attacker,maps\mp\gametypes\_globallogic_score::_getPlayerScore( attacker ) - scoreSub ); + } + + if ( maps\mp\gametypes\_globallogic_utils::getTimePassed() < 5000 ) + teamKillDelay = 1; + else if ( attacker.pers["teamkills_nostats"] > 1 && maps\mp\gametypes\_globallogic_utils::getTimePassed() < (8000 + (attacker.pers["teamkills_nostats"] * 1000)) ) + teamKillDelay = 1; + else + teamKillDelay = attacker TeamKillDelay(); + + if ( teamKillDelay > 0 ) + { + attacker.teamKillPunish = true; + attacker suicide(); + + if ( attacker ShouldTeamKillKick(teamKillDelay) ) + { + attacker TeamKillKick(); + } + + attacker thread reduceTeamKillsOverTime(); + } + + //Play teamkill battlechatter + if( isPlayer( attacker ) ) + thread maps\mp\gametypes\_battlechatter_mp::onPlayerSuicideOrTeamKill( attacker, "teamkill" ); + } + } + else + { + maps\mp\gametypes\_globallogic_score::incTotalKills(attacker.team); + + attacker thread maps\mp\gametypes\_globallogic_score::giveKillStats( sMeansOfDeath, sWeapon, self ); + + self maps\mp\gametypes\_copycat::copycat_clone_loadout( attacker ); + + if ( isAlive( attacker ) ) + { + pixbeginevent("killstreak"); + + if ( !isDefined( eInflictor ) || !isDefined( eInflictor.requiredDeathCount ) || attacker.deathCount == eInflictor.requiredDeathCount ) + { + shouldGiveKillstreak = maps\mp\gametypes\_hardpoints::shouldGiveKillstreak( sWeapon ); + attacker thread maps\mp\_properks::earnedAKill(); + + if ( shouldGiveKillstreak ) + { + attacker maps\mp\gametypes\_hardpoints::addToKillstreakCount(sWeapon); + } + + //Kills gotten through killstreak weapons should not the players killstreak + if ( isDefined( level.killstreaks ) && shouldGiveKillstreak ) + { + attacker.pers["cur_kill_streak"]++; + attacker thread maps\mp\_properks::checkKillCount(); + attacker thread maps\mp\gametypes\_hardpoints::giveKillstreakForStreak(); + } + } + + if( isPlayer( attacker ) ) + self thread maps\mp\gametypes\_battlechatter_mp::onPlayerKillstreak( attacker ); + + pixendevent(); // "killstreak" + } + + + if ( attacker.pers["cur_kill_streak"] > attacker.kill_streak ) + { + attacker setDStat( "HighestStats", "kill_streak", attacker.pers["totalKillstreakCount"] ); + attacker.kill_streak = attacker.pers["cur_kill_streak"]; + } + + + if ( attacker.pers["cur_kill_streak"] > attacker.gametype_kill_streak ) + { + attacker maps\mp\gametypes\_persistence::statSetWithGametype( "kill_streak", attacker.pers["cur_kill_streak"] ); + attacker.gametype_kill_streak = attacker.pers["cur_kill_streak"]; + } + + maps\mp\gametypes\_globallogic_score::givePlayerScore( "kill", attacker, self ); + + attacker thread maps\mp\gametypes\_globallogic_score::trackAttackerKill( self.name, self.pers["rank"], self.pers["rankxp"], self.pers["prestige"], self getXuid(true) ); + + attackerName = attacker.name; + self thread maps\mp\gametypes\_globallogic_score::trackAttackeeDeath( attackerName, attacker.pers["rank"], attacker.pers["rankxp"], attacker.pers["prestige"], attacker getXuid(true) ); + self thread maps\mp\_medals::setLastKilledBy( attacker ); + + attacker thread maps\mp\gametypes\_globallogic_score::incKillstreakTracker( sWeapon ); + + // to prevent spectator gain score for team-spectator after throwing a granade and killing someone before he switched + if ( level.teamBased && attacker.team != "spectator") + { + // dog score for team + if( isai(Attacker) ) + maps\mp\gametypes\_globallogic_score::giveTeamScore( "kill", attacker.aiteam, attacker, self ); + else + maps\mp\gametypes\_globallogic_score::giveTeamScore( "kill", attacker.team, attacker, self ); + } + + scoreSub = maps\mp\gametypes\_tweakables::getTweakableValue( "game", "deathpointloss" ); + if ( scoreSub != 0 ) + { + maps\mp\gametypes\_globallogic_score::_setPlayerScore( self, maps\mp\gametypes\_globallogic_score::_getPlayerScore( self ) - scoreSub ); + } + + level thread playKillBattleChatter( attacker, sWeapon ); + + if ( level.teamBased ) + { + //check if assist points should be awarded + awardAssists = true; + } + } + + pixendevent( "PlayerKilled attacker" ); + } + } + else if ( isDefined( attacker ) && ( attacker.classname == "trigger_hurt" || attacker.classname == "worldspawn" ) ) + { + doKillcam = false; + + lpattacknum = -1; + lpattackguid = ""; + lpattackname = ""; + lpattackteam = "world"; + + self thread [[level.onXPEvent]]( "suicide" ); + self maps\mp\gametypes\_globallogic_score::incPersStat( "suicides", 1 ); + self.suicides = self maps\mp\gametypes\_globallogic_score::getPersStat( "suicides" ); + + //Check for player death related battlechatter + thread maps\mp\gametypes\_battlechatter_mp::onPlayerSuicideOrTeamKill( self, "suicide" ); //Play suicide battlechatter + + //check if assist points should be awarded + awardAssists = true; + + } + else + { + doKillcam = false; + + lpattacknum = -1; + lpattackguid = ""; + lpattackname = ""; + lpattackteam = "world"; + + // we may have a killcam on an world entity like the rocket in cosmodrome + if ( IsDefined( eInflictor ) && IsDefined( eInflictor.killCamEnt ) ) + { + doKillcam = true; + lpattacknum = self getEntityNumber(); + } + + // even if the attacker isn't a player, it might be on a team + if ( isDefined( attacker ) && isDefined( attacker.team ) && (attacker.team == "axis" || attacker.team == "allies") ) + { + if ( attacker.team != self.team ) + { + if ( level.teamBased ) + maps\mp\gametypes\_globallogic_score::giveTeamScore( "kill", attacker.team, attacker, self ); + } + } + //check if assist points should be awarded + awardAssists = true; + + } + + //award assist points if needed + if( awardAssists ) + { + pixbeginevent( "PlayerKilled assists" ); + + if ( isdefined( self.attackers ) ) + { + for ( j = 0; j < self.attackers.size; j++ ) + { + player = self.attackers[j]; + + if ( !isDefined( player ) ) + continue; + + if ( player == attacker ) + continue; + + damage_done = self.attackerDamage[player.clientId].damage; + player thread maps\mp\gametypes\_globallogic_score::processAssist( self, damage_done); + } + } + + pixendevent( "END: PlayerKilled assists" ); + } + + pixbeginevent( "PlayerKilled post constants" ); + + self.lastAttacker = attacker; + self.lastDeathPos = self.origin; + + if ( isDefined( attacker ) && isPlayer( attacker ) && attacker != self && (!level.teambased || attacker.team != self.team) ) + { + self thread maps\mp\gametypes\_missions::playerKilled(eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, sHitLoc, attackerStance ); + } + else + { + + self notify("playerKilledChallengesProcessed"); + } + + if ( isdefined ( self.attackers )) + self.attackers = []; + if( isPlayer( attacker ) ) + { + if( maps\mp\gametypes\_hardpoints::isKillstreakWeapon( sWeapon ) ) + { + killstreak = maps\mp\gametypes\_hardpoints::getKillstreakForWeapon( sWeapon ); + bbPrint( "mpattacks: gametime %d attackerspawnid %d attackerweapon %s attackerx %f attackery %f attackerz %f victimspawnid %d victimx %f victimy %f victimz %f damage %d damagetype %s damagelocation %s death 1 killstreak %s", + gettime(), getplayerspawnid( attacker ), sWeapon, lpattackorigin, getplayerspawnid( self ), self.origin, iDamage, sMeansOfDeath, sHitLoc, killstreak ); + } + else + { + bbPrint( "mpattacks: gametime %d attackerspawnid %d attackerweapon %s attackerx %f attackery %f attackerz %f victimspawnid %d victimx %f victimy %f victimz %f damage %d damagetype %s damagelocation %s death 1", + gettime(), getplayerspawnid( attacker ), sWeapon, lpattackorigin, getplayerspawnid( self ), self.origin, iDamage, sMeansOfDeath, sHitLoc ); + } + } + else + { + bbPrint( "mpattacks: gametime %d attackerweapon %s victimspawnid %d victimx %f victimy %f victimz %f damage %d damagetype %s damagelocation %s death 1", + gettime(), sWeapon, getplayerspawnid( self ), self.origin, iDamage, sMeansOfDeath, sHitLoc ); + } + + logPrint( "K;" + lpselfguid + ";" + lpselfnum + ";" + lpselfteam + ";" + lpselfname + ";" + lpattackguid + ";" + lpattacknum + ";" + lpattackteam + ";" + lpattackname + ";" + sWeapon + ";" + iDamage + ";" + sMeansOfDeath + ";" + sHitLoc + "\n" ); + attackerString = "none"; + if ( isPlayer( attacker ) ) // attacker can be the worldspawn if it's not a player + attackerString = attacker getXuid() + "(" + lpattackname + ")"; + self logstring( "d " + sMeansOfDeath + "(" + sWeapon + ") a:" + attackerString + " d:" + iDamage + " l:" + sHitLoc + " @ " + int( self.origin[0] ) + " " + int( self.origin[1] ) + " " + int( self.origin[2] ) ); + + level thread maps\mp\gametypes\_globallogic::updateTeamStatus(); + + killcamentity = self getKillcamEntity( attacker, eInflictor, sWeapon ); + killcamentityindex = -1; + killcamentitystarttime = 0; + + if ( isDefined( killcamentity ) ) + { + killcamentityindex = killcamentity getEntityNumber(); // must do this before any waiting lest the entity be deleted + if ( isdefined( killcamentity.startTime ) ) + { + killcamentitystarttime = killcamentity.startTime; + } + else + { + killcamentitystarttime = killcamentity.birthtime; + } + if ( !isdefined( killcamentitystarttime ) ) + killcamentitystarttime = 0; + } + + if ( self IsRemoteControlling() ) + doKillcam = false; + + self maps\mp\gametypes\_weapons::detachCarryObjectModel(); + + died_in_vehicle= false; + if (IsDefined(self.diedOnVehicle)) + { + died_in_vehicle = self.diedOnVehicle; // only works when vehicle blows up + } + pixendevent( "END: PlayerKilled post constants" ); + + pixbeginevent( "PlayerKilled body and gibbing" ); + if ( !died_in_vehicle ) + { + vAttackerOrigin = undefined; + if ( isdefined( attacker ) ) + vAttackerOrigin = attacker.origin; + + ragdoll_now = false; + if( IsDefined(self.usingvehicle) && self.usingvehicle && IsDefined(self.vehicleposition) && self.vehicleposition == 1 ) + ragdoll_now = true; + + if ( sMeansOfDeath == "MOD_FALLING" ) + { + if ( IsDefined( eInflictor ) && IsDefined( eInflictor.script_noteworthy ) && eInflictor.script_noteworthy == "ragdoll_now" ) + { + ragdoll_now = true; + self thread maps\mp\_challenges::fellOffTheMap(); + } + } + + body = self clonePlayer( deathAnimDuration ); + self createDeadBody( iDamage, sMeansOfDeath, sWeapon, sHitLoc, vDir, vAttackerOrigin, deathAnimDuration, eInflictor, ragdoll_now, body ); + } + pixendevent( "END: PlayerKilled body and gibbing" ); + + self.switching_teams = undefined; + self.joining_team = undefined; + self.leaving_team = undefined; + + self thread [[level.onPlayerKilled]](eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration); + + for ( iCB = 0; iCB < level.onPlayerKilledExtraUnthreadedCBs.size; iCB++ ) + { + self [[ level.onPlayerKilledExtraUnthreadedCBs[ iCB ] ]]( + eInflictor, + attacker, + iDamage, + sMeansOfDeath, + sWeapon, + vDir, + sHitLoc, + psOffsetTime, + deathAnimDuration ); + } + + self.wantSafeSpawn = false; + perks = maps\mp\gametypes\_globallogic::getPerks( attacker ); + killstreaks = maps\mp\gametypes\_globallogic::getKillstreaks( attacker ); + + // let the player watch themselves die + wait ( 0.25 ); + + //check if killed by a sniper + weaponClass = maps\mp\gametypes\_missions::getWeaponClass( sWeapon ); + if ( weaponClass == "weapon_sniper" ) + { + self thread maps\mp\gametypes\_battlechatter_mp::KilledBySniper( attacker ); + } + else + { + self thread maps\mp\gametypes\_battlechatter_mp::PlayerKilled( attacker ); + } + self.cancelKillcam = false; + self thread maps\mp\gametypes\_killcam::cancelKillCamOnUse(); + maps\mp\gametypes\_globallogic_utils::waitForTimeOrNotifies( 1.75 ); + self notify ( "death_delay_finished" ); + +/# + if ( GetDvarInt( #"scr_forcekillcam" ) != 0 ) + { + doKillcam = true; + + if ( lpattacknum < 0 ) + lpattacknum = self getEntityNumber(); + } +#/ + + if ( game["state"] != "playing" ) + { + // if no longer playing then this was probably the kill that ended the round + // store off the killcam info + level thread maps\mp\gametypes\_killcam::startFinalKillcam( lpattacknum, self getEntityNumber(), killcamentity, killcamentityindex, killcamentitystarttime, sWeapon, self.deathTime, deathTimeOffset, psOffsetTime, perks, killstreaks, attacker ); + return; + } + + respawnTimerStartTime = gettime(); + + if ( !self.cancelKillcam && doKillcam && level.killcam ) + { + livesLeft = !(level.numLives && !self.pers["lives"]); + timeUntilSpawn = maps\mp\gametypes\_globallogic_spawn::TimeUntilSpawn( true ); + willRespawnImmediately = livesLeft && (timeUntilSpawn <= 0); + + self thread maps\mp\_tutorial::tutorial_display_tip(); + self maps\mp\gametypes\_killcam::killcam( lpattacknum, self getEntityNumber(), killcamentity, killcamentityindex, killcamentitystarttime, sWeapon, self.deathTime, deathTimeOffset, psOffsetTime, willRespawnImmediately, maps\mp\gametypes\_globallogic_utils::timeUntilRoundEnd(), perks, killstreaks, attacker ); + } + + if ( game["state"] != "playing" ) + { + self.sessionstate = "dead"; + self.spectatorclient = -1; + self.killcamtargetentity = -1; + self.killcamentity = -1; + self.archivetime = 0; + self.psoffsettime = 0; + return; + } + + WaitTillKillStreakDone(); + + // class may be undefined if we have changed teams + if ( maps\mp\gametypes\_globallogic_utils::isValidClass( self.class ) ) + { + timePassed = (gettime() - respawnTimerStartTime) / 1000; + self thread [[level.spawnClient]]( timePassed ); + } +} + +updateGlobalBotKilledCounter() +{ + self endon("disconnect"); + wait( .05 ); + maps\mp\gametypes\_globallogic_utils::WaitTillSlowProcessAllowed(); + + if ( isDefined( self.pers["isBot"] ) ) + { + level.globalLarrysKilled++; + } +} + + +WaitTillKillStreakDone() +{ + if( isdefined( self.killstreak_waitamount ) ) + { + starttime = gettime(); + waitTime = self.killstreak_waitamount * 1000; + + while( (gettime() < (starttime+waitTime)) && isdefined( self.killstreak_waitamount ) ) + { + wait( 0.1 ); + } + + //Plus a small amount so we can see our dead body + wait( 2.0 ); + + self.killstreak_waitamount = undefined; + } +} + +TeamKillKick() +{ + self maps\mp\gametypes\_globallogic_score::incPersStat( "sessionbans", 1 ); + + self endon("disconnect"); + waittillframeend; + + //for test purposes lets lock them out of certain game type for 2mins + + playlistbanquantum = maps\mp\gametypes\_tweakables::getTweakableValue( "team", "teamkillerplaylistbanquantum" ); + playlistbanpenalty = maps\mp\gametypes\_tweakables::getTweakableValue( "team", "teamkillerplaylistbanpenalty" ); + if ( playlistbanquantum > 0 && playlistbanpenalty > 0 ) + { + timeplayedtotal = self maps\mp\gametypes\_persistence::statGet( "time_played_total" ); + minutesplayed = timeplayedtotal / 60; + + freebees = 2; + + banallowance = int( floor(minutesplayed / playlistbanquantum) ) + freebees; + + if ( self.sessionbans > banallowance ) + { + self maps\mp\gametypes\_persistence::statSet( "gametypeban", timeplayedtotal + (playlistbanpenalty * 60), false ); + } + } + + // no waiting because then they could quit and rejoin before the ban +// self setLowerMessage( &"MP_FRIENDLY_FIRE_WILL_NOT", 2 ); + + if ( self is_bot() ) + { + level notify( "bot_kicked", self.team ); + } + + ban( self getentitynumber(), 1 ); + maps\mp\gametypes\_globallogic_audio::leaderDialog( "kicked" ); +} + +TeamKillDelay() +{ + teamkills = self.pers["teamkills_nostats"]; + if ( level.minimumAllowedTeamKills < 0 || teamkills <= level.minimumAllowedTeamKills ) + return 0; + + exceeded = (teamkills - level.minimumAllowedTeamKills); + return maps\mp\gametypes\_tweakables::getTweakableValue( "team", "teamkillspawndelay" ) * exceeded; +} + + +ShouldTeamKillKick(teamKillDelay) +{ + if ( teamKillDelay && maps\mp\gametypes\_tweakables::getTweakableValue( "team", "kickteamkillers" ) ) + { + // if its more then 5 seconds into the match and we have a delay then just kick them + if ( maps\mp\gametypes\_globallogic_utils::getTimePassed() >= 5000 ) + { + return true; + } + + // if its under 5 seconds into the match only kick them if they have killed more then one players so far + if ( self.pers["teamkills_nostats"] > 1 ) + { + return true; + } + } + + return false; +} + +reduceTeamKillsOverTime() +{ + timePerOneTeamkillReduction = 20.0; + reductionPerSecond = 1.0 / timePerOneTeamkillReduction; + + while(1) + { + if ( isAlive( self ) ) + { + self.pers["teamkills_nostats"] -= reductionPerSecond; + if ( self.pers["teamkills_nostats"] < level.minimumAllowedTeamKills ) + { + self.pers["teamkills_nostats"] = level.minimumAllowedTeamKills; + break; + } + } + wait 1; + } +} + + +IgnoreTeamKills( sWeapon, sMeansOfDeath ) +{ + if ( sMeansOfDeath == "MOD_MELEE" ) + return false; + + if ( sWeapon == "briefcase_bomb_mp" ) + return true; + + if ( sWeapon == "supplydrop_mp" ) + return true; + +// if ( isSubStr( sWeapon, "mine_bouncing_betty_" ) ) +// return true; + + return false; +} + + +Callback_PlayerLastStand( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration ) +{ + maps\mp\_laststand::playerlaststand(eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration ); +} + + +damageShellshockAndRumble( eAttacker, eInflictor, sWeapon, sMeansOfDeath, iDamage ) +{ + self thread maps\mp\gametypes\_weapons::onWeaponDamage( eAttacker, eInflictor, sWeapon, sMeansOfDeath, iDamage ); + self PlayRumbleOnEntity( "damage_heavy" ); +} + + +createDeadBody( iDamage, sMeansOfDeath, sWeapon, sHitLoc, vDir, vAttackerOrigin, deathAnimDuration, eInflictor, ragdoll_jib, body ) +{ + if ( sMeansOfDeath == "MOD_HIT_BY_OBJECT" && self GetStance() == "prone" ) + { + self.body = body; + if ( !isDefined( self.switching_teams ) ) + thread maps\mp\gametypes\_deathicons::addDeathicon( body, self, self.team, 5.0 ); + + return; + } + + if ( IsDefined( level.ragdoll_override ) && self [[level.ragdoll_override]]() ) + { + return; + } + + if ( ragdoll_jib || self isOnLadder() || self isMantling() || sMeansOfDeath == "MOD_CRUSH" || sMeansOfDeath == "MOD_HIT_BY_OBJECT" ) + body startRagDoll(); + + if ( !self IsOnGround() ) + { + if ( GetDvarInt( #"scr_disable_air_death_ragdoll" ) == 0 ) + { + body startRagDoll(); + } + } + + if ( self is_explosive_ragdoll( sWeapon, eInflictor ) ) + { + body start_explosive_ragdoll( vDir, sWeapon ); + } + + thread delayStartRagdoll( body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath ); + + if( sMeansOfDeath == "MOD_BURNED" || isdefined( self.burning ) ) + { + body maps\mp\_burnplayer::burnedToDeath(); + } + if ( sMeansOfDeath == "MOD_CRUSH" ) + { + body maps\mp\gametypes\_globallogic_vehicle::vehicleCrush(); + } + + self.body = body; + if ( !isDefined( self.switching_teams ) ) + thread maps\mp\gametypes\_deathicons::addDeathicon( body, self, self.team, 5.0 ); +} + +is_explosive_ragdoll( weapon, inflictor ) +{ + if ( !IsDefined( weapon ) ) + { + return false; + } + + // destructible explosives + if ( weapon == "destructible_car_mp" || weapon == "explodable_barrel_mp" ) + { + return true; + } + + // special explosive weapons + if ( weapon == "sticky_grenade_mp" || weapon == "explosive_bolt_mp" ) + { + if ( IsDefined( inflictor ) && IsDefined( inflictor.stuckToPlayer ) ) + { + if ( inflictor.stuckToPlayer == self ) + { + return true; + } + } + } + + return false; +} + +start_explosive_ragdoll( dir, weapon ) +{ + if ( !IsDefined( self ) ) + { + return; + } + + x = RandomIntRange( 50, 100 ); + y = RandomIntRange( 50, 100 ); + z = RandomIntRange( 10, 20 ); + + if ( IsDefined( weapon ) && ( weapon == "sticky_grenade_mp" || weapon == "explosive_bolt_mp" ) ) + { + if ( IsDefined( dir ) && LengthSquared( dir ) > 0 ) + { + x = dir[0] * x; + y = dir[1] * y; + } + } + else + { + if ( cointoss() ) + { + x = x * -1; + } + if ( cointoss() ) + { + y = y * -1; + } + } + + self StartRagdoll(); + self LaunchRagdoll( ( x, y, z ) ); +} + + +notifyConnecting() +{ + waittillframeend; + + if( isDefined( self ) ) + level notify( "connecting", self ); +} + + +delayStartRagdoll( ent, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath ) +{ + if ( isDefined( ent ) ) + { + deathAnim = ent getcorpseanim(); + if ( animhasnotetrack( deathAnim, "ignore_ragdoll" ) ) + return; + } + + if ( level.oldschool ) + { + if ( !isDefined( vDir ) ) + vDir = (0,0,0); + + explosionPos = ent.origin + ( 0, 0, maps\mp\gametypes\_globallogic_utils::getHitLocHeight( sHitLoc ) ); + explosionPos -= vDir * 20; + //thread maps\mp\gametypes\_globallogic_utils::debugLine( ent.origin + (0,0,(explosionPos[2] - ent.origin[2])), explosionPos ); + explosionRadius = 40; + explosionForce = .75; + if ( sMeansOfDeath == "MOD_IMPACT" || sMeansOfDeath == "MOD_EXPLOSIVE" || isSubStr(sMeansOfDeath, "MOD_GRENADE") || isSubStr(sMeansOfDeath, "MOD_PROJECTILE") || sHitLoc == "head" || sHitLoc == "helmet" ) + { + explosionForce = 2.5; + } + + ent startragdoll( 1 ); + + wait .05; + + if ( !isDefined( ent ) ) + return; + + // apply extra physics force to make the ragdoll go crazy + physicsExplosionSphere( explosionPos, explosionRadius, explosionRadius/2, explosionForce ); + return; + } + + wait( 0.2 ); + + if ( !isDefined( ent ) ) + return; + + if ( ent isRagDoll() ) + return; + + deathAnim = ent getcorpseanim(); + + startFrac = 0.35; + + if ( animhasnotetrack( deathAnim, "start_ragdoll" ) ) + { + times = getnotetracktimes( deathAnim, "start_ragdoll" ); + if ( isDefined( times ) ) + startFrac = times[0]; + } + + waitTime = startFrac * getanimlength( deathAnim ); + wait( waitTime ); + + if ( isDefined( ent ) ) + { + println( "Ragdolling after " + waitTime + " seconds" ); + ent startragdoll( 1 ); + } +} + +trackAttackerDamage( eAttacker, iDamage, sMeansOfDeath, sWeapon ) +{ + Assert( isPlayer( eAttacker ) ); + + if ( !isdefined( self.attackerData[eAttacker.clientid] ) ) + { + self.attackerDamage[eAttacker.clientid] = spawnstruct(); + self.attackerDamage[eAttacker.clientid].damage = iDamage; + self.attackerDamage[eAttacker.clientid].meansOfDeath = sMeansOfDeath; + self.attackerDamage[eAttacker.clientid].weapon = sWeapon; + self.attackerDamage[eAttacker.clientid].time = getTime(); + self.attackers[ self.attackers.size ] = eAttacker; + // we keep an array of attackers by their client ID so we can easily tell + // if they're already one of the existing attackers in the above if(). + // we store in this array data that is useful for other things, like challenges + self.attackerData[eAttacker.clientid] = false; + } + else + { + self.attackerDamage[eAttacker.clientid].damage += iDamage; + self.attackerDamage[eAttacker.clientid].meansOfDeath = sMeansOfDeath; + self.attackerDamage[eAttacker.clientid].weapon = sWeapon; + if ( !isdefined( self.attackerDamage[eAttacker.clientid].time ) ) + self.attackerDamage[eAttacker.clientid].time = getTime(); + } + + self.attackerDamage[eAttacker.clientid].lasttimedamaged = getTime(); + if ( maps\mp\gametypes\_weapons::isPrimaryWeapon( sWeapon ) ) + self.attackerData[eAttacker.clientid] = true; +} + +giveInflictorOwnerAssist( eAttacker, eInflictor, iDamage, sMeansOfDeath, sWeapon ) +{ + if ( !isDefined( eInflictor ) ) + return; + + if ( !isDefined( eInflictor.owner ) ) + return; + + if ( !IsDefined( eInflictor.ownerGetsAssist ) ) + return; + + if ( !eInflictor.ownerGetsAssist ) + return; + + Assert( isPlayer( eInflictor.owner ) ); + + trackAttackerDamage( eInflictor.owner, iDamage, sMeansOfDeath, sWeapon ); +} + +updateMeansOfDeath( sWeapon, sMeansOfDeath ) +{ + // we do not want the melee icon to show up for dog attacks + // AE 10-22-09: added the check for the crossbow so that it'll show the right icon + switch(sWeapon) + { + case "crossbow_mp": + case "knife_ballistic_mp": + { + if ( ( sMeansOfDeath != "MOD_HEAD_SHOT" ) && ( sMeansOfDeath != "MOD_MELEE" ) ) + { + sMeansOfDeath = "MOD_PISTOL_BULLET"; + } + } + break; + case "dog_bite_mp": + sMeansOfDeath = "MOD_PISTOL_BULLET"; + break; + case "destructible_car_mp": + sMeansOfDeath = "MOD_EXPLOSIVE"; + break; + case "explodable_barrel_mp": + sMeansOfDeath = "MOD_EXPLOSIVE"; + break; + } + + return sMeansOfDeath; +} + +updateAttacker( attacker ) +{ + if( isai(attacker) && isDefined( attacker.script_owner ) ) + { + // if the person who called the dogs in switched teams make sure they don't + // get penalized for the kill + if ( !level.teambased || attacker.script_owner.team != self.team ) + attacker = attacker.script_owner; + } + + if( attacker.classname == "script_vehicle" && isDefined( attacker.owner ) ) + { + attacker notify("killed",self); + + attacker = attacker.owner; + } + + if( isai(attacker) ) + attacker notify("killed",self); + + if ( ( isdefined ( self.capturingLastFlag ) ) && ( self.capturingLastFlag == true ) ) + { + attacker.lastCapKiller = true; + } + + return attacker; +} + +updateInflictor( eInflictor ) +{ + if( IsDefined( eInflictor ) && eInflictor.classname == "script_vehicle" ) + { + eInflictor notify("killed",self); + } + + return eInflictor; +} + +updateWeapon( eInflictor, sWeapon ) +{ + // explosive barrel/car detection + if ( sWeapon == "none" && isDefined( eInflictor ) ) + { + if ( isDefined( eInflictor.targetname ) && eInflictor.targetname == "explodable_barrel" ) + sWeapon = "explodable_barrel_mp"; + else if ( isDefined( eInflictor.destructible_type ) && isSubStr( eInflictor.destructible_type, "vehicle_" ) ) + sWeapon = "destructible_car_mp"; + } + + return sWeapon; +} + +getClosestKillcamEntity( attacker, killCamEntities ) +{ + closestKillcamEnt = undefined; + closestKillcamEntDist = undefined; + origin = undefined; + + for ( killcamEntIndex = 0; killcamEntIndex < killCamEntities.size; killcamEntIndex++ ) + { + killcamEnt = killCamEntities[killcamEntIndex]; + if ( killcamEnt == attacker ) + continue; + + origin = killcamEnt.origin; + if ( IsDefined( killcamEnt.offsetPoint ) ) + origin += killcamEnt.offsetPoint; + + dist = DistanceSquared( self.origin, origin ); + + if ( !IsDefined( closestKillcamEnt ) || dist < closestKillcamEntDist ) + { + closestKillcamEnt = killcamEnt; + closestKillcamEntDist = dist; + } + } + + return closestKillcamEnt; +} + +getKillcamEntity( attacker, eInflictor, sWeapon ) +{ + if ( !isDefined( eInflictor ) ) + return undefined; + + if ( eInflictor == attacker ) + { + if( !IsDefined( eInflictor.isMagicBullet ) ) + return undefined; + if( IsDefined( eInflictor.isMagicBullet ) && !eInflictor.isMagicBullet ) + return undefined; + } + else if ( isdefined( level.levelSpecificKillcam ) ) + { + levelSpecificKillcamEnt = self [[level.levelSpecificKillcam]](); + if ( isdefined( levelSpecificKillcamEnt ) ) + return levelSpecificKillcamEnt; + } + + if ( sWeapon == "m220_tow_mp" ) + return undefined; + + if ( isDefined(eInflictor.killCamEnt) ) + { + // this is the case with the player helis + if ( eInflictor.killCamEnt == attacker ) + return undefined; + + return eInflictor.killCamEnt; + } + else if ( isDefined(eInflictor.killCamEntities) ) + { + return getClosestKillcamEntity( attacker, eInflictor.killCamEntities ); + } + + if ( isDefined( eInflictor.script_gameobjectname ) && eInflictor.script_gameobjectname == "bombzone" ) + return eInflictor.killCamEnt; + + //if ( eInflictor.classname == "script_origin" || eInflictor.classname == "script_model" || eInflictor.classname == "script_brushmodel" ) + // return undefined; // probably a barrel or a car... code does airstrike cam for these things which looks bad + + return eInflictor; +} + +playKillBattleChatter( attacker, sWeapon ) +{ + if( IsPlayer( attacker ) ) + { + if( isDefined(level.bcKillInformProbability) && randomIntRange( 0, 100 ) >= level.bcKillInformProbability ) + { + if ( !maps\mp\gametypes\_hardpoints::isKillstreakWeapon( sWeapon ) ) + { + level thread maps\mp\gametypes\_battlechatter_mp::sayLocalSoundDelayed( attacker, "kill", "infantry", 0.75 ); + } + } + } +} + \ No newline at end of file diff --git a/main_shared/maps/mp/gametypes/_hardpoints.gsc b/main_shared/maps/mp/gametypes/_hardpoints.gsc new file mode 100644 index 0000000..23f428e --- /dev/null +++ b/main_shared/maps/mp/gametypes/_hardpoints.gsc @@ -0,0 +1,1141 @@ +#include maps\mp\_utility; +#include maps\mp\gametypes\_hud_util; +#include common_scripts\utility; +init() +{ + PreCacheString( &"MP_KILLSTREAK_N" ); + if ( GetDvar( #"scr_allow_killstreak_building") == "" ) + { + SetDvar( "scr_allow_killstreak_building", "0" ); + } + + level.killstreaks = []; + level.killstreakWeapons = []; + level.menuReferenceForKillStreak = []; + level.numKillstreakReservedObjectives = 0; + level.killstreakCounter = 0; + if( !isDefined(level.killstreakRoundDelay) ) + level.killstreakRoundDelay = 0; + maps\mp\_airsupport::initAirsupport(); + maps\mp\_helicopter::init(); + maps\mp\_airstrike::init(); + maps\mp\_napalm::init(); + maps\mp\_artillery::init(); + maps\mp\_mortar::init(); + maps\mp\_radar::init(); + maps\mp\_rcbomb::init(); + maps\mp\_helicopter_player::init(); + maps\mp\_spyplane::init(); + maps\mp\_dogs::initKillstreak(); + maps\mp\gametypes\_supplydrop::init(); + maps\mp\_killstreakrules::init(); + maps\mp\_turret_killstreak::init(); + maps\mp\gametypes\_killstreak_weapons::init(); + level thread onPlayerConnect(); +} +registerKillstreak(killstreakType, + killstreakWeapon, + killstreakMenuName, + killstreakUsageKey, + killstreakUseFunction, + killstreakDelayStreak, + weaponHoldAllowed, + killstreakStatsName + ) +{ + AssertEx( IsDefined(killstreakType), "Can not register a killstreak without a valid type name."); + AssertEx( !IsDefined(level.killstreaks[killstreakType]), "Killstreak " + killstreakType + " already registered"); + AssertEx( IsDefined(killstreakUseFunction), "No use function defined for killstreak " + killstreakType); + + level.killstreaks[killstreakType] = SpawnStruct(); + + + level.killstreaks[killstreakType].killstreakLevel = int( tablelookup( "mp/statstable.csv", level.cac_creference, killstreakMenuName, level.cac_ccount ) ); + level.killstreaks[killstreakType].usageKey = killstreakUsageKey; + level.killstreaks[killstreakType].useFunction = killstreakUseFunction; + level.killstreaks[killstreakType].menuName = killstreakMenuName; + level.killstreaks[killstreakType].delayStreak = killstreakDelayStreak; + level.killstreaks[killstreakType].allowAssists = false; + + if ( IsDefined( killstreakWeapon ) ) + { + AssertEx( !IsDefined(level.killstreakWeapons[killstreakWeapon]), "Can not have a weapon associated with multiple killstreaks."); + precacheItem( killstreakWeapon ); + level.killstreaks[killstreakType].weapon = killstreakWeapon; + level.killstreakWeapons[killstreakWeapon] = killstreakType; + } + if ( !IsDefined( weaponHoldAllowed ) ) + { + weaponHoldAllowed = false; + } + if( isDefined( killstreakStatsName ) ) + { + level.killstreaks[killstreakType].killstreakStatsName = killstreakStatsName; + } + level.killstreaks[killstreakType].weaponHoldAllowed = weaponHoldAllowed; + level.menuReferenceForKillStreak[killstreakMenuName] = killstreakType; +} +registerKillstreakStrings( killstreakType, receivedText, notUsableText, inboundText, inboundNearPlayerText ) +{ + AssertEx( IsDefined(killstreakType), "Can not register a killstreak without a valid type name."); + AssertEx( IsDefined(level.killstreaks[killstreakType]), "Killstreak needs to be registered before calling registerKillstreakStrings."); + + level.killstreaks[killstreakType].receivedText = receivedText; + level.killstreaks[killstreakType].notAvailableText = notUsableText; + level.killstreaks[killstreakType].inboundText = inboundText; + level.killstreaks[killstreakType].inboundNearPlayerText = inboundNearPlayerText; + + if( IsDefined(level.killstreaks[killstreakType].receivedText) ) + precacheString( level.killstreaks[killstreakType].receivedText ); + if( IsDefined(level.killstreaks[killstreakType].notAvailableText) ) + precacheString( level.killstreaks[killstreakType].notAvailableText ); + if( IsDefined(level.killstreaks[killstreakType].inboundText) ) + precacheString( level.killstreaks[killstreakType].inboundText ); + if( IsDefined(level.killstreaks[killstreakType].inboundNearPlayerText) ) + precacheString( level.killstreaks[killstreakType].inboundNearPlayerText ); +} +registerKillstreakDialog( killstreakType, + receivedDialog, + friendlyStartDialog, + friendlyEndDialog, + enemyStartDialog, + enemyEndDialog, + dialog + ) +{ + AssertEx( IsDefined(killstreakType), "Can not register a killstreak without a valid type name."); + AssertEx( IsDefined(level.killstreaks[killstreakType]), "Killstreak needs to be registered before calling registerKillstreakDialog."); + level.killstreaks[killstreakType].informDialog = receivedDialog; + + game["dialog"][killstreakType + "_start"] = friendlyStartDialog; + game["dialog"][killstreakType + "_end"] = friendlyEndDialog; + game["dialog"][killstreakType + "_enemy_start"] = enemyStartDialog; + game["dialog"][killstreakType + "_enemy_end"] = enemyEndDialog; + game["dialog"][killstreakType] = dialog; +} +registerKillstreakAltWeapon( killstreakType, weapon ) +{ + AssertEx( IsDefined(killstreakType), "Can not register a killstreak without a valid type name."); + AssertEx( IsDefined(level.killstreaks[killstreakType]), "Killstreak needs to be registered before calling registerKillstreakAltWeapon."); + if ( level.killstreaks[killstreakType].weapon == weapon ) + return; + + + if ( !IsDefined( level.killstreaks[killstreakType].altWeapons ) ) + { + level.killstreaks[killstreakType].altWeapons = []; + } + if( !IsDefined( level.killstreakWeapons[weapon] ) ) + { + level.killstreakWeapons[weapon] = killstreakType; + } + level.killstreaks[killstreakType].altWeapons[level.killstreaks[killstreakType].altWeapons.size] = weapon; +} +registerKillstreakDevDvar(killstreakType, dvar) +{ + AssertEx( IsDefined(killstreakType), "Can not register a killstreak without a valid type name."); + AssertEx( IsDefined(level.killstreaks[killstreakType]), "Killstreak needs to be registered before calling registerKillstreakDevDvar."); + level.killstreaks[killstreakType].devDvar = dvar; +} +allowKillstreakAssists( killstreakType, allow ) +{ + level.killstreaks[killstreakType].allowAssists = allow; +} +isKillstreakAvailable( killstreak ) +{ + if ( isDefined( level.menuReferenceForKillStreak[killstreak] ) ) + return true; + else + return false; +} +getKillstreakByMenuName( killstreak ) +{ + return level.menuReferenceForKillStreak[killstreak]; +} +getKillStreakMenuName( killstreakType ) +{ + Assert( IsDefined(level.killstreaks[killstreakType] ) ); + return level.killstreaks[killstreakType].menuName; +} +drawLine( start, end, timeSlice, color ) +{ + drawTime = int(timeSlice * 20); + for( time = 0; time < drawTime; time++ ) + { + line( start, end, (1,0,0),false, 1 ); + wait ( 0.05 ); + } +} +getKillstreakLevel( index, killstreak ) +{ + killstreakLevel = level.killstreaks[ getKillstreakByMenuName( killstreak ) ].killstreakLevel; + if( getDvarInt( "custom_killstreak_mode" ) == 2 ) + { + if( killstreak == self.killstreak[ 0 ] && index == 0 ) + { + killstreakLevel = getDvarInt( "custom_killstreak_1_kills" ); + } + else if( killstreak == self.killstreak[ 1 ] && index == 1 ) + { + killstreakLevel = getDvarInt( "custom_killstreak_2_kills" ); + } + else if( killstreak == self.killstreak[ 2 ] && index == 2 ) + { + killstreakLevel = getDvarInt( "custom_killstreak_3_kills" ); + } + } + return killstreakLevel; +} +giveKillstreakIfStreakCountMatches( index, killstreak, streakCount ) +{ + pixbeginevent( "giveKillstreakIfStreakCountMatches" ); + + + if( self.pers["killstreaksEarnedThisKillstreak"] > index && isRoundBased() ) + hasAlreadyEarnedKillstreak = true; + else + hasAlreadyEarnedKillstreak = false; + if ( IsDefined( killstreak ) && isKillstreakAvailable(killstreak) && !hasAlreadyEarnedKillstreak ) + { + killstreakLevel = GetKillstreakLevel( index, killstreak ); + if ( self HasPerk( "specialty_killstreak" ) ) + { + reduction = GetDvarInt( #"perk_killstreakReduction" ); + killstreakLevel -= reduction; + + if( killstreakLevel <= 0 ) + { + killstreakLevel = 1; + } + } + + if ( killstreakLevel == streakCount ) + { + + + + + self thread maps\mp\_properks::earnedAKillstreak(); + self thread maps\mp\_challenges::earnedKillstreak( killstreak ); + self giveKillstreak( getKillstreakByMenuName( killstreak ), streakCount ); + self.pers["killstreaksEarnedThisKillstreak"] = index + 1; + pixendevent(); + return true; + } + } + pixendevent(); + return false; +} +giveKillstreakForStreak() +{ + self notify("new_killstreak_count"); + + if ( !isKillStreaksEnabled() ) + { + return; + } + + + + + if( !IsDefined(self.pers["totalKillstreakCount"]) ) + { + self.pers["totalKillstreakCount"] = 0; + } + + + given = false; + + for ( i = 0; i < self.killstreak.size && i < 3; i++ ) + { + given |= giveKillstreakIfStreakCountMatches( i, self.killstreak[i], self.pers["cur_kill_streak"] ); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + +} +isOneAwayFromKillstreak() +{ + if( !IsDefined( self.pers["kill_streak_before_death"] ) ) + { + self.pers["kill_streak_before_death"] = 0; + } + + streakPlusOne = self.pers["kill_streak_before_death"] + 1; + + + + oneAway = doesStreakCountMatches( self.killstreak[0], streakPlusOne ); + oneAway |= doesStreakCountMatches( self.killstreak[1], streakPlusOne ); + oneAway |= doesStreakCountMatches( self.killstreak[2], streakPlusOne ); + return oneAway; +} +doesStreakCountMatches( killstreak, streakCount ) +{ + if ( IsDefined( killstreak ) && isKillstreakAvailable(killstreak) ) + { + killstreak = getKillstreakByMenuName( killstreak ); + killstreakLevel = level.killstreaks[killstreak].killstreakLevel; + if ( self HasPerk( "specialty_killstreak" ) ) + { + reduction = GetDvarInt( #"perk_killstreakReduction" ); + killstreakLevel -= reduction; + } + + if ( killstreakLevel == streakCount ) + { + return true; + } + } + + return false; +} +streakNotify( streakVal ) +{ + self endon("disconnect"); + + self waittill( "playerKilledChallengesProcessed" ); + wait .05; + + notifyData = spawnStruct(); + notifyData.titleLabel = &"MP_KILLSTREAK_N"; + notifyData.titleText = streakVal; + notifyData.iconHeight = 32; + self maps\mp\gametypes\_hud_message::notifyMessage( notifyData ); + + +} +giveKillstreak( killstreakType, streak, suppressNotification, noXP ) +{ + pixbeginevent( "giveKillstreak" ); + self endon("disconnect"); + level endon( "game_ended" ); + + had_to_delay = false; + + killstreakGiven = false; + if( isDefined( noXP ) ) + { + if ( self giveKillstreakInternal( killstreakType, undefined, noXP ) ) + { + killstreakGiven = true; + self addKillstreakToQueue( level.killstreaks[killstreakType].menuname, streak, killstreakType, noXP ); + } + } + else if ( self giveKillstreakInternal( killstreakType, noXP ) ) + { + killstreakGiven = true; + self addKillstreakToQueue( level.killstreaks[killstreakType].menuname, streak, killstreakType, noXP ); + } + if( killstreakGiven ) + { + self maps\mp\gametypes\_gametype_variants::onPlayerKillstreakEarned(); + } + pixendevent(); +} +giveKillstreakInternal( killstreakType, do_not_update_death_count, noXP ) +{ + if ( level.gameEnded ) + return false; + + if ( !isKillStreaksEnabled() ) + return false; + + if ( !isDefined( level.killstreaks[killstreakType] ) ) + return false; + if ( !IsDefined( self.pers["killstreaks"] ) ) + { + self.pers["killstreaks"] = []; + } + if( !IsDefined( self.pers["killstreak_has_been_used"] ) ) + { + self.pers["killstreak_has_been_used"] = []; + } + if( !IsDefined( self.pers["killstreak_unique_id"] ) ) + { + self.pers["killstreak_unique_id"] = []; + } + + self.pers["killstreaks"][self.pers["killstreaks"].size] = killstreakType; + self.pers["killstreak_unique_id"][self.pers["killstreak_unique_id"].size] = level.killstreakCounter; + level.killstreakCounter++; + + if( isDefined(noXP) ) + { + self.pers["killstreak_has_been_used"][self.pers["killstreak_has_been_used"].size] = noXP; + } + else + { + self.pers["killstreak_has_been_used"][self.pers["killstreak_has_been_used"].size] = false; + } + + weapon = getKillstreakWeapon( killstreakType ); + + giveKillstreakWeapon( weapon ); + return true; +} +addKillstreakToQueue( menuName, streakCount, hardpointType, noNotify ) +{ + killstreakTableNumber = -1; + for( killstreakTableNumber=level.killStreakBaseValue; killstreakTableNumber<(level.killStreakBaseValue+level.totalkillStreaks + 1); killstreakTableNumber++ ) + { + if ( !isDefined( level.tbl_KillStreakData[ killstreakTableNumber ] ) || + !isDefined( level.tbl_KillStreakData[ killstreakTableNumber ][ "reference" ] ) ) + { + continue; + } + + if ( menuName == level.tbl_KillStreakData[ killstreakTableNumber ][ "reference" ] ) + { + break; + } + } + + assert ( killstreakTableNumber != level.killStreakBaseValue+level.totalkillStreaks + 1); + if ( killstreakTableNumber == level.killStreakBaseValue+level.totalkillStreaks + 1 ) + return; + if( isDefined( noNotify ) && noNotify ) + return; + size = self.killstreakNotifyQueue.size; + self.killstreakNotifyQueue[size] = spawnstruct(); + self.killstreakNotifyQueue[size].streakCount = streakCount; + self.killstreakNotifyQueue[size].killstreakTableNumber = killstreakTableNumber; + self.killstreakNotifyQueue[size].hardpointType = hardpointType; + + self notify( "received award" ); +} +hasKillstreakEquipped( ) +{ + currentWeapon = self getCurrentWeapon(); + keys = getarraykeys( level.killstreaks ); + for ( i = 0; i < keys.size; i++ ) + { + if ( level.killstreaks[keys[i]].weapon == currentWeapon ) + return true; + } + return false; +} +giveKillstreakWeapon( weapon ) +{ + weaponsList = self GetWeaponsList(); + currentWeapon = self GetCurrentWeapon(); + for( idx = 0; idx < weaponsList.size; idx++ ) + { + carriedWeapon = weaponsList[idx]; + + if ( currentWeapon == carriedWeapon ) + continue; + if( currentWeapon == "none" ) + continue; + + switch( carriedWeapon ) + { + case "minigun_mp": + case "m202_flash_mp": + case "m220_tow_mp": + case "mp40_blinged_mp": + continue; + } + + if ( isKillstreakWeapon(carriedWeapon) ) + { + self TakeWeapon( carriedWeapon ); + } + } + + + + if(currentWeapon != weapon && !self hasWeapon(weapon) ) + { + self TakeWeapon( weapon ); + self GiveWeapon( weapon ); + } + self setActionSlot( 4, "weapon", weapon ); +} +activateNextKillstreak( do_not_update_death_count ) +{ + if ( level.gameEnded ) + return false; + + self setActionSlot( 4, "" ); + if ( !IsDefined( self.pers["killstreaks"] ) || self.pers["killstreaks"].size == 0 ) + return false; + + killstreakType = self.pers["killstreaks"][self.pers["killstreaks"].size - 1]; + if ( !isDefined( level.killstreaks[killstreakType] ) ) + return false; + + weapon = level.killstreaks[killstreakType].weapon; + wait( 0.05 ); + + giveKillstreakWeapon( weapon ); + + + if ( !isdefined( do_not_update_death_count ) || do_not_update_death_count != false ) + { + self.pers["killstreakItemDeathCount"+killstreakType] = self.deathCount; + } + + return true; +} +takeKillstreak( killstreakType ) +{ + if ( level.gameEnded ) + return; + + if ( !isKillStreaksEnabled() ) + return false; + + if ( isDefined( self.selectingLocation ) ) + return false; + if ( !isDefined( level.killstreaks[killstreakType] ) ) + return false; + + self takeWeapon( killstreakType ); + self setActionSlot( 4, "" ); + self.pers["killstreakItemDeathCount"+killstreakType] = 0; + + return true; +} +giveOwnedKillstreak() +{ + if ( isDefined( self.pers["killstreaks"] ) && self.pers["killstreaks"].size > 0 ) + self activateNextKillstreak( false ); +} +changeWeaponAfterKillstreak( killstreak ) +{ + self endon( "disconnect" ); + self endon( "death" ); + currentWeapon = self GetCurrentWeapon(); + + if ( level.killstreaks[ killstreak ].weaponHoldAllowed ) + { + return; + } + self waittill( "killstreak_done" ); + + if ( isDefined( self.lastStand ) && self.lastStand && isDefined( self.laststandpistol ) && self hasWeapon( self.laststandpistol ) ) + self switchToWeapon( self.laststandpistol ); + else if( self hasWeapon(self.lastNonKillstreakWeapon) ) + self switchToWeapon( self.lastNonKillstreakWeapon ); + else if( self hasWeapon(self.lastDroppableWeapon) ) + self switchToWeapon( self.lastDroppableWeapon ); +} +removeKillstreakWhenDone( killstreak, hasKillstreakBeenUsed ) +{ + self endon( "disconnect" ); + + self waittill( "killstreak_done", successful, killstreakType ); + if ( successful ) + { + logString( "killstreak: " + getKillStreakMenuName( killstreak ) ); + + if( !isDefined( hasKillstreakBeenUsed ) || !hasKillstreakBeenUsed ) + { + self thread maps\mp\gametypes\_missions::useKillstreak( killstreak ); + } + + killstreak_weapon = getKillstreakWeapon( killstreak ); + + removeUsedKillstreak(killstreak); + self setActionSlot( 4, "" ); + success = true; + } + waittillframeend; + + currentWeapon = self GetCurrentWeapon(); + if( maps\mp\gametypes\_killstreak_weapons::isHeldKillstreakWeapon(killstreakType) && currentWeapon == killstreakType ) + return; + activateNextKillstreak( ); +} +useKillstreak( ) +{ + killstreak = getTopKillstreak(); + hasKillstreakBeenUsed = getIfTopKillstreakHasBeenUsed(); + + if ( isDefined( self.selectingLocation ) ) + return; + self thread changeWeaponAfterKillstreak( killstreak ); + self thread removeKillstreakWhenDone( killstreak, hasKillstreakBeenUsed ); + self thread triggerKillstreak( killstreak ); +} +removeUsedKillstreak( killstreak, killstreakId ) +{ + + + + + killstreakIndex = undefined; + for ( i = self.pers["killstreaks"].size - 1; i >= 0; i-- ) + { + if ( self.pers["killstreaks"][i] == killstreak ) + { + if( isDefined( killstreakId ) && self.pers["killstreak_unique_id"][i] != killstreakId ) + continue; + + killstreakIndex = i; + break; + } + } + + if ( !IsDefined(killstreakIndex) ) + return; + + arraySize = self.pers["killstreaks"].size; + for ( i = killstreakIndex; i < arraySize - 1; i++ ) + { + self.pers["killstreaks"][i] = self.pers["killstreaks"][i + 1]; + self.pers["killstreak_has_been_used"][i] = self.pers["killstreak_has_been_used"][i + 1]; + self.pers["killstreak_unique_id"][i] = self.pers["killstreak_unique_id"][i + 1]; + } + + self.pers["killstreaks"][arraySize-1] = undefined; + self.pers["killstreak_has_been_used"][arraySize-1] = undefined; + self.pers["killstreak_unique_id"][arraySize-1] = undefined; +} +getTopKillstreak() +{ + if ( self.pers["killstreaks"].size == 0 ) + return undefined; + + return self.pers["killstreaks"][self.pers["killstreaks"].size-1]; +} +getIfTopKillstreakHasBeenUsed() +{ + if ( self.pers["killstreak_has_been_used"].size == 0 ) + return undefined; + + return self.pers["killstreak_has_been_used"][self.pers["killstreak_has_been_used"].size-1]; +} +getTopKillstreakUniqueId() +{ + if ( self.pers["killstreak_unique_id"].size == 0 ) + return undefined; + + return self.pers["killstreak_unique_id"][self.pers["killstreak_unique_id"].size-1]; +} +getKillstreakWeapon( killstreak ) +{ + if( !IsDefined( killstreak ) ) + return "none"; + Assert( IsDefined(level.killstreaks[killstreak]) ); + + return level.killstreaks[killstreak].weapon; +} +getKillstreakForWeapon( weapon ) +{ + return level.killstreakWeapons[weapon]; +} +isKillstreakWeapon( weapon ) +{ + if ( isWeaponAssociatedWithKillstreak( weapon ) ) + return true; + switch( weapon ) + { + case "none": + case "briefcase_bomb_defuse_mp": + case "briefcase_bomb_mp": + case "scavenger_item_mp": + case "syrette_mp": + case "tabun_fx_mp": + case "tabun_center_mp": + case "tabun_large_mp": + case "tabun_medium_mp": + case "tabun_small_mp": + case "tabun_tiny_mp": + return false; + } + + specificUse = IsWeaponSpecificUse( weapon ); + if ( isdefined( specificUse ) && specificUse == true ) + return true; + + return false; +} +isKillstreakWeaponAssistAllowed( weapon ) +{ + killstreak = getKillstreakForWeapon( weapon ); + if ( !IsDefined( killstreak ) ) + return false; + + if ( level.killstreaks[killstreak].allowAssists ) + return true; + + return false; +} +trackWeaponUsage() +{ + self endon( "death" ); + self endon( "disconnect" ); + self.lastNonKillstreakWeapon = self GetCurrentWeapon(); + lastValidPimary = self GetCurrentWeapon(); + if ( self.lastNonKillstreakWeapon == "none" ) + { + weapons = self GetWeaponsListPrimaries(); + Assert( weapons.size > 0 ); + if ( weapons.size > 0 ) + self.lastNonKillstreakWeapon = weapons[0]; + } + Assert( self.lastNonKillstreakWeapon != "none" ); + + for ( ;; ) + { + currentWeapon = self GetCurrentWeapon(); + self waittill( "weapon_change", weapon ); + if ( maps\mp\gametypes\_weapons::isPrimaryWeapon( weapon ) ) + lastValidPimary = weapon; + if ( weapon == self.lastNonKillstreakWeapon ) + { + continue; + } + switch( weapon ) + { + case "none": + case "knife_mp": + case "syrette_mp": + continue; + } + name = getKillstreakForWeapon( weapon ); + if ( IsDefined( name ) ) + { + killstreak = level.killstreaks[ name ]; + if ( killstreak.weaponHoldAllowed == true ) + { + self.lastNonKillstreakWeapon = weapon; + } + continue; + } + if( currentWeapon != "none" && IsWeaponEquipment( currentWeapon ) && maps\mp\gametypes\_killstreak_weapons::isHeldKillstreakWeapon( self.lastNonKillstreakWeapon ) ) + { + self.lastNonKillstreakWeapon = lastValidPimary; + continue; + } + if ( IsWeaponEquipment( weapon ) ) + { + continue; + } + self.lastNonKillstreakWeapon = weapon; + } +} +killstreakWaiter() +{ + self endon ( "death" ); + self endon ( "disconnect" ); + level endon ( "game_ended" ); + + self thread trackWeaponUsage(); + + self giveOwnedKillstreak(); + + for ( ;; ) + { + self waittill( "weapon_change", weapon ); + + if( !isKillstreakWeapon( weapon ) ) + continue; + killstreak = getTopKillstreak(); + if( weapon != getKillstreakWeapon(killstreak) ) + continue; + waittillframeend; + if( isDefined( self.usingKillstreakHeldWeapon ) && maps\mp\gametypes\_killstreak_weapons::isHeldKillstreakWeapon(killstreak) ) + continue; + + thread useKillstreak(); + if ( IsDefined( self.selectingLocation ) ) + { + event = self waittill_any_return( "cancel_location", "game_ended", "used", "weapon_change" ); + if ( event == "cancel_location" || event == "weapon_change" ) + { + + wait( 1 ); + } + } + } +} +shouldDelayKillstreak( killstreakType ) +{ + if( !isDefined(level.startTime) ) + return false; + if( level.killstreakRoundDelay < ( ( ( gettime() - level.startTime ) - level.discardTime ) / 1000 ) ) + return false; + if( !isDelayableKillstreak(killstreakType) ) + return false; + if( maps\mp\gametypes\_killstreak_weapons::isHeldKillstreakWeapon(killstreakType) ) + return false; + return true; +} +isDelayableKillstreak( killstreakType ) +{ + if( isDefined( level.killstreaks[killstreakType] ) && isDefined( level.killstreaks[killstreakType].delayStreak ) && level.killstreaks[killstreakType].delayStreak ) + return true; + return false; +} +getXPAmountForKillstreak( killstreakType ) +{ + xpAmount = 0; + switch( level.killstreaks[killstreakType].killstreakLevel ) + { + case 1: + case 2: + case 3: + case 4: + xpAmount = 100; + break; + case 5: + xpAmount = 150; + break; + case 6: + case 7: + xpAmount = 200; + break; + case 8: + xpAmount = 250; + break; + case 9: + xpAmount = 300; + break; + case 10: + case 11: + xpAmount = 350; + break; + case 12: + case 13: + case 14: + case 15: + xpAmount = 500; + break; + } + return xpAmount; +} +triggerKillstreak( killstreakType ) +{ + AssertEx( IsDefined(level.killstreaks[killstreakType].useFunction), "No use function defined for killstreak " + killstreakType); + + if( shouldDelayKillstreak( killstreakType ) ) + { + timeLeft = Int( level.killstreakRoundDelay - (maps\mp\gametypes\_globallogic_utils::getTimePassed() / 1000) ); + + if( !timeLeft ) + timeLeft = 1; + self iPrintLnBold( &"MP_UNAVAILABLE_FOR_N", " " + timeLeft + " ", &"EXE_SECONDS" ); + } + else if ( [[level.killstreaks[killstreakType].useFunction]](killstreakType) ) + { + + if ( isdefined( level.killstreaks[killstreakType].killstreakLevel ) ) + { + xpAmount = getXPAmountForKillstreak( killstreakType ); + + + if ( xpAmount > 0 && killstreakType != "rcbomb_mp" ) + self thread maps\mp\gametypes\_rank::giveRankXP( "medal", xpAmount ); + self maps\mp\gametypes\_gametype_variants::onPlayerKillstreakActivated(); + } + + if ( IsDefined( self ) ) + { + bbPrint( "mpkillstreakuses: gametime %d spawnid %d name %s", getTime(), getplayerspawnid( self ), killstreakType ); + if ( !IsDefined( self.pers[level.killstreaks[killstreakType].usageKey] ) ) + { + self.pers[level.killstreaks[killstreakType].usageKey] = 0; + } + + self.pers[level.killstreaks[killstreakType].usageKey]++; + self notify( "killstreak_used", killstreakType ); + self notify( "killstreak_done", true, killstreakType ); + } + return true; + } + + if ( IsDefined( self ) ) + self notify( "killstreak_done", false, killstreakType ); + return false; +} +addToKillstreakCount( weapon ) +{ + if ( !isdefined( self.pers["totalKillstreakCount"] ) ) + self.pers["totalKillstreakCount"] = 0; + + self.pers["totalKillstreakCount"]++; +} +isWeaponAssociatedWithKillstreak( weapon ) +{ + return IsDefined( level.killstreakWeapons[weapon] ); +} +getFirstValidKillstreakAltWeapon( killstreakType ) +{ + AssertEx( IsDefined(level.killstreaks[killstreakType]), "Killstreak not registered."); + if( isDefined( level.killstreaks[killstreakType].altWeapons ) ) + { + for( i = 0; i < level.killstreaks[killstreakType].altWeapons.size; i++ ) + { + if( isDefined( level.killstreaks[killstreakType].altWeapons[i] ) ) + return level.killstreaks[killstreakType].altWeapons[i]; + } + } + + return "none"; +} +shouldGiveKillstreak( weapon ) +{ + killstreakBuilding = GetDvarInt( #"scr_allow_killstreak_building" ); + + if ( killstreakBuilding == 0 ) + { + if ( isWeaponAssociatedWithKillstreak(weapon) ) + return false; + } + + return true; +} +pointIsInDangerArea( point, targetpos, radius ) +{ + return distance2d( point, targetpos ) <= radius * 1.25; +} +printKillstreakStartText( killstreakType, owner, team, targetpos, dangerRadius ) +{ + if ( !IsDefined( level.killstreaks[killstreakType] ) ) + { + return; + } + + if ( level.teambased ) + { + players = level.players; + if ( !level.hardcoreMode && IsDefined(level.killstreaks[killstreakType].inboundNearPlayerText)) + { + for(i = 0; i < players.size; i++) + { + if(isalive(players[i]) && (isdefined(players[i].pers["team"])) && (players[i].pers["team"] == team)) + { + if ( pointIsInDangerArea( players[i].origin, targetpos, dangerRadius ) ) + players[i] iprintlnbold(level.killstreaks[killstreakType].inboundNearPlayerText); + } + } + } + + if ( IsDefined(level.killstreaks[killstreakType]) ) + { + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[i]; + playerteam = player.pers["team"]; + if ( isdefined( playerteam ) ) + { + if ( playerteam == team ) + player iprintln( level.killstreaks[killstreakType].inboundText, owner ); + } + } + } + } + else + { + if ( !level.hardcoreMode && IsDefined(level.killstreaks[killstreakType].inboundNearPlayerText) ) + { + if ( pointIsInDangerArea( owner.origin, targetpos, dangerRadius ) ) + owner iprintlnbold(level.killstreaks[killstreakType].inboundNearPlayerText); + } + } +} +playKillstreakStartDialog( killstreakType, team, playNonTeamBasedEnemySounds ) +{ + if ( !IsDefined( level.killstreaks[killstreakType] ) ) + { + return; + } + + + if ( killstreakType == "radar_mp" && level.teambased ) + { + if( getTime() - level.radarTimers[team] > 30000 ) + { + maps\mp\gametypes\_globallogic_audio::leaderDialog( killstreakType + "_start", team ); + maps\mp\gametypes\_globallogic_audio::leaderDialog( killstreakType + "_enemy_start", level.otherTeam[team] ); + level.radarTimers[team] = getTime(); + } + else + { + self maps\mp\gametypes\_globallogic_audio::leaderDialogOnPlayer( killstreakType + "_start", team ); + } + return; + } + + if ( level.teambased ) + { + maps\mp\gametypes\_globallogic_audio::leaderDialog( killstreakType + "_start", team ); + maps\mp\gametypes\_globallogic_audio::leaderDialog( killstreakType + "_enemy_start", level.otherTeam[team] ); + } + else + { + self maps\mp\gametypes\_globallogic_audio::leaderDialogOnPlayer( killstreakType + "_start" ); + + { + selfarray = []; + selfarray[0] = self; + maps\mp\gametypes\_globallogic_audio::leaderDialog( killstreakType + "_enemy_start", undefined, undefined, selfarray ); + } + } +} +playKillstreakReadyDialog( killstreakType ) +{ + self maps\mp\gametypes\_globallogic_audio::leaderDialogOnPlayer( killstreakType ); +} +playKillstreakReadyAndInformDialog( killstreakType ) +{ + self maps\mp\gametypes\_globallogic_audio::leaderDialogOnPlayer( killstreakType ); + if ( IsDefined( level.killstreaks[killstreakType].informDialog ) ) + self playLocalSound( level.killstreaks[killstreakType].informDialog ); +} +playKillstreakEndDialog( killstreakType, team ) +{ + if ( !IsDefined( level.killstreaks[killstreakType] ) ) + { + return; + } + + if ( level.teambased ) + { + maps\mp\gametypes\_globallogic_audio::leaderDialog( killstreakType + "_end", team ); + maps\mp\gametypes\_globallogic_audio::leaderDialog( killstreakType + "_enemy_end", level.otherTeam[team] ); + } + else + { + self maps\mp\gametypes\_globallogic_audio::leaderDialogOnPlayer( killstreakType + "_end" ); + } +} +getKillstreakUsageByKillstreak(killstreakType) +{ + AssertEx( IsDefined(level.killstreaks[killstreakType]), "Killstreak needs to be registered before calling getKillstreakUsage."); + + return getKillstreakUsage( level.killstreaks[killstreakType].usageKey ); +} +getKillstreakUsage(usageKey) +{ + if ( !IsDefined( self.pers[usageKey] ) ) + { + return 0; + } + + return self.pers[usageKey]; +} +onPlayerConnect() +{ + for(;;) + { + level waittill("connecting", player); + player thread onPlayerSpawned(); + player thread onJoinedTeam(); + } +} +onPlayerSpawned() +{ + self endon("disconnect"); + for(;;) + { + self waittill("spawned_player"); + pixbeginevent("_hardpoints.gsc/onPlayerSpawned"); + + giveOwnedKillstreak(); + + self thread watchNotifyKSMessage(); + + if ( !IsDefined( self.pers["killstreaks"] ) ) + self.pers["killstreaks"] = []; + if ( !IsDefined( self.pers["killstreak_has_been_used"] ) ) + self.pers["killstreak_has_been_used"] = []; + if ( !IsDefined( self.pers["killstreak_unique_id"] ) ) + self.pers["killstreak_unique_id"] = []; + size = self.pers["killstreaks"].size; + if ( size > 0 ) + playKillstreakReadyDialog( self.pers["killstreaks"][size - 1] ); + + pixendevent(); + } +} +onJoinedTeam() +{ + self endon("disconnect"); + + for(;;) + { + self waittill("joined_team"); + self.pers["cur_kill_streak"] = 0; + self.pers["totalKillstreakCount"] = 0; + self.pers["killstreaks"] = []; + self.pers["killstreak_has_been_used"] = []; + self.pers["killstreak_unique_id"] = []; + } +} + + +watchNotifyKSMessage() +{ + self endon("disconnect"); + self endon("death"); + + for (lastKs = self.pers["cur_kill_streak"];;) + { + self waittill( "new_killstreak_count" ); + + for (curStreak = lastKs + 1; curStreak <= self.pers["cur_kill_streak"]; curStreak++) + { + //if (curStreak == 5) + // continue; + + if (curStreak % 5 != 0) + continue; + + self thread streakNotify2(curStreak); + } + + lastKs = self.pers["cur_kill_streak"]; + } +} + +streakNotify2( streakVal ) +{ + self endon( "disconnect" ); + + notifyData = spawnStruct(); + + xpReward = streakVal * 100; + + self thread maps\mp\gametypes\_rank::giveRankXP( "killstreak_bonus", xpReward ); + + notifyData.notifyText = "+" + xpReward; + + wait .05; + + notifyData.titleLabel = &"MP_KILLSTREAK_N"; + notifyData.titleText = streakVal; + + self maps\mp\gametypes\_hud_message::notifyMessage( notifyData ); + + iprintln( &"RANK_KILL_STREAK_N", self, streakVal ); +} + +