#using scripts\shared\callbacks_shared; #using scripts\shared\challenges_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\demo_shared; #using scripts\shared\gameobjects_shared; #using scripts\shared\hostmigration_shared; #using scripts\shared\hud_util_shared; #using scripts\shared\math_shared; #using scripts\shared\medals_shared; #using scripts\shared\popups_shared; #using scripts\shared\scoreevents_shared; #using scripts\shared\sound_shared; #using scripts\shared\util_shared; #using scripts\mp\gametypes\_battlechatter; #using scripts\mp\gametypes\_globallogic; #using scripts\mp\gametypes\_globallogic_audio; #using scripts\mp\gametypes\_globallogic_score; #using scripts\mp\gametypes\_globallogic_utils; #using scripts\mp\gametypes\_hostmigration; #using scripts\mp\gametypes\_spawning; #using scripts\mp\gametypes\_spawnlogic; #using scripts\mp\_challenges; #using scripts\mp\_util; /*QUAKED mp_multi_team_spawn (1.0 0.0 0.0) (-16 -16 0) (16 16 72) Spawns used for use in some multi team game modes to open up other portions of the map for multi team scenarios.*/ #precache( "string", "OBJECTIVES_KOTH" ); #precache( "string", "OBJECTIVES_KOTH_SCORE" ); #precache( "string", "MP_WAITING_FOR_HQ" ); #precache( "string", "MP_KOTH_CAPTURED_BY" ); #precache( "string", "MP_KOTH_CAPTURED_BY_ENEMY" ); #precache( "string", "MP_KOTH_MOVING_IN" ); #precache( "string", "MP_CAPTURING_OBJECTIVE" ); #precache( "string", "MP_KOTH_CONTESTED_BY_ENEMY" ); #precache( "string", "MP_KOTH_AVAILABLE_IN" ); #precache( "string", "MP_CONTROL_KOTH" ); #precache( "string", "MP_CAPTURE_KOTH" ); #precache( "string", "MP_DEFEND_KOTH" ); #precache( "string", "MP_KOTH_AVAILABLE_IN" ); #precache( "string", "MP_HQ_DESPAWN_IN" ); #precache( "string", "MP_HQ_REINFORCEMENTS_IN" ); #precache( "string", "MP_CAPTURING_HQ" ); #precache( "string", "MP_DESTROYING_HQ" ); #precache( "fx", "ui/fx_koth_marker_blue" ); #precache( "fx", "ui/fx_koth_marker_orng" ); #precache( "fx", "ui/fx_koth_marker_neutral" ); #precache( "fx", "ui/fx_koth_marker_contested" ); #precache( "fx", "ui/fx_koth_marker_blue_window" ); #precache( "fx", "ui/fx_koth_marker_orng_window" ); #precache( "fx", "ui/fx_koth_marker_neutral_window" ); #precache( "fx", "ui/fx_koth_marker_contested_window" ); #precache( "objective", "hardpoint" ); function main() { globallogic::init(); util::registerTimeLimit( 0, 1440 ); util::registerScoreLimit( 0, 1000 ); util::registerNumLives( 0, 100 ); util::registerRoundSwitch( 0, 9 ); util::registerRoundWinLimit( 0, 10 ); globallogic::registerFriendlyFireDelay( level.gameType, 15, 0, 1440 ); level.teamBased = true; level.doPrematch = true; level.overrideTeamScore = true; level.scoreRoundWinBased = true; level.kothStartTime = 0; level.onStartGameType =&onStartGameType; level.playerSpawnedCB =&koth_playerSpawnedCB; level.onRoundSwitch =&onRoundSwitch; level.onPlayerKilled =&onPlayerKilled; level.onEndGame=&onEndGame; clientfield::register( "world", "hardpoint", 1, 5, "int" ); clientfield::register( "world", "hardpointteam", 1, 5, "int" ); level.zoneAutoMoveTime = GetGametypeSetting( "autoDestroyTime" ); level.zoneSpawnTime = GetGametypeSetting( "objectiveSpawnTime" ); level.kothMode = GetGametypeSetting( "kothMode" ); level.captureTime = GetGametypeSetting( "captureTime" ); level.destroyTime = GetGametypeSetting( "destroyTime" ); level.delayPlayer = GetGametypeSetting( "delayPlayer" ); level.randomZoneSpawn = GetGametypeSetting( "randomObjectiveLocations" ); level.scorePerPlayer = GetGametypeSetting( "scorePerPlayer" ); level.timePausesWhenInZone = GetGametypeSetting( "timePausesWhenInZone" ); level.iconoffset = (0,0,32); level.onRespawnDelay =&getRespawnDelay; gameobjects::register_allowed_gameobject( level.gameType ); globallogic_audio::set_leader_gametype_dialog ( "startHardPoint", "hcStartHardPoint", "objCapture", "objCapture" ); game["objective_gained_sound"] = "mpl_flagcapture_sting_friend"; game["objective_lost_sound"] = "mpl_flagcapture_sting_enemy"; game["objective_contested_sound"] = "mpl_flagreturn_sting"; level.lastDialogTime = 0; level.zoneSpawnQueue = []; // Sets the scoreboard columns and determines with data is sent across the network if ( !SessionModeIsSystemlink() && !SessionModeIsOnlineGame() && IsSplitScreen() ) // local matches only show the first three columns globallogic::setvisiblescoreboardcolumns( "score", "kills", "objtime", "defends", "deaths" ); else globallogic::setvisiblescoreboardcolumns( "score", "kills", "deaths", "objtime", "defends" ); /# // HQ radio triggers are not scoped to exclude koth right now // going to delete them just so if we render triggers we dont see all these // TODO in the future is to get them into the automatic game type delete system trigs = getentarray("radiotrigger", "targetname"); foreach( trig in trigs ) { trig delete(); } #/ } function updateObjectiveHintMessages( defenderTeam, defendMessage, attackMessage ) { foreach( team in level.teams ) { if ( defenderTeam == team ) { game["strings"]["objective_hint_" + team] = defendMessage; } else { game["strings"]["objective_hint_" + team] = attackMessage; } } } function updateObjectiveHintMessage( message ) { foreach( team in level.teams ) { game["strings"]["objective_hint_" + team] = message; } } function getRespawnDelay() { self.lowerMessageOverride = undefined; if ( !isdefined( level.zone.gameobject ) ) return undefined; zoneOwningTeam = level.zone.gameobject gameobjects::get_owner_team(); if ( self.pers["team"] == zoneOwningTeam ) { if ( !isdefined( level.zoneMoveTime ) ) return undefined; timeRemaining = (level.zoneMoveTime - gettime()) / 1000; if (!level.playerObjectiveHeldRespawnDelay ) return undefined; if ( level.playerObjectiveHeldRespawnDelay >= level.zoneAutoMoveTime ) self.lowerMessageOverride = &"MP_WAITING_FOR_HQ"; if ( level.delayPlayer ) { return min( level.spawnDelay, timeRemaining ); } else { return ceil(timeRemaining); } } } function onStartGameType() { if ( !isdefined( game["switchedsides"] ) ) game["switchedsides"] = false; if ( game["switchedsides"] ) { oldAttackers = game["attackers"]; oldDefenders = game["defenders"]; game["attackers"] = oldDefenders; game["defenders"] = oldAttackers; } globallogic_score::resetTeamScores(); foreach( team in level.teams ) { util::setObjectiveText( team, &"OBJECTIVES_KOTH" ); if ( level.splitscreen ) { util::setObjectiveScoreText( team, &"OBJECTIVES_KOTH" ); } else { util::setObjectiveScoreText( team, &"OBJECTIVES_KOTH_SCORE" ); } } level.kothTotalSecondsInZone = 0; level.objectiveHintPrepareZone = &"MP_CONTROL_KOTH"; level.objectiveHintCaptureZone = &"MP_CAPTURE_KOTH"; level.objectiveHintDefendHQ = &"MP_DEFEND_KOTH"; if ( level.zoneSpawnTime ) updateObjectiveHintMessage( level.objectiveHintPrepareZone ); else updateObjectiveHintMessage( level.objectiveHintCaptureZone ); setClientNameMode("auto_change"); // now that the game objects have been deleted place the influencers spawning::create_map_placed_influencers(); // TODO: HQ spawnpoints level.spawnMins = ( 0, 0, 0 ); level.spawnMaxs = ( 0, 0, 0 ); foreach( team in level.teams ) { spawnlogic::add_spawn_points( team, "mp_tdm_spawn" ); spawnlogic::add_spawn_points( team, "mp_multi_team_spawn" ); spawnlogic::place_spawn_points( spawning::getTDMStartSpawnName(team) ); } spawning::updateAllSpawnPoints(); level.spawn_start = []; foreach( team in level.teams ) { level.spawn_start[ team ] = spawnlogic::get_spawnpoint_array( spawning::getTDMStartSpawnName(team) ); } level.mapCenter = math::find_box_center( level.spawnMins, level.spawnMaxs ); setMapCenter( level.mapCenter ); spawnpoint = spawnlogic::get_random_intermission_point(); setDemoIntermissionPoint( spawnpoint.origin, spawnpoint.angles ); level.spawn_all = spawnlogic::get_spawnpoint_array( "mp_tdm_spawn" ); if ( !level.spawn_all.size ) { /# println("^1No mp_tdm_spawn spawnpoints in level!"); #/ callback::abort_level(); return; } thread SetupZones(); updateGametypeDvars(); thread KothMainLoop(); } function pause_time() { if ( level.timePausesWhenInZone ) { globallogic_utils::pauseTimer(); level.timerPaused = true; } } function resume_time() { if ( level.timePausesWhenInZone ) { globallogic_utils::resumeTimerDiscardOverride( level.kothTotalSecondsInZone * 1000 ); level.timerPaused = false; } } function updateGametypeDvars() { level.playerCaptureLPM = GetGametypeSetting( "maxPlayerEventsPerMinute" ); level.timePausesWhenInZone = GetGametypeSetting( "timePausesWhenInZone" ); } function spawn_first_zone(delay) { // pick next Zone object if ( level.randomZoneSpawn == 1 ) { level.zone = GetNextZoneFromQueue(); } else { level.zone = GetFirstZone(); } if ( isdefined( level.zone ) ) { /#print("zone spawned: ("+level.zone.trigOrigin[0]+","+level.zone.trigOrigin[1]+","+level.zone.trigOrigin[2]+")");#/ level.zone spawning::enable_influencers(true); } level.zone.gameobject.trigger AllowTacticalInsertion( false ); return; } function spawn_next_zone() { level.zone.gameobject.trigger AllowTacticalInsertion( true ); // pick next Zone object if ( level.randomZoneSpawn != 0 ) { level.zone = GetNextZoneFromQueue(); } else { level.zone = GetNextZone(); } if ( isdefined( level.zone ) ) { /#print("zone spawned: ("+level.zone.trigOrigin[0]+","+level.zone.trigOrigin[1]+","+level.zone.trigOrigin[2]+")");#/ level.zone spawning::enable_influencers(true); } level.zone.gameobject.trigger AllowTacticalInsertion( false ); return; } function getNumTouching( ) { numTouching = 0; foreach( team in level.teams ) { numTouching += self.numTouching[team]; } return numTouching; } function toggleZoneEffects( enabled ) { index = 0; if ( enabled ) { index = self.script_index; } level clientfield::set( "hardpoint", index ); level clientfield::set( "hardpointteam", 0 ); } function KothCaptureLoop() { level endon("game_ended"); level endon("zone_moved"); level.kothStartTime = gettime(); while( 1 ) { level.zone.gameobject gameobjects::allow_use( "any" ); level.zone.gameobject gameobjects::set_use_time( level.captureTime ); level.zone.gameobject gameobjects::set_use_text( &"MP_CAPTURING_OBJECTIVE" ); numTouching = level.zone.gameobject getNumTouching( ); level.zone.gameobject gameobjects::set_visible_team( "any" ); level.zone.gameobject gameobjects::set_model_visibility( true ); level.zone.gameobject gameobjects::must_maintain_claim( false ); level.zone.gameobject gameobjects::can_contest_claim( true ); level.zone.gameobject.onUse =&onZoneCapture; level.zone.gameobject.onBeginUse =&onBeginUse; level.zone.gameobject.onEndUse =&onEndUse; level.zone toggleZoneEffects( true ); msg = level util::waittill_any_return( "zone_captured", "zone_destroyed", "game_ended", "zone_moved" ); // this happens if it goes from contested to neutral if ( msg == "zone_destroyed" ) continue; ownerTeam = level.zone.gameobject gameobjects::get_owner_team(); foreach( team in level.teams ) { updateObjectiveHintMessages( ownerTeam, level.objectiveHintDefendHQ, level.objectiveHintCaptureZone ); } level.zone.gameobject gameobjects::allow_use( "none" ); level.zone.gameobject.onUse = undefined; level.zone.gameobject.onUnoccupied =&onZoneUnoccupied; level.zone.gameobject.onContested =&onZoneContested; level.zone.gameobject.onUncontested =&onZoneUncontested; level waittill( "zone_destroyed", destroy_team ); if ( !level.kothMode || level.zoneDestroyedByTimer ) break; thread forceSpawnTeam( ownerTeam ); if ( isdefined( destroy_team ) ) { level.zone.gameobject gameobjects::set_owner_team( destroy_team ); } else { level.zone.gameobject gameobjects::set_owner_team( "none" ); } } } function KothMainLoop() { level endon("game_ended"); level.zoneRevealTime = -100000; zoneSpawningInStr = &"MP_KOTH_AVAILABLE_IN"; if ( level.kothMode ) { zoneDestroyedInFriendlyStr = &"MP_HQ_DESPAWN_IN"; zoneDestroyedInEnemyStr = &"MP_KOTH_MOVING_IN"; } else { zoneDestroyedInFriendlyStr = &"MP_HQ_REINFORCEMENTS_IN"; zoneDestroyedInEnemyStr = &"MP_HQ_DESPAWN_IN"; } spawn_first_zone(); while ( level.inPrematchPeriod ) {wait(.05);}; pause_time(); wait 5; SetBombTimer( "A", 0 ); setMatchFlag( "bomb_timer_a", 0 ); thread hideTimerDisplayOnGameEnd(); while( 1 ) { resume_time(); sound::play_on_players( "mp_suitcase_pickup" ); globallogic_audio::leader_dialog( "kothLocated", undefined, undefined, "gamemode_objective", undefined, "kothActiveDialogBuffer" ); level.zone.gameobject gameobjects::set_model_visibility( true ); level.zoneRevealTime = gettime(); if ( level.zoneSpawnTime ) { level.zone.gameobject gameobjects::set_visible_team( "any" ); level.zone.gameobject gameobjects::set_flags( 1 ); updateObjectiveHintMessage( level.objectiveHintPrepareZone ); setMatchFlag( "bomb_timer_a", 1 ); SetBombTimer( "A", int( gettime() + 1000 + level.zoneSpawnTime * 1000 ) ); wait level.zoneSpawnTime; level.zone.gameobject gameobjects::set_flags( 0 ); globallogic_audio::leader_dialog( "kothOnline", undefined, undefined, "gamemode_objective", undefined, "kothActiveDialogBuffer" ); } setMatchFlag( "bomb_timer_a", 0 ); waittillframeend; updateObjectiveHintMessage( level.objectiveHintCaptureZone ); sound::play_on_players( "mpl_hq_cap_us" ); level.zone.gameobject gameobjects::enable_object(); // Attach the objective to the visuals since it is at the right position and we can't attch it to the koth_zone_center script_origin Objective_OnEntity( level.zone.gameobject.objectiveID, level.zone.objectiveAnchor ); level.zone.gameobject.captureCount = 0; if ( level.zoneAutoMoveTime ) { thread MoveZoneAfterTime( level.zoneAutoMoveTime ); setMatchFlag( "bomb_timer_a", 1 ); SetBombTimer( "A", int( gettime() + 1000 + level.zoneAutoMoveTime * 1000 ) ); } else { level.zoneDestroyedByTimer = false; } KothCaptureLoop(); ownerTeam = level.zone.gameobject gameobjects::get_owner_team(); if ( level.zone.gameobject.captureCount == 1 ) { // Copy touch list so there aren't any threading issues touchList = []; touchKeys = GetArrayKeys( level.zone.gameobject.touchList[ownerTeam] ); for ( i = 0 ; i < touchKeys.size ; i++ ) touchList[touchKeys[i]] = level.zone.gameobject.touchList[ownerTeam][touchKeys[i]]; thread give_held_credit( touchList ); } pause_time(); level.zone spawning::enable_influencers(false); level.zone.gameobject.lastCaptureTeam = undefined; level.zone.gameobject gameobjects::disable_object(); level.zone.gameobject gameobjects::allow_use( "none" ); level.zone.gameobject gameobjects::set_owner_team( "neutral" ); level.zone.gameobject gameobjects::set_model_visibility( false ); level.zone.gameobject gameobjects::must_maintain_claim( false ); level.zone toggleZoneEffects( false ); level notify("zone_reset"); setMatchFlag( "bomb_timer_a", 0 ); spawn_next_zone(); wait 0.5; thread forceSpawnTeam( ownerTeam ); wait 0.5; } } function hideTimerDisplayOnGameEnd() { level waittill("game_ended"); setMatchFlag( "bomb_timer_a", 0 ); } function forceSpawnTeam( team ) { players = level.players; for ( i = 0; i < players.size; i++ ) { player = players[i]; if ( !isdefined( player ) ) continue; if ( player.pers["team"] == team ) { player notify( "force_spawn" ); wait .1; } } } function updateTeamClientField() { ownerTeam = self gameobjects::get_owner_team(); if ( ( isdefined( self.isContested ) && self.isContested ) ) { level clientfield::set( "hardpointteam", 3 ); } else if ( ownerTeam == "neutral" ) { level clientfield::set( "hardpointteam", 0 ); } else { if ( ownerTeam == "allies" ) level clientfield::set( "hardpointteam", 1 ); else level clientfield::set( "hardpointteam", 2 ); } } function onBeginUse( player ) { ownerTeam = self gameobjects::get_owner_team(); if ( ownerTeam == "neutral" ) { player thread battlechatter::gametype_specific_battle_chatter( "hq_protect", player.pers["team"] ); } else { player thread battlechatter::gametype_specific_battle_chatter( "hq_attack", player.pers["team"] ); } } function onEndUse( team, player, success ) { player notify( "event_ended" ); } function onZoneCapture( player ) { capture_team = player.pers["team"]; captureTime = getTime(); /#print( "zone captured" );#/ pause_time(); string = &"MP_KOTH_CAPTURED_BY"; level.zone.gameobject.isContested = false; level.useStartSpawns = false; if ( !isdefined( self.lastCaptureTeam ) || self.lastCaptureTeam != capture_team ) { // Copy touch list so there aren't any threading issues touchList = []; touchKeys = GetArrayKeys( self.touchList[capture_team] ); for ( i = 0 ; i < touchKeys.size ; i++ ) touchList[touchKeys[i]] = self.touchList[capture_team][touchKeys[i]]; thread give_capture_credit( touchList, string, captureTime, capture_team, self.lastCaptureTeam ); } level.kothCapTeam = capture_team; self gameobjects::set_owner_team( capture_team ); if ( !level.kothMode ) self gameobjects::set_use_time( level.destroyTime ); foreach( team in level.teams ) { if ( team == capture_team ) { if ( !isdefined( self.lastCaptureTeam ) || ( self.lastCaptureTeam != team ) ) // If retaking this point after being contested, don't play VO again { globallogic_audio::leader_dialog( "kothSecured", team, undefined, "gamemode_objective", undefined, "kothActiveDialogBuffer" ); for ( index = 0; index < level.players.size; index++ ) { player = level.players[index]; if ( player.pers["team"] == team ) { if ( player.lastKilltime + 500 > getTime() ) { player challenges::killedLastContester(); } } } } thread sound::play_on_players( game["objective_gained_sound"], team ); } else { if ( !isdefined( self.lastCaptureTeam ) ) { globallogic_audio::leader_dialog( "kothCaptured", team, undefined, "gamemode_objective", undefined, "kothActiveDialogBuffer" ); } else if ( self.lastCaptureTeam == team ) { globallogic_audio::leader_dialog( "kothLost", team, undefined, "gamemode_objective", undefined, "kothActiveDialogBuffer" ); } thread sound::play_on_players( game["objective_lost_sound"], team ); } } self thread awardCapturePoints( capture_team, self.lastCaptureTeam ); self.captureCount++; self.lastCaptureTeam = capture_team; self gameobjects::must_maintain_claim( true ); self updateTeamClientField(); player RecordGameEvent( "hardpoint_captured" ); level notify( "zone_captured" ); level notify( "zone_captured" + capture_team ); player notify( "event_ended" ); } function track_capture_time() { } function give_capture_credit( touchList, string, captureTime, capture_team, lastCaptureTeam ) { wait .05; util::WaitTillSlowProcessAllowed(); players = getArrayKeys( touchList ); for ( i = 0; i < players.size; i++ ) { player = touchList[players[i]].player; player updateCapsPerMinute( lastCaptureTeam ); if ( !isScoreBoosting( player ) ) { player challenges::capturedObjective( captureTime, self.trigger ); if ( level.kothStartTime + 3000 > captureTime && level.kothCapTeam == capture_team ) { scoreevents::processScoreEvent( "quickly_secure_point", player ); } scoreevents::processScoreEvent( "koth_secure", player ); player RecordGameEvent("capture"); level thread popups::DisplayTeamMessageToAll( string, player ); if( isdefined(player.pers["captures"]) ) { player.pers["captures"]++; player.captures = player.pers["captures"]; } if ( level.kothStartTime + 500 > captureTime ) { player challenges::immediateCapture(); } demo::bookmark( "event", gettime(), player ); player AddPlayerStatWithGameType( "CAPTURES", 1 ); } else { /# player IPrintlnBold( "GAMETYPE DEBUG: NOT GIVING YOU CAPTURE CREDIT AS BOOSTING PREVENTION" ); #/ } } } function give_held_credit( touchList, team ) { wait .05; util::WaitTillSlowProcessAllowed(); players = getArrayKeys( touchList ); for ( i = 0; i < players.size; i++ ) { player = touchList[players[i]].player; //scoreevents::processScoreEvent( "koth_held", player ); // do not know if we want the following //player RecordGameEvent("held"); } } function onZoneDestroy( player ) { destroyed_team = player.pers["team"]; /#print( "zone destroyed" );#/ scoreevents::processScoreEvent( "zone_destroyed", player ); player RecordGameEvent("destroy"); player AddPlayerStatWithGameType( "DESTRUCTIONS", 1 ); if( isdefined(player.pers["destructions"]) ) { player.pers["destructions"]++; player.destructions = player.pers["destructions"]; } destroyTeamMessage = &"MP_HQ_DESTROYED_BY"; otherTeamMessage = &"MP_HQ_DESTROYED_BY_ENEMY"; if ( level.kothMode ) { destroyTeamMessage = &"MP_KOTH_CAPTURED_BY"; otherTeamMessage = &"MP_KOTH_CAPTURED_BY_ENEMY"; } level thread popups::DisplayTeamMessageToAll( destroyTeamMessage, player ); foreach( team in level.teams ) { if ( team == destroyed_team ) { globallogic_audio::leader_dialog( "koth_secured", team, undefined, "gamemode_objective" ); } else { globallogic_audio::leader_dialog( "koth_destroyed", team, undefined, "gamemode_objective" ); } } level notify( "zone_destroyed", destroyed_team ); if ( level.kothMode ) level thread awardCapturePoints( destroyed_team ); player notify( "event_ended" ); } function onZoneUnoccupied() { level notify( "zone_destroyed" ); level.kothCapTeam = "neutral"; level.zone.gameobject.wasLeftUnoccupied = true; level.zone.gameobject.isContested = false; level.zone.gameobject RecordGameEventNonPlayer( "hardpoint_empty" ); resume_time(); self updateTeamClientField(); } function onZoneContested() { zoneOwningTeam = self gameobjects::get_owner_team(); self.wasContested = true; self.isContested = true; self updateTeamClientField(); self RecordGameEventNonPlayer( "hardpoint_contested" ); resume_time(); foreach( team in level.teams ) { if ( team == zoneOwningTeam ) { thread sound::play_on_players( game["objective_contested_sound"], team ); globallogic_audio::leader_dialog( "kothContested", team, undefined, "gamemode_objective", undefined, "kothActiveDialogBuffer" ); } } } function onZoneUncontested( lastClaimTeam ) { assert( lastClaimTeam == level.zone.gameobject gameobjects::get_owner_team() ); self.isContested = false; pause_time(); self gameobjects::set_claim_team( lastClaimTeam ); self updateTeamClientField(); self RecordGameEventNonPlayer( "hardpoint_uncontested" ); } function MoveZoneAfterTime( time ) { level endon( "game_ended" ); level endon( "zone_reset" ); level.zoneMoveTime = gettime() + time * 1000; level.zoneDestroyedByTimer = false; wait time; if ( !isdefined( level.zone.gameobject.wasContested ) || level.zone.gameobject.wasContested == false ) { if ( !isdefined( level.zone.gameobject.wasLeftUnoccupied ) || level.zone.gameobject.wasLeftUnoccupied == false ) { zoneOwningTeam = level.zone.gameobject gameobjects::get_owner_team(); challenges::controlZoneEntirely( zoneOwningTeam ); } } level.zoneDestroyedByTimer = true; level.zone.gameobject RecordGameEventNonPlayer( "hardpoint_moved" ); level notify( "zone_moved" ); } function awardCapturePoints( team, lastCaptureTeam ) { level endon( "game_ended" ); level endon( "zone_destroyed" ); level endon( "zone_reset" ); level endon( "zone_moved" ); level notify("awardCapturePointsRunning"); level endon("awardCapturePointsRunning"); seconds = 1; score = 1; while ( !level.gameEnded ) { wait seconds; hostmigration::waitTillHostMigrationDone(); if ( !level.zone.gameobject.isContested ) { if ( level.scorePerPlayer ) { score = level.zone.gameobject.numTouching[team]; } globallogic_score::giveTeamScoreForObjective( team, score ); level.kothTotalSecondsInZone++; foreach( player in level.aliveplayers[team] ) { if ( !IsDefined( player.touchTriggers[self.entNum] ) ) continue; if( isdefined(player.pers["objtime"]) ) { player.pers["objtime"]++; player.objtime = player.pers["objtime"]; } player AddPlayerStatWithGameType( "OBJECTIVE_TIME", 1 ); } } } } function koth_playerSpawnedCB() { self.lowerMessageOverride = undefined; } function CompareZoneIndexes( zone_a, zone_b ) { script_index_a = zone_a.script_index; script_index_b = zone_b.script_index; if( !isdefined(script_index_a) && !isdefined(script_index_b) ) { return false; } if( !isdefined(script_index_a) && isdefined(script_index_b) ) { /# println( "KOTH: Missing script_index on zone at " + zone_a.origin ); #/ return true; } if( isdefined(script_index_a) && !isdefined(script_index_b) ) { /# println( "KOTH: Missing script_index on zone at " + zone_b.origin ); #/ return false; } if( script_index_a > script_index_b ) { return true; } return false; } function getZoneArray() { zones = getentarray( "koth_zone_center", "targetname" ); if( !isdefined( zones ) ) { return undefined; } swapped = true; n = zones.size; while ( swapped ) { swapped = false; for( i = 0 ; i < n-1 ; i++ ) { if( CompareZoneIndexes(zones[i], zones[i+1]) ) { temp = zones[i]; zones[i] = zones[i+1]; zones[i+1] = temp; swapped = true; } } n--; } return zones; } function SetupZones() { maperrors = []; zones = getZoneArray(); // if ( zones.size < 2 ) // { // maperrors[maperrors.size] = "There are not at least 2 entities with targetname \"zone\""; // } trigs = getentarray("koth_zone_trigger", "targetname"); for ( i = 0; i < zones.size; i++ ) { errored = false; zone = zones[i]; zone.trig = undefined; for ( j = 0; j < trigs.size; j++ ) { if ( zone istouching( trigs[j] ) ) { if ( isdefined( zone.trig ) ) { maperrors[maperrors.size] = "Zone at " + zone.origin + " is touching more than one \"zonetrigger\" trigger"; errored = true; break; } zone.trig = trigs[j]; break; } } if ( !isdefined( zone.trig ) ) { if ( !errored ) { maperrors[maperrors.size] = "Zone at " + zone.origin + " is not inside any \"zonetrigger\" trigger"; continue; } // possible fallback (has been tested) //zone.trig = spawn( "trigger_radius", zone.origin, 0, 128, 128 ); //errored = false; } assert( !errored ); zone.trigorigin = zone.trig.origin; zone.objectiveAnchor = Spawn( "script_model", zone.origin ); // We need a script_model to attach the objective to visuals = []; visuals[0] = zone; if ( isdefined( zone.target ) ) { otherVisuals = getEntArray( zone.target, "targetname" ); for ( j = 0; j < otherVisuals.size; j++ ) { visuals[visuals.size] = otherVisuals[j]; } } objective_name = istring("hardpoint"); zone.gameObject = gameobjects::create_use_object( "neutral", zone.trig, visuals, (0,0,0), objective_name ); zone.gameObject gameobjects::set_objective_entity( zone ); zone.gameObject gameobjects::disable_object(); zone.gameObject gameobjects::set_model_visibility( false ); zone.trig.useObj = zone.gameObject; zone.trig.remote_control_player_can_trigger = true; zone setUpNearbySpawns(); zone createZoneSpawnInfluencer(); } if (maperrors.size > 0) { /# println("^1------------ Map Errors ------------"); for(i = 0; i < maperrors.size; i++) println(maperrors[i]); println("^1------------------------------------"); util::error("Map errors. See above"); #/ callback::abort_level(); return; } level.zones = zones; level.prevzone = undefined; level.prevzone2 = undefined; setupZoneExclusions(); return true; } function setupZoneExclusions() { if ( !isdefined( level.levelkothDisable ) ) return; /* foreach( nullZone in level.levelkothDisable ) { foreach( zone in level.zones ) { // if ( zone.gameObject.trigger istouching( nullZone ) ) if ( zone.gameObject.trigger istouchingvolume( nullZone.origin, nullZone getmins(), nullZone getmaxs() ) ) { if ( !isdefined( zone.gameObject.exclusions ) ) { zone.gameObject.exclusions = []; } zone.gameObject.exclusions[ zone.gameObject.exclusions.size ] = nullZone; } } } */ foreach( nullZone in level.levelkothDisable ) { mindist = 10000000000; foundZone = undefined; foreach( zone in level.zones ) { distance = DistanceSquared( nullZone.origin, zone.origin ); if ( distance < mindist ) { foundZone = zone; mindist = distance; } } if ( isdefined( foundZone ) ) { if ( !isdefined( foundZone.gameObject.exclusions ) ) { foundZone.gameObject.exclusions = []; } foundZone.gameObject.exclusions[ foundZone.gameObject.exclusions.size ] = nullZone; } } } function setUpNearbySpawns() { spawns = level.spawn_all; for ( i = 0; i < spawns.size; i++ ) { spawns[i].distsq = distanceSquared( spawns[i].origin, self.origin ); } // sort by distsq for ( i = 1; i < spawns.size; i++ ) { thespawn = spawns[i]; for ( j = i - 1; j >= 0 && thespawn.distsq < spawns[j].distsq; j-- ) spawns[j + 1] = spawns[j]; spawns[j + 1] = thespawn; } first = []; second = []; third = []; outer = []; thirdSize = spawns.size / 3; for ( i = 0; i <= thirdSize; i++ ) { first[ first.size ] = spawns[i]; } for ( ; i < spawns.size; i++ ) { outer[ outer.size ] = spawns[i]; if ( i <= (thirdSize*2) ) second[ second.size ] = spawns[i]; else third[ third.size ] = spawns[i]; } self.gameObject.nearSpawns = first; self.gameObject.midSpawns = second; self.gameObject.farSpawns = third; self.gameObject.outerSpawns = outer; } function GetFirstZone() { zone = level.zones[ 0 ]; // old linear and "random" systems level.prevzone2 = level.prevzone; level.prevzone = zone; level.prevZoneIndex = 0; // new shuffled system ShuffleZones(); ArrayRemoveValue( level.zoneSpawnQueue, zone ); return zone; } function GetNextZone() { nextZoneIndex = (level.prevZoneIndex + 1) % level.zones.size; zone = level.zones[ nextZoneIndex ]; level.prevzone2 = level.prevzone; level.prevzone = zone; level.prevZoneIndex = nextZoneIndex; return zone; } function PickRandomZoneToSpawn() { level.prevZoneIndex = randomint( level.zones.size); zone = level.zones[ level.prevZoneIndex ]; level.prevzone2 = level.prevzone; level.prevzone = zone; return zone; } function ShuffleZones() { level.zoneSpawnQueue = []; spawnQueue = ArrayCopy(level.zones); total_left = spawnQueue.size; while( total_left > 0 ) { index = randomint( total_left ); valid_zones = 0; for( zone = 0; zone < level.zones.size; zone++ ) { if ( !isdefined(spawnQueue[zone]) ) continue; if ( valid_zones == index ) { // dont allow the last radio from the previous shuffle to be put first in the next if ( level.zoneSpawnQueue.size == 0 && isdefined( level.zone ) && level.zone == spawnQueue[zone] ) continue; level.zoneSpawnQueue[level.zoneSpawnQueue.size] = spawnQueue[zone]; spawnQueue[zone] = undefined; break; } valid_zones++; } total_left--; } } // shuffled picking function GetNextZoneFromQueue() { if ( level.zoneSpawnQueue.size == 0 ) ShuffleZones(); assert( level.zoneSpawnQueue.size > 0 ); next_zone = level.zoneSpawnQueue[0]; ArrayRemoveIndex( level.zoneSpawnQueue, 0 ); return next_zone; } function GetCountOfTeamsWithPlayers(num) { has_players = 0; foreach( team in level.teams ) { if ( num[team] > 0 ) has_players++; } return has_players; } function GetPointCost( avgpos, origin ) { avg_distance = 0; total_error = 0; distances = []; foreach( team, position in avgpos ) { distances[team] = Distance(origin, avgpos[team]); avg_distance += distances[team]; } avg_distance = avg_distance / distances.size; foreach( team, dist in distances ) { err = (distances[team] - avg_distance); total_error += err * err; } return total_error; } function PickZoneToSpawn() { // find average of positions of each team // (medians would be better, to get rid of outliers...) // and find the zone which has the least difference in distance from those two averages foreach( team in level.teams ) { avgpos[team] = (0,0,0); num[team] = 0; } for ( i = 0; i < level.players.size; i++ ) { player = level.players[i]; if ( isalive( player ) ) { avgpos[ player.pers["team"] ] += player.origin; num[ player.pers["team"] ]++; } } if ( GetCountOfTeamsWithPlayers(num) <= 1 ) { zone = level.zones[ randomint( level.zones.size) ]; while ( isdefined( level.prevzone ) && zone == level.prevzone ) // so lazy zone = level.zones[ randomint( level.zones.size) ]; level.prevzone2 = level.prevzone; level.prevzone = zone; return zone; } foreach( team in level.teams ) { if ( num[team] == 0 ) { avgpos[team] = undefined; } else { avgpos[team] = avgpos[team] / num[team]; } } bestzone = undefined; lowestcost = undefined; for ( i = 0; i < level.zones.size; i++ ) { zone = level.zones[i]; // (purposefully using distance instead of distanceSquared) cost = GetPointCost( avgpos, zone.origin ); if ( isdefined( level.prevzone ) && zone == level.prevzone ) { continue; } if ( isdefined( level.prevzone2 ) && zone == level.prevzone2 ) { if ( level.zones.size > 2 ) continue; else cost += 512 * 512; } if ( !isdefined( lowestcost ) || cost < lowestcost ) { lowestcost = cost; bestzone = zone; } } assert( isdefined( bestzone ) ); level.prevzone2 = level.prevzone; level.prevzone = bestzone; return bestzone; } function onRoundSwitch() { game["switchedsides"] = !game["switchedsides"]; } function onPlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration ) { if ( !isPlayer( attacker ) || (level.captureTime && !self.touchTriggers.size && !attacker.touchTriggers.size) || attacker.pers["team"] == self.pers["team"] ) return; medalGiven = false; scoreEventProcessed = false; ownerTeam = undefined; if ( level.captureTime == 0 ) { if ( !isdefined( level.zone ) ) return; ownerTeam = level.zone.gameObject.ownerTeam ; if ( !isdefined( ownerTeam ) || ownerTeam == "neutral" ) return; } if ( self.touchTriggers.size || ( level.captureTime == 0 && self IsTouching( level.zone.trig ) ) ) { if ( level.captureTime > 0 ) { triggerIds = getArrayKeys( self.touchTriggers ); ownerTeam = self.touchTriggers[triggerIds[0]].useObj.ownerTeam; } if ( ownerTeam != "neutral" ) { attacker.lastKilltime = getTime(); team = attacker.pers["team"]; if ( team == ownerTeam ) { if ( !medalGiven ) { attacker medals::offenseGlobalCount(); attacker thread challenges::killedBaseOffender(level.zone.trig, weapon); medalGiven = true; } //scoreevents::processScoreEvent( "killed_defender", attacker, undefined, weapon );// TFLAME 9/3/12 - Changing these events to "hardpoint_kill" as attacker / defender changes so often in Hardpoint scoreevents::processScoreEvent( "hardpoint_kill", attacker, undefined, weapon ); self RecordKillModifier("defending"); scoreEventProcessed = true; } else { if ( !medalGiven ) { if( isdefined(attacker.pers["defends"]) ) { attacker.pers["defends"]++; attacker.defends = attacker.pers["defends"]; } attacker medals::defenseGlobalCount(); medalGiven = true; attacker thread challenges::killedBaseDefender(level.zone.trig); attacker RecordGameEvent("defending"); } attacker challenges::killedZoneAttacker( weapon ); //scoreevents::processScoreEvent( "killed_attacker", attacker, undefined, weapon ); // TFLAME 9/3/12 - Changing these events to "hardpoint_kill" as attacker / defender changes so often in Hardpoint scoreevents::processScoreEvent( "hardpoint_kill", attacker, undefined, weapon ); self RecordKillModifier("assaulting"); scoreEventProcessed = true; } } } if ( attacker.touchTriggers.size || ( level.captureTime == 0 && attacker IsTouching( level.zone.trig ) ) ) { if ( level.captureTime > 0 ) { triggerIds = getArrayKeys( attacker.touchTriggers ); ownerTeam = attacker.touchTriggers[triggerIds[0]].useObj.ownerTeam; } if ( ownerTeam != "neutral" ) { team = self.pers["team"]; if ( team == ownerTeam ) { if ( !medalGiven ) { if( isdefined(attacker.pers["defends"]) ) { attacker.pers["defends"]++; attacker.defends = attacker.pers["defends"]; } attacker medals::defenseGlobalCount(); medalGiven = true; attacker thread challenges::killedBaseDefender(level.zone.trig); attacker RecordGameEvent("defending"); } if ( scoreEventProcessed == false ) { attacker challenges::killedZoneAttacker( weapon ); //scoreevents::processScoreEvent( "killed_attacker", attacker, undefined, weapon );// TFLAME 9/3/12 - Changing these events to "hardpoint_kill" as attacker / defender changes so often in Hardpoint scoreevents::processScoreEvent( "hardpoint_kill", attacker, undefined, weapon ); self RecordKillModifier("assaulting"); } } else { if ( !medalGiven ) { attacker medals::offenseGlobalCount(); medalGiven = true; attacker thread challenges::killedBaseOffender(level.zone.trig, weapon); } if ( scoreEventProcessed == false ) { //scoreevents::processScoreEvent( "killed_defender", attacker, undefined, weapon );// TFLAME 9/3/12 - Changing these events to "hardpoint_kill" as attacker / defender changes so often in Hardpoint scoreevents::processScoreEvent( "hardpoint_kill", attacker, undefined, weapon ); self RecordKillModifier("defending"); } } } } if ( medalGiven == true ) { if ( ( isdefined( level.zone.gameobject.isContested ) && level.zone.gameobject.isContested ) ) { attacker thread killWhileContesting(); } } } function watchKillWhileContesting( zone_captured_team ) { level endon( zone_captured_team ); level endon( "zone_destroyed" ); level endon( "zone_captured" ); level endon( "death" ); self util::waittill_any_return( "killWhileContesting", "disconnect" ); level notify( "abortKillWhileContesting" ); } function killWhileContesting() { self notify( "killWhileContesting" ); self endon( "killWhileContesting" ); self endon( "disconnect" ); killTime = getTime(); playerteam = self.pers["team"]; if ( !isdefined ( self.clearEnemyCount ) ) { self.clearEnemyCount = 0; } self.clearEnemyCount++; zone_captured_team = "zone_captured" + playerteam; self thread watchKillWhileContesting( zone_captured_team ); zoneReturn = level util::waittill_any_return( zone_captured_team, "zone_destroyed", "zone_captured", "death", "abortKillWhileContesting" ); if ( zoneReturn == "death" || playerteam != self.pers["team"] ) { self.clearEnemyCount = 0; return; } if ( self.clearEnemyCount >= 2 && killTime + 200 > getTime() ) { scoreevents::processScoreEvent( "clear_2_attackers", self ); } self.clearEnemyCount = 0; } function onEndGame( winningTeam ) { for ( i = 0; i < level.zones.size; i++ ) { level.zones[i].gameobject gameobjects::allow_use( "none" ); } } function createZoneSpawnInfluencer() { // this affects both teams self spawning::create_influencer( "koth_large", self.gameobject.curOrigin, 0 ); self spawning::create_influencer( "koth_small", self.gameobject.curOrigin, 0 ); // turn it off for now self spawning::enable_influencers(false); } function updateCapsPerMinute(lastOwnerTeam) { if ( !isdefined( self.capsPerMinute ) ) { self.numCaps = 0; self.capsPerMinute = 0; } // not including neutral flags as part of the boosting prevention // to help with false positives at the start if ( !isdefined ( lastOwnerTeam ) || lastOwnerTeam == "neutral" ) return; self.numCaps++; minutesPassed = globallogic_utils::getTimePassed() / ( 60 * 1000 ); // players use the actual time played if ( IsPlayer( self ) && isdefined(self.timePlayed["total"]) ) minutesPassed = self.timePlayed["total"] / 60; self.capsPerMinute = ( minutesPassed ? self.numCaps / minutesPassed : 0 ); if ( self.capsPerMinute > self.numCaps ) self.capsPerMinute = self.numCaps; } function isScoreBoosting( player ) { if ( !level.rankedMatch ) return false; if ( player.capsPerMinute > level.playerCaptureLPM ) return true; return false; }