#using scripts\codescripts\struct; #using scripts\shared\ai_shared; #using scripts\shared\array_shared; #using scripts\shared\flag_shared; #using scripts\shared\system_shared; #using scripts\shared\trigger_shared; #using scripts\shared\spawner_shared; #using scripts\shared\util_shared; #namespace colors; /* Color coded AI travel system A colorCode is a color( red, blue, yellow, cyan, green, purple or orange ) and a #. When a trigger and AI and node are color grouped in Radiant, they get a unique color and #. When when a color coded trigger is hit, that colorCode is "fired" and any AI that are in colorCoded mode get their goalnode set. AI can be forced to a color generically. For example if an AI is forced to the color "red" and a trigger fires off "red15", the AI will go to that node even if the AI doesn't have script_color_allies( or axis ) "red15". This is mainly for friendlies. AI can also have their script_careful variable set, which prevents them from advancing to a node or a volume if an enemy is occupying it. They will move up if the zone is cleared. Volumes can also be tagged with a code and a number like r6. So if an enemy is present in this volume, no AI will run to any node tagged r6. */ function autoexec __init__sytem__() { system::register("colors",&__init__,&__main__,undefined); } function __init__() { nodes = GetAllNodes(); // friendly spawner global stuff level flag::init( "player_looks_away_from_spawner" ); level flag::init( "friendly_spawner_locked" ); // can be turned on and off to control friendly_respawn_trigger level flag::init( "respawn_friendlies" ); level.arrays_of_colorCoded_nodes = []; level.arrays_of_colorCoded_nodes[ "axis" ] = []; level.arrays_of_colorCoded_nodes[ "allies" ] = []; level.colorCoded_volumes = []; level.colorCoded_volumes[ "axis" ] = []; level.colorCoded_volumes[ "allies" ] = []; volumes = GetEntArray( "info_volume", "classname" ); // go through all the nodes and if they have color codes then add them too for( i=0;i 0 ) { thread debug_colornodes(); } wait .05; } } function get_team_substr() { if( self.team == "allies" ) { if( !isdefined( self.node.script_color_allies_old ) ) { return; } return self.node.script_color_allies_old; } if( self.team == "axis" ) { if( !isdefined( self.node.script_color_axis_old ) ) { return; } return self.node.script_color_axis_old; } } function try_to_draw_line_to_node() { if( !isdefined( self.node ) ) { return; } if( !isdefined( self.script_forceColor ) ) { return; } substr = get_team_substr(); if( !isdefined( substr ) ) { return; } if( !issubstr( substr, self.script_forceColor ) ) { return; } RecordLine( self.origin +( 0, 0, 25 ), self.node.origin, _get_debug_color( self.script_forceColor ), "Script", self ); line( self.origin +( 0, 0, 25 ), self.node.origin, _get_debug_color( self.script_forceColor ) ); } function _get_debug_color( str_color ) { switch( str_color ) { case "r": case "red": return ( 1, 0, 0 ); break; case "g": case "green": return ( 0, 1, 0 ); break; case "b": case "blue": return ( 0, 0, 1 ); break; case "y": case "yellow": return ( 1, 1, 0 ); break; case "o": case "orange": return ( 1, .5, 0 ); break; case "c": case "cyan": return ( 0, 1, 1 ); break; case "p": case "purple": return ( 1, 0, 1 ); break; default: PrintLn( "Debug color " + str_color + " not set in debug::_get_debug_color(). Please add this color." ); return ( 0, 0, 0 ); break; } } function debug_colornodes() { array = []; array[ "axis" ] = []; array[ "allies" ] = []; array[ "neutral" ] = []; foreach ( ai in GetAIArray() ) { if ( !isdefined( ai.currentColorCode ) ) { continue; } array[ ai.team ][ ai.currentColorCode ] = true; color = ( 1, 1, 1 ); if ( isdefined( ai.script_forceColor ) ) { color = _get_debug_color( ai.script_forceColor ); } RecordEntText( ai.currentColorCode, ai, color, "Script"); Print3d( ai.origin + ( 0, 0, 25 ), ai.currentColorCode, color, 1, 0.7 ); // axis don't do forcecolor behavior, they do follow the leader for force color if ( ai.team == "axis" ) { continue; } ai try_to_draw_line_to_node(); } draw_colorNodes( array, "allies" ); draw_colorNodes( array, "axis" ); } function draw_colorNodes( array, team ) { keys = GetArrayKeys( array[ team ] ); for ( i = 0; i < keys.size; i++ ) { color = _get_debug_color( GetSubStr( keys[ i ], 0, 1 ) ); if ( isdefined( level.colorNodes_debug_array[ team ][ keys[ i ] ] ) ) { a_team_nodes = level.colorNodes_debug_array[ team ][ keys[ i ] ]; for ( p = 0; p < a_team_nodes.size; p++ ) { // there can be multiple colors on the nodes, avoid drawing them on top of each other. Print3d( a_team_nodes[ p ].origin, "N-" + keys[ i ], color, 1, 0.7 ); if ( GetDvarString( "debug_colornodes" ) == "2" && isdefined( a_team_nodes[ p ].script_color_allies_old ) ) { if ( isdefined( a_team_nodes[ p ].color_user ) && IsAlive( a_team_nodes[ p ].color_user ) && isdefined( a_team_nodes[ p ].color_user.script_forceColor ) ) { print3d( a_team_nodes[ p ].origin + ( 0, 0, -5 ), "N-" + a_team_nodes[ p ].script_color_allies_old, _get_debug_color( a_team_nodes[ p ].color_user.script_forceColor ), 0.5, 0.4 ); } else { print3d( a_team_nodes[ p ].origin + ( 0, 0, -5 ), "N-" + a_team_nodes[ p ].script_color_allies_old, color, 0.5, 0.4 ); } } } } } } function debugColorFriendlies() { level.debug_color_friendlies = []; level.debug_color_huds = []; level thread debugColorFriendliesToggleWatch(); for( ;; ) { level waittill( "updated_color_friendlies" ); draw_color_friendlies(); } } function debugColorFriendliesToggleWatch() { just_turned_on = false; just_turned_off = false; while( 1 ) { if( GetDvarString( "debug_colornodes" ) == "1" && !just_turned_on ) { just_turned_on = true; just_turned_off = false; draw_color_friendlies(); } if( GetDvarString( "debug_colornodes" ) != "1" && !just_turned_off ) { just_turned_off = true; just_turned_on = false; draw_color_friendlies(); } wait( 0.25 ); } } function get_script_palette() { rgb = []; rgb[ "r" ] = ( 1, 0, 0 ); rgb[ "o" ] = ( 1, .5, 0 ); rgb[ "y" ] = ( 1, 1, 0 ); rgb[ "g" ] = ( 0, 1, 0 ); rgb[ "c" ] = ( 0, 1, 1 ); rgb[ "b" ] = ( 0, 0, 1 ); rgb[ "p" ] = ( 1, 0, 1 ); return rgb; } function draw_color_friendlies() { level endon( "updated_color_friendlies" ); keys = getarraykeys( level.debug_color_friendlies ); colored_friendlies = []; colors = []; colors[ colors.size ] = "r"; colors[ colors.size ] = "o"; colors[ colors.size ] = "y"; colors[ colors.size ] = "g"; colors[ colors.size ] = "c"; colors[ colors.size ] = "b"; colors[ colors.size ] = "p"; rgb = colors::get_script_palette(); for( i=0; i < colors.size; i++ ) { colored_friendlies[ colors[ i ] ] = 0; } for( i=0; i < keys.size; i++ ) { color = level.debug_color_friendlies[ keys[ i ] ]; colored_friendlies[ color ]++; } for( i=0; i < level.debug_color_huds.size; i++ ) { level.debug_color_huds[ i ] destroy(); } level.debug_color_huds = []; if( GetDvarString( "debug_colornodes" ) != "1" ) { return; } const x = 15; y = 365; const offset_y = 25; for( i=0; i < colors.size; i++ ) { if( colored_friendlies[ colors[ i ] ] <= 0 ) { continue; } for( p=0; p < colored_friendlies[ colors[ i ] ]; p++ ) { overlay = newHudElem(); overlay.x = x + 25*p; overlay.y = y; overlay setshader( "white", 16, 16 ); overlay.alignX = "left"; overlay.alignY = "bottom"; overlay.alpha = 1; overlay.color = rgb[ colors[ i ] ]; level.debug_color_huds[ level.debug_color_huds.size ] = overlay; } y += offset_y; } } #/ function player_init_color_grouping( ) { thread player_color_node(); } function convert_color_to_short_string() { // shorten the forcecolors string to a single letter self.script_forceColor = level.colorCheckList[ self.script_forceColor ]; } function goto_current_ColorIndex() { if( !IsDefined( self.currentColorCode ) ) { return; } nodes = level.arrays_of_colorCoded_nodes[ self.team ][ self.currentColorCode ]; if ( !isdefined( nodes ) ) nodes = []; else if ( !IsArray( nodes ) ) nodes = array( nodes ); nodes[nodes.size]=level.colorCoded_volumes[ self.team ][ self.currentColorCode ];; self left_color_node(); // can be deleted/killed during left_color_node if( !isalive( self ) ) { return; } // can lose color during left_color_node if( !has_color() ) { return; } for( i=0; i n_dot_check ) { return true; } else if ( IsVec( target ) ) { a_trace = BulletTrace( v_eye, target, false, player ); if ( a_trace["fraction"] == 1 ) { return true; } } else if ( target SightConeTrace( v_eye, player ) != 0 ) { return true; } } return false; } // Teleports a hero once it's safe to do so (no telefragging, no player visibility)." // // Will bail if one of the teleport points is further from the goal than the hero. // function hero_catch_up_teleport( s_teleport, n_min_dist_from_player = 400.0, b_disable_colors = false, func_callback ) { self notify( "_hero_catch_up_teleport_" ); self endon( "_hero_catch_up_teleport_" ); self endon( "stop_hero_catch_up_teleport" ); const n_telefrag_radius = 16.0; const n_teleport_cooldown_ms = 2000; n_min_player_dist_sq = n_min_dist_from_player * n_min_dist_from_player; self endon( "death" ); a_teleport = s_teleport; if ( !isdefined( a_teleport ) ) a_teleport = []; else if ( !IsArray( a_teleport ) ) a_teleport = array( a_teleport );; a_teleport = array::randomize( a_teleport ); while ( true ) { b_player_nearby = false; foreach( player in level.players ) { if ( DistanceSquared( player.origin, self.origin ) < n_min_player_dist_sq ) { b_player_nearby = true; break; } } if ( !b_player_nearby ) { n_ai_dist = -1.0; if ( isdefined( self.goal ) ) { n_ai_dist = self CalcPathLength( self.node ); } foreach( s in a_teleport ) { if ( PositionWouldTelefrag( s.origin ) ) { continue; } if ( isdefined( s.teleport_cooldown ) ) { if ( GetTime() < s.teleport_cooldown ) { continue; } } // Don't teleport on top of heroes. // if ( self.team == "allies" && isdefined( level.heroes ) ) { hit_hero = ArrayGetClosest( s.origin, level.heroes, n_telefrag_radius ); if ( isdefined( hit_hero ) ) { continue; } } // Distance from teleport location to the goal node. if ( isdefined( self.node ) && n_ai_dist >= 0.0 ) { // If the teleport position would put us further from our goal, we're close enough. n_teleport_distance = PathDistance( s.origin, self.node.origin ); if ( n_teleport_distance > n_ai_dist ) { return; } } // Don't teleport if someone's looking at them. // if ( is_target_visible( self ) ) { continue; } // Don't teleport if someone's looking at the destination. // if ( is_target_visible( s.origin ) ) { continue; } if ( isdefined( self.script_forceColor ) || b_disable_colors ) { if ( self ForceTeleport( s.origin, s.angles, true, true ) ) { self PathMode( "move allowed" ); s.teleport_cooldown = GetTime() + n_teleport_cooldown_ms; self notify( "hero_catch_up_teleport" ); if ( b_disable_colors ) { disable(); } else { self colors::set_force_color( self.script_forceColor ); } if ( isdefined( func_callback ) ) { self [[ func_callback ]](); } return; } } } } else { return;//A player is close, no need to keep checking } // Try again soon. wait 0.5; } } function activate_color_trigger_internal( colorCodes, colors, team, colorCodesByColorIndex ) { // remove all the dead from any colors this trigger effects // a trigger should never effect the same color twice for( i = 0; i < colorCodes.size; i++ ) { if( !IsDefined( level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ] ) ) { continue; } // remove deleted spawners ArrayRemoveValue( level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ], undefined ); // set the .currentColorCode on each appropriate spawner for( p = 0; p < level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ].size; p++ ) { level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ][ p ].currentColorCode = colorCodes[ i ]; } } for( i = 0; i < colors.size; i++ ) { // remove the dead from the color forced ai level.arrays_of_colorForced_ai[ team ][ colors[ i ] ] = array::remove_dead( level.arrays_of_colorForced_ai[ team ][ colors[ i ] ] ); // set the last color forced so we can compare it with current when we tell guys to go to nodes, // so they can prefer new nodes over old ones, so they move up level.lastColorForced[ team ][ colors[ i ] ] = level.currentColorForced[ team ][ colors[ i ] ]; // set the destination of the color forced spawners level.currentColorForced[ team ][ colors[ i ] ] = colorCodesByColorIndex[ colors[ i ] ]; /# assert( IsDefined( level.arrays_of_colorCoded_nodes[ team ][ level.currentColorForced[ team ][ colors[ i ] ] ] ) || IsDefined( level.colorCoded_volumes[ team ][ level.currentColorForced[ team ][ colors[ i ] ] ] ), "Trigger tried to set colorCode " + colors[ i ] + " but there are no nodes for " + team + " that use that color combo." ); #/ } ai_array = []; for ( i = 0; i < colorCodes.size; i++ ) { // no need to run this again if it's still the current forced color if ( same_color_code_as_last_time( team, colors[ i ] ) ) { continue; } colorCode = colorCodes[ i ]; if ( !IsDefined( level.arrays_of_colorCoded_ai[ team ][ colorCode ] ) ) { continue; } ai_array[ colorCode ] = issue_leave_node_order_to_ai_and_get_ai( colorCode, colors[ i ], team ); if ( isdefined( self.a_s_hero_catch_up ) && ai_array.size > 0 ) { if( isdefined( ai_array[ colorCode ] ) ) { for ( j = 0; j < ai_array[ colorCode ].size; j++ ) { ai = ai_array[ colorCode ][ j ]; if ( ( isdefined( ai.is_hero ) && ai.is_hero ) && IsDefined( ai.script_forceColor ) ) { ai thread hero_catch_up_teleport( self.a_s_hero_catch_up ); } } } } } for( i = 0; i < colorCodes.size; i++ ) { colorCode = colorCodes[ i ]; if ( !IsDefined( ai_array[ colorCode ] ) ) { continue; } // no need to run this again if it's still the current forced color if( same_color_code_as_last_time( team, colors[ i ] ) ) { continue; } if ( !IsDefined( level.arrays_of_colorCoded_ai[ team ][ colorCode ] ) ) { continue; } issue_color_order_to_ai( colorCode, colors[ i ], team, ai_array[ colorCode ] ); } } function same_color_code_as_last_time( team, color ) { if( !IsDefined( level.lastColorForced[ team ][ color ] ) ) { return false; } return level.lastColorForced[ team ][ color ] == level.currentColorForced[ team ][ color ]; } function process_cover_node_with_last_in_mind_allies( node, lastColor ) { // nodes that were in the last color order go at the end if( issubstr( node.script_color_allies, lastColor ) ) { self.cover_nodes_last[ self.cover_nodes_last.size ] = node; } else { self.cover_nodes_first[ self.cover_nodes_first.size ] = node; } } function process_cover_node_with_last_in_mind_axis( node, lastColor ) { // nodes that were in the last color order go at the end if( issubstr( node.script_color_axis, lastColor ) ) { self.cover_nodes_last[ self.cover_nodes_last.size ] = node; } else { self.cover_nodes_first[ self.cover_nodes_first.size ] = node; } } function process_cover_node( node, null ) { self.cover_nodes_first[ self.cover_nodes_first.size ] = node; } function process_path_node( node, null ) { self.path_nodes[ self.path_nodes.size ] = node; } function prioritize_colorCoded_nodes( team, colorCode, color ) { nodes = level.arrays_of_colorCoded_nodes[ team ][ colorCode ]; // need a place to store the nodes externally so we can put the pathnodes in the back ent = spawnstruct(); ent.path_nodes = []; ent.cover_nodes_first = []; ent.cover_nodes_last = []; lastColorForced_exists = IsDefined( level.lastColorForced[ team ][ color ] ); // fills ent.path_nodes or .cover_nodes depending on node type for( i=0 ; i < nodes.size; i++ ) { node = nodes[ i ]; ent [ [ level.color_node_type_function[ node.type ][ lastColorForced_exists ][ team ] ] ]( node, level.lastColorForced[ team ][ color ] ); } ent.cover_nodes_first = array::randomize( ent.cover_nodes_first ); nodes = ent.cover_nodes_first; // put the path nodes at the end of the array so they're less favored for( i=0; i < ent.cover_nodes_last.size; i++ ) { nodes[ nodes.size ] = ent.cover_nodes_last[ i ]; } for( i=0; i < ent.path_nodes.size; i++ ) { nodes[ nodes.size ] = ent.path_nodes[ i ]; } level.arrays_of_colorCoded_nodes[ team ][ colorCode ] = nodes; } function get_prioritized_colorCoded_nodes( team, colorCode, color ) { if ( IsDefined( level.arrays_of_colorCoded_nodes[ team ][ colorCode ] ) ) return level.arrays_of_colorCoded_nodes[ team ][ colorCode ]; if ( IsDefined( level.colorCoded_volumes[ team ][ colorCode ] ) ) return level.colorCoded_volumes[ team ][ colorCode ]; } function issue_leave_node_order_to_ai_and_get_ai( colorCode, color, team ) { // remove dead from this specific colorCode level.arrays_of_colorCoded_ai[ team ][ colorCode ] = array::remove_dead( level.arrays_of_colorCoded_ai[ team ][ colorCode ] ); ai = level.arrays_of_colorCoded_ai[ team ][ colorCode ]; ai = ArrayCombine( ai, level.arrays_of_colorForced_ai[ team ][ color ], true, false ); newArray = []; for( i=0;i 0, "Tried to make guy with export " + self.export + " go to forcecolor " + self.script_forceColor + " but there are no nodes of that color enabled" ); for( i=0; i < nodes.size; i++ ) { if( !isalive( nodes[ i ].color_user ) ) { return nodes[ i ]; } } } function get_best_available_new_colored_node() { assert( self.team != "neutral" ); assert( IsDefined( self.script_forceColor ), "AI with export " + self.export + " lost his script_forcecolor.. somehow." ); colorCode = level.currentColorForced[ self.team ][ self.script_forceColor ]; nodes = get_prioritized_colorCoded_nodes( self.team, colorCode, self.script_forcecolor ); assert( nodes.size > 0, "Tried to make guy with export " + self.export + " go to forcecolor " + self.script_forceColor + " but there are no nodes of that color enabled" ); nodes = ArraySort( nodes, self.origin ); for( i=0; i < nodes.size; i++ ) { if( !isalive( nodes[ i ].color_user ) ) { return nodes[ i ]; } } } function process_stop_short_of_node( node ) { self endon( "stopScript" ); self endon( "death" ); if( IsDefined( self.node ) ) { return; } // first check to see if we're right near it if( distancesquared( node.origin, self.origin ) < 32*32 ) { reached_node_but_could_not_claim_it( node ); return; } // if we're far away, maybe somebody cut us off then took our node, now we're stuck in limbo // so wait one second, if we're still in stop script( ie no killanimscripts ) then push the guy // off the node currentTime = gettime(); wait_for_killanimscript_or_time( 1 ); newTime = gettime(); // did we break out of stop fast enough to indicate we continued moving? If not, then reclaim the node if( newTime - currentTime >= 1000 ) { reached_node_but_could_not_claim_it( node ); } } function wait_for_killanimscript_or_time( timer ) { self endon( "killanimscript" ); wait( timer ); } function reached_node_but_could_not_claim_it( node ) { ai = GetAIArray(); for( i=0;i 0) { spawn = undefined; for( ;; ) { if( !level flag::get( "respawn_friendlies" ) ) { if( !IsDefined( level.friendly_respawn_vision_checker_thread ) ) thread friendly_spawner_vision_checker(); // have to break if respawn_friendlies gets enabled because that disables the // fov check that toggles player_looks_away_from_spawner. for ( ;; ) { level flag::wait_till_any( array( "player_looks_away_from_spawner", "respawn_friendlies" ) ); level flag::wait_till_clear( "friendly_spawner_locked" ); if ( level flag::get( "player_looks_away_from_spawner" ) || level flag::get( "respawn_friendlies" ) ) { break; } } level flag::set( "friendly_spawner_locked" ); } spawner = get_color_spawner( classname, fromColor ); spawner.count = 1; level.friendly_spawners_types[friendly_spawners_type] = level.friendly_spawners_types[friendly_spawners_type] - 1; spawner util::script_wait(); spawn = spawner spawner::spawn(); if( spawner::spawn_failed( spawn ) ) { thread lock_spawner_for_awhile(); wait( 1 ); continue; } level notify( "reinforcement_spawned", spawn ); break; } // figure out which color the spawned guy should be for( ;; ) { if( !IsDefined( fromColor ) ) { break; } if( get_color_from_order( fromColor, level.current_color_order ) == "none" ) { break; } fromColor = level.current_color_order[ fromColor ]; } if( IsDefined( fromColor ) ) { spawn set_force_color( fromColor ); } thread lock_spawner_for_awhile(); if( IsDefined( level.friendly_startup_thread ) ) { spawn thread [ [ level.friendly_startup_thread ] ](); } spawn thread colorNode_replace_on_death(); } } function colorNode_replace_on_death() { level endon( "kill_color_replacements" ); assert( isalive( self ), "Tried to do replace on death on something that was not alive" ); self endon( "_disable_reinforcement" ); if( self.team == "axis" ) { return; } if ( IsDefined( self.replace_on_death ) ) { return; } self.replace_on_death = true; assert( !IsDefined( self.respawn_on_death ), "Guy with export " + self.export + " tried to run respawn on death twice." ); // when a red or green guy dies, an orange guy becomes a red guy // when an orange guy dies, a yellow guy becomes an orange guy classname = self.classname; color = self.script_forceColor; // if we spawn a new guy with spawn_reinforcement, he needs to get his color assignment before he checks his forcecolor waittillframeend; if( isalive( self ) ) { // could've died in waittillframeend self waittill( "death" ); } color_order = level.current_color_order; if( !IsDefined( self.script_forceColor ) ) { return; } //Create only 1 respawn thread per class/color friendly_spawners_type = getClassColorHash(classname, self.script_forceColor); if(!isdefined(level.friendly_spawners_types) || !isdefined(level.friendly_spawners_types[friendly_spawners_type]) || level.friendly_spawners_types[friendly_spawners_type] <= 0) { level.friendly_spawners_types[friendly_spawners_type] = 1; // spawn a replacement yellow guy thread colorNode_spawn_reinforcement( classname, self.script_forceColor ); } else { level.friendly_spawners_types[friendly_spawners_type] = level.friendly_spawners_types[friendly_spawners_type] + 1; } if( IsDefined( self ) && IsDefined( self.script_forceColor ) ) { color = self.script_forceColor; } if( IsDefined( self ) && IsDefined( self.origin ) ) { origin = self.origin; } // a replacement has been spawned, so now promote somebody to our color for( ;; ) { if( get_color_from_order( color, color_order ) == "none" ) { return; } correct_colored_friendlies = get_force_color_guys( "allies", color_order[ color ] ); //correct_colored_friendlies = remove_heroes( correct_colored_friendlies );//TODO T7 - bring over hero system if we need it correct_colored_friendlies = array::filter_classname( correct_colored_friendlies, true, classname ); if( !correct_colored_friendlies.size ) { // nobody of the correct color existed, so give them more time to spawn wait( 2 ); continue; } players = GetPlayers(); //correct_colored_guy = _utility::getClosest( players[0].origin, correct_colored_friendlies ); correct_colored_guy = ArraySort( correct_colored_friendlies, players[0].origin, 1 )[0]; assert( correct_colored_guy.script_forceColor != color, "Tried to replace a " + color + " guy with a guy of the same color!" ); // have to wait until the end of the frame because the guy may have just spawned and been given his forcecolor, // and you cant give a guy forcecolor twice in one frame currently. waittillframeend; if( !isalive( correct_colored_guy ) ) { // if he died during the frame then try again! continue; } correct_colored_guy set_force_color( color ); // should something special happen when a guy is promoted? Like a change in threatbias group? if( IsDefined( level.friendly_promotion_thread ) ) { correct_colored_guy [ [ level.friendly_promotion_thread ] ]( color ); } color = color_order[ color ]; } } function get_color_from_order( color, color_order ) { if( !IsDefined( color ) ) { return "none"; } if( !IsDefined( color_order ) ) { return "none"; } if( !IsDefined( color_order[ color ] ) ) { return "none"; } return color_order[ color ]; } function friendly_spawner_vision_checker() { level.friendly_respawn_vision_checker_thread = true; // checks to see if the player is looking at the friendly spawner successes = 0; for( ;; ) { level flag::wait_till_clear( "respawn_friendlies" ); wait( 1 ); // friendly_respawn is disabled but if the player is far enough away and looking away // from the spawner then we can still spawn from it. if( !IsDefined( level.respawn_spawner ) ) { continue; } spawner = level.respawn_spawner; players = GetPlayers(); player_sees_spawner = false; for( q = 0; q < players.size; q++ ) { difference_vec = players[q].origin - spawner.origin; if( length( difference_vec ) < 200 ) { player_sees_spawner(); player_sees_spawner = true; break; } forward = anglesToForward(( 0, players[q] getplayerangles()[ 1 ], 0 ) ); difference = vectornormalize( difference_vec ); dot = vectordot( forward, difference ); if( dot < 0.2 ) { player_sees_spawner(); player_sees_spawner = true; break; } successes++; if( successes < 3 ) { continue; } } if( player_sees_spawner ) { continue; } // player has been looking away for 3 seconds level flag::set( "player_looks_away_from_spawner" ); } } function get_color_spawner( classname, fromColor ) { // make sure we don't assume that this array is defined! specificFromColor = false; if( IsDefined( level.respawn_spawners_specific ) && IsDefined( level.respawn_spawners_specific[fromColor] ) ) { specificFromColor = true; } if( !IsDefined( level.respawn_spawner ) ) { // make sure we're not using color-specific respawners instead if( !IsDefined( fromColor ) || !specificFromColor ) { ASSERTMSG( "Tried to spawn a guy but neither level.respawn_spawner or level.respawn_spawners_specific is defined. Either set it to a spawner or use targetname trigger_friendly_respawn triggers. HINT: has the player hit a friendly_respawn_trigger for ALL allied color groups in the map by the time the player has reached this point?" ); } } // if the classname is not set, just use the global respawn spawner if( !IsDefined( classname ) ) { if( IsDefined( fromColor ) && specificFromColor ) { return level.respawn_spawners_specific[fromColor]; } else { return level.respawn_spawner; } } spawners = GetEntArray( "color_spawner", "targetname" ); class_spawners = []; for( i=0; i < spawners.size; i++ ) { class_spawners[ spawners[ i ].classname ] = spawners[ i ]; } // find the spawner that has the supplied classname as a substr spawner = undefined; keys = getarraykeys( class_spawners ); for( i=0; i < keys.size; i++ ) { if( !issubstr( class_spawners[ keys[ i ] ].classname, classname ) ) { continue; } spawner = class_spawners[ keys[ i ] ]; break; } // spawner = class_spawners[ classname ]; if( !IsDefined( spawner ) ) { if( IsDefined( fromColor ) && specificFromColor ) { return level.respawn_spawners_specific[fromColor]; } else { return level.respawn_spawner; } } if( IsDefined( fromColor ) && specificFromColor ) { spawner.origin = level.respawn_spawners_specific[fromColor].origin; } else { spawner.origin = level.respawn_spawner.origin; } return spawner; } function getClassColorHash(classname, fromcolor ) { //Create only 1 respawn thread per class/color classColorHash = classname; if(isdefined(fromcolor)) { classColorHash += "##" + fromcolor; } return classColorHash; } function lock_spawner_for_awhile() { level flag::set( "friendly_spawner_locked" ); wait( 2 ); level flag::clear( "friendly_spawner_locked" ); } function player_sees_spawner() { level flag::clear( "player_looks_away_from_spawner" ); } function kill_color_replacements() { // kills ALL color respawning level flag::clear( "friendly_spawner_locked" ); level notify( "kill_color_replacements" ); level.friendly_spawners_types = undefined; ai = GetAIArray(); array::thread_all( ai,&remove_replace_on_death ); } function remove_replace_on_death() { self.replace_on_death = undefined; } /@ "Name: set_force_color( <_color> )" "Summary: Sets a guy's force color" "Module: Color" "CallOn: An AI" "Example: guy set_force_color( "p" );" "SPMP: singleplayer" @/ function set_force_color( _color ) { // shorten and lowercase the ai's forcecolor to a single letter color = shortenColor( _color ); assert( colorIsLegit( color ), "Tried to set force color on an undefined color: " + color ); if( !IsActor( self ) ) { set_force_color_spawner( color ); return; } assert( isalive( self ), "Tried to set force color on a dead / undefined entity." ); self.fixedNodeSafeRadius = 64; self.script_color_axis = undefined; self.script_color_allies = undefined; self.old_forcecolor = undefined; if( IsDefined( self.script_forcecolor ) ) { // first remove the guy from the force color array he used to belong to ArrayRemoveValue( level.arrays_of_colorForced_ai[ self.team ][ self.script_forcecolor ], self ); } self.script_forceColor = color; // get added to the new array of AI that are forced to this color if ( !isdefined( level.arrays_of_colorForced_ai[ self.team ][ self.script_forceColor ] ) ) level.arrays_of_colorForced_ai[ self.team ][ self.script_forceColor ] = []; else if ( !IsArray( level.arrays_of_colorForced_ai[ self.team ][ self.script_forceColor ] ) ) level.arrays_of_colorForced_ai[ self.team ][ self.script_forceColor ] = array( level.arrays_of_colorForced_ai[ self.team ][ self.script_forceColor ] ); level.arrays_of_colorForced_ai[ self.team ][ self.script_forceColor ][level.arrays_of_colorForced_ai[ self.team ][ self.script_forceColor ].size]=self;; level thread remove_colorForced_ai_when_dead( self ); // set it here so that he continues in script as the correct color self thread new_color_being_set( color ); } function remove_colorForced_ai_when_dead( ai ) { script_forceColor = ai.script_forceColor; team = ai.team; ai waittill( "death" ); level.arrays_of_colorForced_ai[ team ][ script_forceColor ] = array::remove_undefined( level.arrays_of_colorForced_ai[ team ][ script_forceColor ] ); } function shortenColor( color ) { Assert( IsDefined( level.colorCheckList[ ToLower( color ) ] ), "Tried to set force color on an undefined color: " + color ); return level.colorCheckList[ ToLower( color ) ]; } function set_force_color_spawner( color ) { self.script_forceColor = color; self.old_forceColor = undefined; } function new_color_being_set( color ) { self notify( "new_color_being_set" ); self.new_force_color_being_set = true; left_color_node(); self endon( "new_color_being_set" ); self endon( "death" ); // insure we're only getting one color change, multiple in one frame will get overwritten. waittillframeend; waittillframeend; if ( IsDefined( self.script_forceColor ) ) { // grab the current colorCode that AI of this color are forced to, if there is one self.currentColorCode = level.currentColorForced[ self.team ][ self.script_forceColor ]; self thread goto_current_ColorIndex(); } self.new_force_color_being_set = undefined; self notify( "done_setting_new_color" ); /# update_debug_friendlycolor(); #/ } function update_debug_friendlycolor_on_death() { self notify( "debug_color_update" ); self endon( "debug_color_update" ); self waittill( "death" ); /# a_keys = GetArrayKeys( level.debug_color_friendlies ); foreach( n_key in a_keys ) { ai = GetEntByNum( n_key ); if ( !IsAlive( ai ) ) { ArrayRemoveIndex( level.debug_color_friendlies, n_key, true ); } } #/ // updates the debug color friendlies info level notify( "updated_color_friendlies" ); } function update_debug_friendlycolor() { self thread update_debug_friendlycolor_on_death(); if ( isdefined( self.script_forceColor ) ) { level.debug_color_friendlies[ self GetEntityNumber() ] = self.script_forceColor; } else { level.debug_color_friendlies[ self GetEntityNumber() ] = undefined; } // updates the debug color friendlies info level notify( "updated_color_friendlies" ); } function has_color() { // can lose color during the waittillframeend in left_color_node if ( self.team == "axis" ) { return IsDefined( self.script_color_axis ) || IsDefined( self.script_forceColor ); } return IsDefined( self.script_color_allies ) || IsDefined( self.script_forceColor ); } /@ "Name: get_force_color()" "Summary: Returns a guy's force color" "Module: Color" "CallOn: An AI" "Example: color = guy get_force_color()" "SPMP: singleplayer" @/ function get_force_color() { color = self.script_forceColor; return color; } /@ "Name: get_force_color_guys( , )" "Summary: Returns all alive ai of a certain force color." "Module: AI" "CallOn: " "Example: red_guys = get_force_color_guys( "allies", "r" );" "MandatoryArg: : the team of the guys to check" "MandatoryArg: : the color value of the guys you want to collect" "SPMP: singleplayer" @/ function get_force_color_guys( team, color ) { ai = GetAITeamArray( team ); guys = []; for( i = 0; i < ai.size; i++ ) { guy = ai[ i ]; if( !IsDefined( guy.script_forceColor ) ) { continue; } if( guy.script_forceColor != color ) { continue; } guys[ guys.size ] = guy; } return guys; } function get_all_force_color_friendlies() { ai = GetAITeamArray( "allies" ); guys = []; for( i = 0; i < ai.size; i++ ) { guy = ai[ i ]; if( !IsDefined( guy.script_forceColor ) ) { continue; } guys[ guys.size ] = guy; } return guys; } /@ "Name: disable()" "Summary: disables an ai's force color. Essentially takes him off the color chain." "Module: Color" "CallOn: An AI" "Example: guy colors::disable();" "SPMP: singleplayer" @/ function disable( stop_being_careful ) { if( IsDefined( self.new_force_color_being_set ) ) { self endon( "death" ); // setting force color happens after waittillframeend so we need to wait until it finishes // setting before we disable it, so a set followed by a disable will send the guy to a node. self waittill( "done_setting_new_color" ); } // SUMEET_TODO - AI should stop going to node/running careful right when color is disabled. // Added this late on Black Ops2 ( 08/10/2012 ) hence not making it global. Might introduce issues // on other maps. if( ( isdefined( stop_being_careful ) && stop_being_careful ) ) { self notify( "stop_going_to_node" ); self notify( "stop_being_careful" ); } self clearFixedNodeSafeVolume(); // any color on this guy? if( !IsDefined( self.script_forceColor ) ) { return; } assert( !IsDefined( self.old_forcecolor ), "Tried to disable forcecolor on a guy that somehow had a old_forcecolor already. Investigate!!!" ); self.old_forceColor = self.script_forceColor; // first remove the guy from the force color array he used to belong to ArrayRemoveValue( level.arrays_of_colorForced_ai[ self.team ][ self.script_forcecolor ], self ); // self _colors::removeAIFromColorNumberArray(); left_color_node(); self.script_forceColor = undefined; self.currentColorCode = undefined; /# update_debug_friendlycolor(); #/ } /@ "Name: enable()" "Summary: Re-enables an ai's force color. Only works on guys that have had a forceColor set previously." "Module: Color" "CallOn: An AI" "Example: guy colors::enable();" "SPMP: singleplayer" @/ function enable() { if ( IsDefined( self.script_forceColor ) ) { return; } if ( !IsDefined( self.old_forceColor ) ) { return; } set_force_color( self.old_forcecolor ); self.old_forceColor = undefined; } function is_color_ai() { return ( isdefined( self.script_forcecolor ) || isdefined( self.old_forcecolor ) ); } /# function insure_player_does_not_set_forcecolor_twice_in_one_frame() { assert( !IsDefined( self.setforcecolor ), "Tried to set forceColor on an ai twice in one frame. Don't spam set_force_color." ); self.setforcecolor = true; waittillframeend; if ( !isalive( self ) ) { return; } self.setforcecolor = undefined; } #/