#using scripts\shared\callbacks_shared; #using scripts\shared\challenges_shared; #using scripts\shared\demo_shared; #using scripts\shared\gameobjects_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\shared\weapons\_weapons; #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\_spawning; #using scripts\mp\gametypes\_spawnlogic; #using scripts\mp\_challenges; #using scripts\mp\_util; #using scripts\mp\killstreaks\_rcbomb; #using scripts\mp\killstreaks\_supplydrop; #precache( "string", "OBJECTIVES_KOTH" ); #precache( "string", "OBJECTIVES_HQ" ); #precache( "string", "OBJECTIVES_HQ_SCORE" ); #precache( "string", "MP_WAITING_FOR_HQ" ); #precache( "string", "MP_HQ_CAPTURED_BY" ); #precache( "string", "MP_CONTROL_HQ" ); #precache( "string", "MP_CAPTURE_HQ" ); #precache( "string", "MP_DESTROY_HQ" ); #precache( "string", "MP_DEFEND_HQ" ); #precache( "string", "MP_HQ_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( "eventstring", "objective" ); 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.onStartGameType =&onStartGameType; level.playerSpawnedCB =&koth_playerSpawnedCB; level.onRoundSwitch =&onRoundSwitch; level.onPlayerKilled =&onPlayerKilled; level.onEndGame=&onEndGame; level.hqAutoDestroyTime = GetGametypeSetting( "autoDestroyTime" ); level.hqSpawnTime = GetGametypeSetting( "objectiveSpawnTime" ); level.kothMode = GetGametypeSetting( "kothMode" ); level.captureTime = GetGametypeSetting( "captureTime" ); level.destroyTime = GetGametypeSetting( "destroyTime" ); level.delayPlayer = GetGametypeSetting( "delayPlayer" ); level.randomHQSpawn = GetGametypeSetting( "randomObjectiveLocations" ); level.maxRespawnDelay = GetGametypeSetting( "timeLimit" ) * 60; level.iconoffset = (0,0,32); level.onRespawnDelay =&getRespawnDelay; gameobjects::register_allowed_gameobject( level.gameType ); game["dialog"]["gametype"] = "hq_start"; game["dialog"]["gametype_hardcore"] = "hchq_start"; game["dialog"]["offense_obj"] = "cap_start"; game["dialog"]["defense_obj"] = "cap_start"; level.lastDialogTime = 0; level.radioSpawnQueue = []; // 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", "captures", "defends", "deaths" ); else globallogic::setvisiblescoreboardcolumns( "score", "kills", "deaths", "captures", "defends" ); } 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.radio.gameobject ) ) return undefined; hqOwningTeam = level.radio.gameobject gameobjects::get_owner_team(); if ( self.pers["team"] == hqOwningTeam ) { if ( !isdefined( level.hqDestroyTime ) ) timeRemaining = level.maxRespawnDelay; else timeRemaining = (level.hqDestroyTime - gettime()) / 1000; if (!level.playerObjectiveHeldRespawnDelay ) return undefined; if ( level.playerObjectiveHeldRespawnDelay >= level.hqAutoDestroyTime ) 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_HQ" ); } else { util::setObjectiveScoreText( team, &"OBJECTIVES_HQ_SCORE" ); } } level.objectiveHintPrepareHQ = &"MP_CONTROL_HQ"; level.objectiveHintCaptureHQ = &"MP_CAPTURE_HQ"; level.objectiveHintDestroyHQ = &"MP_DESTROY_HQ"; level.objectiveHintDefendHQ = &"MP_DEFEND_HQ"; if ( level.kothMode ) level.objectiveHintDestroyHQ = level.objectiveHintCaptureHQ; if ( level.hqSpawnTime ) updateObjectiveHintMessage( level.objectiveHintPrepareHQ ); else updateObjectiveHintMessage( level.objectiveHintCaptureHQ ); 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::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 SetupRadios(); thread HQMainLoop(); } function spawn_first_radio(delay) { // pick next HQ object if ( level.randomHQSpawn == 1 ) { level.radio = GetNextRadioFromQueue(); } else { level.radio = GetFirstRadio(); } /#print("radio spawned: ("+level.radio.trigOrigin[0]+","+level.radio.trigOrigin[1]+","+level.radio.trigOrigin[2]+")");#/ level.radio spawning::enable_influencers(true); return; } function spawn_next_radio() { // pick next HQ object if ( level.randomHQSpawn != 0 ) { level.radio = GetNextRadioFromQueue(); } else { level.radio = GetNextRadio(); } /#print("radio spawned: ("+level.radio.trigOrigin[0]+","+level.radio.trigOrigin[1]+","+level.radio.trigOrigin[2]+")");#/ level.radio spawning::enable_influencers(true); return; } function HQMainLoop() { level endon("game_ended"); level.hqRevealTime = -100000; hqSpawningInStr = &"MP_HQ_AVAILABLE_IN"; if ( level.kothMode ) { hqDestroyedInFriendlyStr = &"MP_HQ_DESPAWN_IN"; hqDestroyedInEnemyStr = &"MP_HQ_DESPAWN_IN"; } else { hqDestroyedInFriendlyStr = &"MP_HQ_REINFORCEMENTS_IN"; hqDestroyedInEnemyStr = &"MP_HQ_DESPAWN_IN"; } spawn_first_radio(); objective_name = istring("objective"); while ( level.inPrematchPeriod ) {wait(.05);}; wait 5; timerDisplay = []; foreach ( team in level.teams ) { timerDisplay[team] = hud::createServerTimer( "objective", 1.4, team ); timerDisplay[team] hud::setGamemodeInfoPoint(); timerDisplay[team].label = hqSpawningInStr; timerDisplay[team].font = "small"; timerDisplay[team].alpha = 0; timerDisplay[team].archived = false; timerDisplay[team].hideWhenInMenu = true; timerDisplay[team].hideWhenInKillcam = true; timerDisplay[team].showplayerteamhudelemtospectator = true; thread hideTimerDisplayOnGameEnd( timerDisplay[team] ); } // locationObjID = gameobjects::get_next_obj_id(); // objective_add( locationObjID, "invisible", (0,0,0) ); while( 1 ) { iPrintLn( &"MP_HQ_REVEALED" ); sound::play_on_players( "mp_suitcase_pickup" ); globallogic_audio::leader_dialog( "hq_located" ); // globallogic_audio::leader_dialog( "move_to_new" ); level.radio.gameobject gameobjects::set_model_visibility( true ); level.hqRevealTime = gettime(); rcbombs = GetEntArray( "rcbomb","targetname" ); radius = 75; for( index = 0; index < rcbombs.size; index ++ ) { if( DistanceSquared( rcbombs[index], level.radio.origin ) < radius * radius ) { rcbombs[index] notify( "rcbomb_shutdown" ); } } if ( level.hqSpawnTime ) { // nextObjPoint = objpoints::create( "objpoint_next_hq", level.radio.origin + level.iconoffset, "all", "waypoint_targetneutral" ); // nextObjPoint setWayPoint( true, "waypoint_targetneutral" ); // objective_position( locationObjID, level.radio.trigorigin ); // objective_icon( locationObjID, "waypoint_targetneutral" ); // objective_state( locationObjID, "active" ); level.radio.gameobject gameobjects::set_visible_team( "any" ); level.radio.gameobject gameobjects::set_flags( 1 ); updateObjectiveHintMessage( level.objectiveHintPrepareHQ ); foreach( team in level.teams ) { timerDisplay[team].label = hqSpawningInStr; timerDisplay[team] setTimer( level.hqSpawnTime ); timerDisplay[team].alpha = 1; } wait level.hqSpawnTime; // objpoints::delete( nextObjPoint ); // objective_state( locationObjID, "invisible" ); level.radio.gameobject gameobjects::set_flags( 0 ); globallogic_audio::leader_dialog( "hq_online" ); } foreach( team in level.teams ) { timerDisplay[team].alpha = 0; } waittillframeend; globallogic_audio::leader_dialog( "obj_capture" ); updateObjectiveHintMessage( level.objectiveHintCaptureHQ ); sound::play_on_players( "mpl_hq_cap_us" ); level.radio.gameobject gameobjects::enable_object(); level.radio.gameobject.onUpdateUseRate =&onUpdateUseRate; level.radio.gameobject gameobjects::allow_use( "any" ); level.radio.gameobject gameobjects::set_use_time( level.captureTime ); level.radio.gameobject gameobjects::set_use_text( &"MP_CAPTURING_HQ" ); //objective_icon( locationObjID, "compass_waypoint_captureneutral" ); // level.radio.gameobject gameobjects::set_2d_icon( "enemy", "compass_waypoint_captureneutral" ); // level.radio.gameobject gameobjects::set_3d_icon( "enemy", "waypoint_captureneutral" ); level.radio.gameobject gameobjects::set_visible_team( "any" ); level.radio.gameobject gameobjects::set_model_visibility( true ); level.radio.gameobject.onUse =&onRadioCapture; level.radio.gameobject.onBeginUse =&onBeginUse; level.radio.gameobject.onEndUse =&onEndUse; level waittill( "hq_captured" ); ownerTeam = level.radio.gameobject gameobjects::get_owner_team(); if ( level.hqAutoDestroyTime ) { thread DestroyHQAfterTime( level.hqAutoDestroyTime, ownerTeam ); foreach( team in level.teams ) { timerDisplay[team] setTimer( level.hqAutoDestroyTime ); } } else { level.hqDestroyedByTimer = false; } while( 1 ) { ownerTeam = level.radio.gameobject gameobjects::get_owner_team(); foreach( team in level.teams ) { updateObjectiveHintMessages( ownerTeam, level.objectiveHintDefendHQ, level.objectiveHintDestroyHQ ); } level.radio.gameobject gameobjects::allow_use( "enemy" ); // level.radio.gameobject gameobjects::set_2d_icon( "friendly", "compass_waypoint_defend" ); // level.radio.gameobject gameobjects::set_3d_icon( "friendly", "waypoint_defend" ); // level.radio.gameobject gameobjects::set_2d_icon( "enemy", "compass_waypoint_capture" ); // level.radio.gameobject gameobjects::set_3d_icon( "enemy", "waypoint_capture" ); if ( !level.kothMode ) level.radio.gameobject gameobjects::set_use_text( &"MP_DESTROYING_HQ" ); level.radio.gameobject.onUse =&onRadioDestroy; if ( level.hqAutoDestroyTime ) { foreach( team in level.teams ) { if ( team == ownerTeam ) timerDisplay[team].label = hqDestroyedInFriendlyStr; else timerDisplay[team].label = hqDestroyedInEnemyStr; timerDisplay[team].alpha = 1; } } level thread dropAllAroundHQ(); level waittill( "hq_destroyed", destroy_team ); level.radio spawning::enable_influencers(false); if ( !level.kothMode || level.hqDestroyedByTimer ) break; thread forceSpawnTeam( ownerTeam ); if ( isdefined( destroy_team ) ) { level.radio.gameobject gameobjects::set_owner_team( destroy_team ); } } level.radio.gameobject gameobjects::disable_object(); level.radio.gameobject gameobjects::allow_use( "none" ); level.radio.gameobject gameobjects::set_owner_team( "neutral" ); level.radio.gameobject gameobjects::set_model_visibility( false ); level notify("hq_reset"); foreach( team in level.teams ) { timerDisplay[team].alpha = 0; } spawn_next_radio(); wait .05; thread forceSpawnTeam( ownerTeam ); wait 3.0; } } function hideTimerDisplayOnGameEnd( timerDisplay ) { level waittill("game_ended"); timerDisplay.alpha = 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 onBeginUse( player ) { ownerTeam = self gameobjects::get_owner_team(); if ( ownerTeam == "neutral" ) { // self.objPoints[player.pers["team"]] thread objpoints::start_flashing(); player thread battlechatter::gametype_specific_battle_chatter( "hq_protect", player.pers["team"] ); } else { // foreach( team in level.teams ) // { // self.objPoints[team] thread objpoints::start_flashing(); // } player thread battlechatter::gametype_specific_battle_chatter( "hq_attack", player.pers["team"] ); } } function onEndUse( team, player, success ) { // foreach( team in level.teams ) // { // self.objPoints[team] thread objpoints::stop_flashing(); // } player notify( "event_ended" ); } function onRadioCapture( player ) { capture_team = player.pers["team"]; /#print( "radio captured" );#/ string = &"MP_HQ_CAPTURED_BY"; level.useStartSpawns = false; thread give_capture_credit( self.touchList[capture_team], string ); oldTeam = gameobjects::get_owner_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 ) { thread util::printOnTeamArg( &"MP_HQ_CAPTURED_BY", team, player ); globallogic_audio::leader_dialog( "hq_secured", team ); thread sound::play_on_players( "mp_war_objective_taken", team ); } else { thread util::printOnTeam( &"MP_HQ_CAPTURED_BY_ENEMY", team ); globallogic_audio::leader_dialog( "hq_enemy_captured", team ); thread sound::play_on_players( "mp_war_objective_lost", team ); } } level thread awardHQPoints( capture_team ); level notify( "hq_captured" ); player notify( "event_ended" ); } function give_capture_credit( touchList, string ) { time = getTime(); wait .05; util::WaitTillSlowProcessAllowed(); players = getArrayKeys( touchList ); for ( i = 0; i < players.size; i++ ) { player_from_touchlist = touchList[players[i]].player; player_from_touchlist challenges::capturedObjective( time, self ); // TODO: fix this when this game mode can be played, need to pass in the "trigger" not self which is the useObj scoreevents::processScoreEvent( "hq_secure", player_from_touchlist ); player_from_touchlist RecordGameEvent("capture"); level thread popups::DisplayTeamMessageToAll( string, player_from_touchlist ); if( isdefined(player_from_touchlist.pers["captures"]) ) { player_from_touchlist.pers["captures"]++; player_from_touchlist.captures = player_from_touchlist.pers["captures"]; } demo::bookmark( "event", gettime(), player_from_touchlist ); player_from_touchlist AddPlayerStatWithGameType( "CAPTURES", 1 ); } } function dropAllToGround( origin, radius, stickyObjectRadius ) { PhysicsExplosionSphere( origin, radius, radius, 0 ); {wait(.05);}; weapons::drop_all_to_ground( origin, radius ); // grenades are now done in code when an entity they were on gets deleted // weapons::drop_grenades_to_ground( origin, radius ); supplydrop::dropCratesToGround( origin, radius ); level notify( "drop_objects_to_ground", origin, stickyObjectRadius ); } function dropAllAroundHQ( radio ) { origin = level.radio.origin; level waittill( "hq_reset" ); dropAllToGround( origin, 100, 50 ); } function onRadioDestroy( firstPlayer ) { destroyed_team = firstPlayer.pers["team"]; touchList = self.touchList[destroyed_team]; touchListKeys = getArrayKeys( touchList ); foreach( index in touchListKeys ) { player = touchList[index].player; /#print( "radio destroyed" );#/ scoreevents::processScoreEvent( "hq_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_HQ_CAPTURED_BY"; otherTeamMessage = &"MP_HQ_CAPTURED_BY_ENEMY"; } level thread popups::DisplayTeamMessageToAll( destroyTeamMessage, player ); foreach( team in level.teams ) { if ( team == destroyed_team ) { thread util::printOnTeamArg( destroyTeamMessage, team, player ); globallogic_audio::leader_dialog( "hq_secured", team ); } else { thread util::printOnTeam( otherTeamMessage, team ); globallogic_audio::leader_dialog( "hq_enemy_destroyed", team ); } } level notify( "hq_destroyed", destroyed_team ); if ( level.kothMode ) level thread awardHQPoints( destroyed_team ); player notify( "event_ended" ); } function DestroyHQAfterTime( time, ownerTeam ) { level endon( "game_ended" ); level endon( "hq_reset" ); level.hqDestroyTime = gettime() + time * 1000; level.hqDestroyedByTimer = false; wait time; globallogic_audio::leader_dialog( "hq_offline" ); level.hqDestroyedByTimer = true; checkPlayerCount( ownerTeam ); level notify( "hq_destroyed" ); } function checkPlayerCount( ownerTeam ) { lastPlayerAlive = undefined; players = level.players; for ( i = 0 ; i < players.size ; i++ ) { if ( players[i].team != ownerTeam ) continue; if ( IsAlive( players[i] ) ) { if ( isdefined( lastPlayerAlive ) ) { return; // more than one player alive } lastPlayerAlive = players[i]; } } if ( isdefined ( lastPlayerAlive ) ) { scoreevents::processScoreEvent( "defend_hq_last_man_alive", lastPlayerAlive ); } } function awardHQPoints( team ) { level endon( "game_ended" ); level endon( "hq_destroyed" ); level notify("awardHQPointsRunning"); level endon("awardHQPointsRunning"); seconds = 5; while ( !level.gameEnded ) { globallogic_score::giveTeamScoreForObjective( team, seconds ); for ( index = 0; index < level.players.size; index++ ) { player = level.players[index]; if ( player.pers["team"] == team ) { //scoreevents::processScoreEvent( "defend", player ); } } wait seconds; } } function koth_playerSpawnedCB() { self.lowerMessageOverride = undefined; } function CompareRadioIndexes( radio_a, radio_b ) { script_index_a = radio_a.script_index; script_index_b = radio_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 radio at " + radio_a.origin ); #/ return true; } if( isdefined(script_index_a) && !isdefined(script_index_b) ) { /# println( "KOTH: Missing script_index on radio at " + radio_b.origin ); #/ return false; } if( script_index_a > script_index_b ) { return true; } return false; } function getRadioArray() { radios = getentarray( "hq_hardpoint", "targetname" ); if( !isdefined( radios ) ) { return undefined; } swapped = true; n = radios.size; while ( swapped ) { swapped = false; for( i = 0 ; i < n-1 ; i++ ) { if( CompareRadioIndexes(radios[i], radios[i+1]) ) { temp = radios[i]; radios[i] = radios[i+1]; radios[i+1] = temp; swapped = true; } } n--; } return radios; } function SetupRadios() { maperrors = []; radios = getRadioArray(); if ( radios.size < 2 ) { maperrors[maperrors.size] = "There are not at least 2 entities with targetname \"radio\""; } trigs = getentarray("radiotrigger", "targetname"); for ( i = 0; i < radios.size; i++ ) { errored = false; radio = radios[i]; radio.trig = undefined; for ( j = 0; j < trigs.size; j++ ) { if ( radio istouching( trigs[j] ) ) { if ( isdefined( radio.trig ) ) { maperrors[maperrors.size] = "Radio at " + radio.origin + " is touching more than one \"radiotrigger\" trigger"; errored = true; break; } radio.trig = trigs[j]; break; } } if ( !isdefined( radio.trig ) ) { if ( !errored ) { maperrors[maperrors.size] = "Radio at " + radio.origin + " is not inside any \"radiotrigger\" trigger"; continue; } // possible fallback (has been tested) //radio.trig = spawn( "trigger_radius", radio.origin, 0, 128, 128 ); //errored = false; } assert( !errored ); radio.trigorigin = radio.trig.origin; visuals = []; visuals[0] = radio; otherVisuals = getEntArray( radio.target, "targetname" ); for ( j = 0; j < otherVisuals.size; j++ ) { visuals[visuals.size] = otherVisuals[j]; } objective_name = istring("objective"); radio setUpNodes(); radio.gameObject = gameobjects::create_use_object( "neutral", radio.trig, visuals, (radio.origin - radio.trigorigin), objective_name ); radio.gameObject gameobjects::disable_object(); radio.gameObject gameobjects::set_model_visibility( false ); radio.trig.useObj = radio.gameObject; radio setUpNearbySpawns(); radio createRadioSpawnInfluencer(); } 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.radios = radios; level.prevradio = undefined; level.prevradio2 = undefined; return true; } 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 setUpNodes() { self.points = []; temp = spawn( "script_model", ( 0, 0, 0 ) ); maxs = self.trig GetPointInBounds( 1, 1, 1 ); self.node_radius = Distance( self.trig.origin, maxs ); points = util::PositionQuery_PointArray( self.trig.origin, 0, self.node_radius, 70, 128 ); foreach( point in points ) { temp.origin = point; if ( temp IsTouching( self.trig ) ) { self.points[ self.points.size ] = point; } } assert( self.points.size ); temp delete(); } function GetFirstRadio() { radio = level.radios[ 0 ]; // old linear and "random" systems level.prevradio2 = level.prevradio; level.prevradio = radio; level.prevRadioIndex = 0; // new shuffled system ShuffleRadios(); ArrayRemoveValue( level.radioSpawnQueue, radio ); return radio; } function GetNextRadio() { nextRadioIndex = (level.prevRadioIndex + 1) % level.radios.size; radio = level.radios[ nextRadioIndex ]; level.prevradio2 = level.prevradio; level.prevradio = radio; level.prevRadioIndex = nextRadioIndex; return radio; } function PickRandomRadioToSpawn() { level.prevRadioIndex = randomint( level.radios.size); radio = level.radios[ level.prevRadioIndex ]; level.prevradio2 = level.prevradio; level.prevradio = radio; return radio; } function ShuffleRadios() { level.radioSpawnQueue = []; spawnQueue = ArrayCopy(level.radios); total_left = spawnQueue.size; while( total_left > 0 ) { index = randomint( total_left ); valid_radios = 0; for( radio = 0; radio < level.radios.size; radio++ ) { if ( !isdefined(spawnQueue[radio]) ) continue; if ( valid_radios == index ) { // dont allow the last radio from the previous shuffle to be put first in the next if ( level.radioSpawnQueue.size == 0 && isdefined( level.radio ) && level.radio == spawnQueue[radio] ) continue; level.radioSpawnQueue[level.radioSpawnQueue.size] = spawnQueue[radio]; spawnQueue[radio] = undefined; break; } valid_radios++; } total_left--; } } // shuffled picking function GetNextRadioFromQueue() { if ( level.radioSpawnQueue.size == 0 ) ShuffleRadios(); assert( level.radioSpawnQueue.size > 0 ); next_radio = level.radioSpawnQueue[0]; ArrayRemoveIndex( level.radioSpawnQueue, 0 ); return next_radio; } 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 PickRadioToSpawn() { // find average of positions of each team // (medians would be better, to get rid of outliers...) // and find the radio 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 ) { radio = level.radios[ randomint( level.radios.size) ]; while ( isdefined( level.prevradio ) && radio == level.prevradio ) // so lazy radio = level.radios[ randomint( level.radios.size) ]; level.prevradio2 = level.prevradio; level.prevradio = radio; return radio; } foreach( team in level.teams ) { if ( num[team] == 0 ) { avgpos[team] = undefined; } else { avgpos[team] = avgpos[team] / num[team]; } } bestradio = undefined; lowestcost = undefined; for ( i = 0; i < level.radios.size; i++ ) { radio = level.radios[i]; // (purposefully using distance instead of distanceSquared) cost = GetPointCost( avgpos, radio.origin ); if ( isdefined( level.prevradio ) && radio == level.prevradio ) { continue; } if ( isdefined( level.prevradio2 ) && radio == level.prevradio2 ) { if ( level.radios.size > 2 ) continue; else cost += 512 * 512; } if ( !isdefined( lowestcost ) || cost < lowestcost ) { lowestcost = cost; bestradio = radio; } } assert( isdefined( bestradio ) ); level.prevradio2 = level.prevradio; level.prevradio = bestradio; return bestradio; } function onRoundSwitch() { game["switchedsides"] = !game["switchedsides"]; } function onPlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration ) { if ( !isPlayer( attacker ) || (!self.touchTriggers.size && !attacker.touchTriggers.size) || attacker.pers["team"] == self.pers["team"] ) return; medalGiven = false; scoreEventProcessed = false; if ( attacker.touchTriggers.size ) { triggerIds = getArrayKeys( attacker.touchTriggers ); ownerTeam = attacker.touchTriggers[triggerIds[0]].useObj.ownerTeam; team = attacker.pers["team"]; if ( team == ownerTeam || ownerTeam == "neutral" ) { if ( !medalGiven ) { if( isdefined(attacker.pers["defends"]) ) { attacker.pers["defends"]++; attacker.defends = attacker.pers["defends"]; } attacker medals::defenseGlobalCount(); medalGiven = true; attacker thread challenges::killedBaseDefender( attacker.touchTriggers[triggerIds[0]] ); attacker RecordGameEvent("return"); } attacker challenges::killedZoneAttacker( weapon ); if (team != ownerTeam ) { scoreevents::processScoreEvent( "kill_enemy_while_capping_hq", attacker, undefined, weapon ); } else { scoreevents::processScoreEvent( "killed_attacker", attacker, undefined, weapon ); } self RecordKillModifier("assaulting"); scoreEventProcessed = true; } else { if ( !medalGiven ) { attacker medals::offenseGlobalCount(); medalGiven = true; attacker thread challenges::killedBaseOffender( attacker.touchTriggers[triggerIds[0]], weapon ); } scoreevents::processScoreEvent( "kill_enemy_while_capping_hq", attacker, undefined, weapon ); self RecordKillModifier("defending"); scoreEventProcessed = true; } } if ( self.touchTriggers.size ) { triggerIds = getArrayKeys( self.touchTriggers ); ownerTeam = self.touchTriggers[triggerIds[0]].useObj.ownerTeam; team = self.pers["team"]; if ( team == ownerTeam ) { if ( !medalGiven ) { attacker medals::offenseGlobalCount(); attacker thread challenges::killedBaseOffender( self.touchTriggers[triggerIds[0]], weapon ); medalGiven = true; } if ( !scoreEventProcessed ) { scoreevents::processScoreEvent( "killed_defender", 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( self.touchTriggers[triggerIds[0]] ); attacker RecordGameEvent("return"); } if ( !scoreEventProcessed ) { attacker challenges::killedZoneAttacker( weapon ); scoreevents::processScoreEvent( "killed_attacker", attacker, undefined, weapon ); self RecordKillModifier("assaulting"); scoreEventProcessed = true; } } if ( scoreEventProcessed == true ) { attacker killWhileContesting( self.touchTriggers[triggerIds[0]].useObj ); } } } function killWhileContesting( radio ) { self notify( "killWhileContesting" ); self endon( "killWhileContesting" ); self endon( "disconnect" ); killTime = getTime(); playerteam = self.pers["team"]; if ( !isdefined ( self.clearEnemyCount ) ) { self.clearEnemyCount = 0; } self.clearEnemyCount++; radio waittill( "state_change" ); if (playerteam != self.pers["team"] || ( isdefined( self.spawntime ) && ( killTime < self.spawntime ) ) ) { self.clearEnemyCount = 0; return; } if ( radio.ownerTeam != playerteam && radio.ownerTeam != "neutral" ) { 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.radios.size; i++ ) { level.radios[i].gameobject gameobjects::allow_use( "none" ); } } function createRadioSpawnInfluencer() { // this affects both teams self spawning::create_influencer( "hq_large", self.gameobject.curOrigin, 0 ); self spawning::create_influencer( "hq_small", self.gameobject.curOrigin, 0 ); // turn it off for now self spawning::enable_influencers(false); } function onUpdateUseRate() { if ( !isdefined( self.currentContenderCount ) ) { self.currentContenderCount = 0; } numOthers = gameobjects::get_num_touching_except_team( self.ownerteam ); // 0 numOwners = self.numTouching[self.ownerteam]; // 1 previousState = self.currentContenderCount; if ( numOthers == 0 && numOwners == 0 ) { self.currentContenderCount = 0; } else { if ( self.ownerteam == "neutral" ) { numOtherClaim = gameobjects::get_num_touching_except_team( self.claimteam ); //1 if ( numOtherClaim > 0 ) { self.currentContenderCount = 2; } else { self.currentContenderCount = 1; } } else { if ( numOthers > 0 ) { self.currentContenderCount = 1; } else { self.currentContenderCount = 0; } } } if ( self.currentContenderCount != previousState ) { self notify( "state_change" ); } }