2023-04-13 17:30:38 +02:00

1421 lines
39 KiB
Plaintext

#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" );
}
}