cod4-sdk/raw/maps/_colors.gsc
2008-01-19 00:00:00 +00:00

1515 lines
No EOL
43 KiB
Text

#include maps\_utility;
#include common_scripts\utility;
/*
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.
*/
init_color_grouping( nodes )
{
// friendly spawner global stuff
flag_init( "player_looks_away_from_spawner" );
flag_init( "friendly_spawner_locked" );
// wait( 0.05 );// begone infinite hangs
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" ] = [];
triggers = [];
triggers = array_combine( triggers, getentarray( "trigger_multiple" , "classname" ) );
triggers = array_combine( triggers, getentarray( "trigger_radius" , "classname" ) );
triggers = array_combine( triggers, getentarray( "trigger_once" , "classname" ) );
volumes = getentarray( "info_volume", "classname" );
// go through all the nodes and if they have color codes then add them too
for ( i = 0;i < nodes.size;i++ )
{
if ( isdefined( nodes[ i ].script_color_allies ) )
nodes[ i ] add_node_to_global_arrays( nodes[ i ].script_color_allies, "allies" );
if ( isdefined( nodes[ i ].script_color_axis ) )
nodes[ i ] add_node_to_global_arrays( nodes[ i ].script_color_axis, "axis" );
}
// triggers that have color codes wait for trigger and then fire their colorcode
for ( i = 0;i < triggers.size;i++ )
{
if ( isdefined( triggers[ i ].script_color_allies ) )
triggers[ i ] thread trigger_issues_orders( triggers[ i ].script_color_allies, "allies" );
if ( isdefined( triggers[ i ].script_color_axis ) )
triggers[ i ] thread trigger_issues_orders( triggers[ i ].script_color_axis, "axis" );
}
// volumes that have color codes wait for trigger and then fire their colorcode to control fixednodesafe volumes
for ( i = 0;i < volumes.size;i++ )
{
if ( isdefined( volumes[ i ].script_color_allies ) )
volumes[ i ] add_volume_to_global_arrays( volumes[ i ].script_color_allies, "allies" );
if ( isdefined( volumes[ i ].script_color_axis ) )
volumes[ i ] add_volume_to_global_arrays( volumes[ i ].script_color_allies, "axis" );
}
/#
level.colorNodes_debug_array = [];
level.colorNodes_debug_array[ "allies" ] = [];
level.colorNodes_debug_array[ "axis" ] = [];
// level.colorNodes_debug_ai = [];
#/
level.color_node_type_function = [];
add_cover_node( "BAD NODE" );
add_cover_node( "Cover Stand" );
add_cover_node( "Cover Crouch" );
add_cover_node( "Cover Prone" );
add_cover_node( "Cover Crouch Window" );
add_cover_node( "Cover Right" );
add_cover_node( "Cover Left" );
add_cover_node( "Cover Wide Left" );
add_cover_node( "Cover Wide Right" );
add_cover_node( "Conceal Stand" );
add_cover_node( "Conceal Crouch" );
add_cover_node( "Conceal Prone" );
add_cover_node( "Reacquire" );
add_cover_node( "Balcony" );
add_cover_node( "Scripted" );
add_cover_node( "Begin" );
add_cover_node( "End" );
add_cover_node( "Turret" );
add_path_node( "Guard" );
add_path_node( "Path" );
level.colorList = [];
level.colorList[ level.colorList.size ] = "r";
level.colorList[ level.colorList.size ] = "b";
level.colorList[ level.colorList.size ] = "y";
level.colorList[ level.colorList.size ] = "c";
level.colorList[ level.colorList.size ] = "g";
level.colorList[ level.colorList.size ] = "p";
level.colorList[ level.colorList.size ] = "o";
level.colorCheckList[ "red" ] = "r";
level.colorCheckList[ "r" ] = "r";
level.colorCheckList[ "blue" ] = "b";
level.colorCheckList[ "b" ] = "b";
level.colorCheckList[ "yellow" ] = "y";
level.colorCheckList[ "y" ] = "y";
level.colorCheckList[ "cyan" ] = "c";
level.colorCheckList[ "c" ] = "c";
level.colorCheckList[ "green" ] = "g";
level.colorCheckList[ "g" ] = "g";
level.colorCheckList[ "purple" ] = "p";
level.colorCheckList[ "p" ] = "p";
level.colorCheckList[ "orange" ] = "o";
level.colorCheckList[ "o" ] = "o";
level.currentColorForced = [];
level.currentColorForced[ "allies" ] = [];
level.currentColorForced[ "axis" ] = [];
level.lastColorForced = [];
level.lastColorForced[ "allies" ] = [];
level.lastColorForced[ "axis" ] = [];
for ( i = 0; i < level.colorList.size; i++ )
{
level.arrays_of_colorForced_ai[ "allies" ][ level.colorList[ i ] ] = [];
level.arrays_of_colorForced_ai[ "axis" ][ level.colorList[ i ] ] = [];
level.currentColorForced[ "allies" ][ level.colorList[ i ] ] = undefined;
level.currentColorForced[ "axis" ][ level.colorList[ i ] ] = undefined;
}
// array_thread( getaiarray(), ::ai_picks_destination );
// array_thread( getspawnerarray(), ::spawner_processes_colorCoded_ai );
thread player_color_node();
}
convert_color_to_short_string()
{
// shorten the forcecolors tring to a single letter
self.script_forceColor = level.colorCheckList[ self.script_forceColor ];
}
ai_picks_destination( currentColorCode )
{
if ( isdefined( self.script_forcecolor ) )
{
convert_color_to_short_string();
/*
// axis don't do node behavior if they're forcecolor.
if ( self.team == "axis" )
{
ai_follow_the_leader_behavior();
return;
}
*/
self.currentColorCode = currentColorCode;
color = self.script_forcecolor;
assert( colorIsLegit( color ), "AI at origin " + self.origin + " has non - legit forced color " + color + ". Legit colors are red blue yellow cyan green purple and orange." );
level.arrays_of_colorForced_ai[ self.team ][ color ] = array_add( level.arrays_of_colorForced_ai[ self.team ][ color ], self );
thread goto_current_ColorIndex();
return;
}
/*
colorTeam = undefined;
if ( issubstr( self.classname, "axis" ) || issubstr( self.classname, "enemy" ) )
{
assertEx( !isdefined( self.script_color_allies ), "Axis at origin " + self.origin + " has script_color_allies!" );
if ( !isdefined( self.script_color_axis ) )
return;
colorTeam = self.script_color_axis;
}
if ( ( issubstr( self.classname, "ally" ) ) || ( issubstr( self.classname, "civilian" ) ) )
{
assertEx( !isdefined( self.script_color_axis ), "Ally at origin " + self.origin + " has script_color_axis!" );
if ( !isdefined( self.script_color_allies ) )
return;
colorTeam = self.script_color_allies;
}
colorCodes = strtok( colorTeam, " " );
for ( i = 0;i < colorCodes.size;i++ )
{
self.currentColorCode = currentColorCode;
assertEx( isdefined( level.arrays_of_colorCoded_ai[ self.team ][ colorCodes[ i ] ] ), "AI at origin " + self.origin + " has script_color " + colorCodes[ i ] + " which does not exist on any nodes" );
level.arrays_of_colorCoded_ai[ self.team ][ colorCodes[ i ] ] = array_add( level.arrays_of_colorCoded_ai[ self.team ][ colorCodes[ i ] ], self );
thread goto_current_ColorIndex();
}
*/
}
goto_current_ColorIndex()
{
if ( !isdefined( self.currentColorCode ) )
return;
nodes = level.arrays_of_colorCoded_nodes[ 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 < nodes.size; i++ )
{
node = nodes[ i ];
if ( isalive( node.color_user ) && node.color_user != level.player )
continue;
self thread ai_sets_goal_with_delay( node );
thread decrementColorUsers( node );
return;
}
no_node_to_go_to();
}
no_node_to_go_to()
{
println( "AI with export " + self.export + " was told to go to color node but had no node to go to." );
// what to do?
/*
col = self.script_forcecolor;
colors = get_script_palette();
timer = 15 * 20;
for ( i = 0; i < timer; i++ )
{
print3d( self.origin + ( 0, 0, 72 ), "? ? ?", colors[ col ], 0.9, 1.2 );
wait( 0.05 );
}
*/
}
/*
spawner_processes_colorCoded_ai()
{
if ( isdefined( self.script_forceColor ) )
{
// this is for non forcecolor AI
return;
}
self endon( "new_color_code" );
axis = issubstr( self.classname, "axis" ) || issubstr( self.classname, "enemy" );
/#
if ( isdefined( self.script_color_axis ) || isdefined( self.script_color_allies ) )
{
assertEx( !isdefined( self.script_forceColor ), "Can't have script_color_axis / allies and script_forceColor on the same spawner." );
}
else
{
if ( isdefined( self.script_forceColor ) )
assertEx( !isdefined( self.script_color_axis ) && !isdefined( self.script_color_allies ), "Can't have script_color_axis / allies and script_forceColor on the spawn spawner." );
}
#/
colorTeam = undefined;
forceTeam = undefined;
team = undefined;
if ( axis )
{
assertEx( !isdefined( self.script_color_allies ), "Axis spawner at origin " + self.origin + " has script_color_allies!" );
if ( !isdefined( self.script_color_axis ) && !isdefined( self.script_forcecolor ) )
return;
colorTeam = self.script_color_axis;
team = "axis";
}
if ( ( issubstr( self.classname, "ally" ) ) || ( issubstr( self.classname, "civilian" ) ) )
{
assertEx( !isdefined( self.script_color_axis ), "Ally spawner at origin " + self.origin + " has script_color_axis!" );
if ( !isdefined( self.script_color_allies ) && !isdefined( self.script_forcecolor ) )
return;
colorTeam = self.script_color_allies;
team = "allies";
}
if ( isdefined( colorTeam ) )
{
colorCodes = strtok( colorTeam, " " );
for ( i = 0; i < colorCodes.size; i++ )
{
if ( !isdefined( level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ] ) )
continue;
level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ] = array_add( level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ], self );
}
}
self endon( "death" );
for ( ;; )
{
self waittill( "spawned", spawn );
if ( spawn_failed( spawn ) )
continue;
if ( isdefined( self.script_forceColor ) )
self.currentColorCode = level.currentColorForced[ team ][ self.script_forceColor ];
spawn thread ai_picks_destination( self.currentColorCode );
}
}
*/
get_color_list()
{
colorList = [];
// returns an array of the acceptable color letters
colorList[ colorList.size ] = "r";
colorList[ colorList.size ] = "b";
colorList[ colorList.size ] = "y";
colorList[ colorList.size ] = "c";
colorList[ colorList.size ] = "g";
colorList[ colorList.size ] = "p";
colorList[ colorList.size ] = "o";
return colorList;
}
get_colorcodes_from_trigger( color_team, team )
{
colorCodes = strtok( color_team, " " );
colors = [];
colorCodesByColorIndex = [];
usable_colorCodes = [];
colorList = get_color_list();
for ( i = 0; i < colorCodes.size; i++ )
{
color = undefined;
for ( p = 0; p < colorList.size; p++ )
{
if ( issubstr( colorCodes[ i ], colorList[ p ] ) )
{
color = colorList[ p ];
break;
}
}
// does this order actually tie to existing nodes?
if ( !isdefined( level.arrays_of_colorCoded_nodes[ team ][ colorCodes[ i ] ] ) )
continue;
assertEx( isdefined( color ), "Trigger at origin " + self getorigin() + " had strange color index " + colorCodes[ i ] );
colorCodesByColorIndex[ color ] = colorCodes[ i ];
colors[ colors.size ] = color;
usable_colorCodes[ usable_colorCodes.size ] = colorCodes[ i ];
}
// color codes that don't tie to existing nodes have been culled
colorCodes = usable_colorCodes;
array = [];
array[ "colorCodes" ] = colorCodes;
array[ "colorCodesByColorIndex" ] = colorCodesByColorIndex;
array[ "colors" ] = colors;
return array;
}
trigger_issues_orders( color_team, team )
{
array = get_colorcodes_from_trigger( color_team, team );
colorCodes = array[ "colorCodes" ];
colorCodesByColorIndex = array[ "colorCodesByColorIndex" ];
colors = array[ "colors" ];
for ( ;; )
{
self waittill( "trigger" );
if ( isdefined( self.activated_color_trigger ) )
{
// activated by an activate_trigger() call, so don't bother running activate_color_trigger() again.
self.activated_color_trigger = undefined;
continue;
}
activate_color_trigger_internal( colorCodes, colors, team, colorCodesByColorIndex );
}
}
activate_color_trigger( team )
{
if ( team == "allies" )
self thread get_colorcodes_and_activate_trigger( self.script_color_allies, team );
else
self thread get_colorcodes_and_activate_trigger( self.script_color_axis, team );
}
get_colorcodes_and_activate_trigger( color_team, team )
{
array = get_colorcodes_from_trigger( color_team, team );
colorCodes = array[ "colorCodes" ];
colorCodesByColorIndex = array[ "colorCodesByColorIndex" ];
colors = array[ "colors" ];
activate_color_trigger_internal( colorCodes, colors, team, colorCodesByColorIndex );
}
activate_color_trigger_internal( colorCodes, colors, team, colorCodesByColorIndex )
{
// give spawners activated by this trigger a chance to spawn their AI
// removing this because for one thing people shouldn't use compound triggers, but if they do,
// the level.currentColorForced will get set, so any AI that spawn will still go to a colored node
// even if it happens after this
// waittillframeend;
// 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
level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ] = array_removeUndefined( level.arrays_of_colorCoded_spawners[ team ][ colorCodes[ i ] ] );
// 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_removeDead( 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 ] ];
/#
assertEx( isdefined( level.arrays_of_colorCoded_nodes[ 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 );
}
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 ] );
}
}
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 ];
}
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;
}
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;
}
process_cover_node( node, null )
{
self.cover_nodes_first[ self.cover_nodes_first.size ] = node;
}
process_path_node( node, null )
{
self.path_nodes[ self.path_nodes.size ] = node;
}
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;
}
get_prioritized_colorCoded_nodes( team, colorCode, color )
{
return level.arrays_of_colorCoded_nodes[ team ][ colorCode ];
}
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_removeDead( level.arrays_of_colorCoded_ai[ team ][ colorCode ] );
ai = level.arrays_of_colorCoded_ai[ team ][ colorCode ];
ai = array_combine( ai, level.arrays_of_colorForced_ai[ team ][ color ] );
newArray = [];
for ( i = 0;i < ai.size;i++ )
{
// ignore AI that are already going to this colorCode
if ( isdefined( ai[ i ].currentColorCode ) && ai[ i ].currentColorCode == colorCode )
continue;
newArray[ newArray.size ] = ai[ i ];
}
ai = newArray;
if ( !ai.size )
return;
for ( i = 0; i < ai.size; i++ )
ai[ i ] left_color_node();
return ai;
}
issue_color_order_to_ai( colorCode, color, team, ai )
{
original_ai_array = ai;
prioritize_colorCoded_nodes( team, colorCode, color );
nodes = get_prioritized_colorCoded_nodes( team, colorCode, color );
/#
level.colorNodes_debug_array[ team ][ colorCode ] = nodes;
#/
/#
if ( nodes.size < ai.size )
println( "^3Warning, ColorNumber system tried to make " + ai.size + " AI go to " + nodes.size + " nodes." );
#/
counter = 0;
ai_count = ai.size;
for ( i = 0; i < nodes.size; i++ )
{
node = nodes[ i ];
// add guys to the nodes with the fewest AI on them
if ( isalive( node.color_user ) )
continue;
closestAI = getclosest( node.origin, ai );
assert( isalive( closestAI ) );
ai = array_remove( ai, closestAI );
// println( "sent ai to node" );
closestAI take_color_node( node, colorCode, self, counter );
counter++ ;
if ( !ai.size )
return;
}
}
take_color_node( node, colorCode, trigger, counter )
{
self notify( "stop_color_move" );
self.currentColorCode = colorCode;
self thread process_color_order_to_ai( node, trigger, counter );
}
player_color_node()
{
// detect if the player gets a node and set its .color_user
for ( ;; )
{
playerNode = undefined;
if ( !isdefined( level.player.node ) )
{
wait( 0.05 );
continue;
}
olduser = level.player.node.color_user;
playerNode = level.player.node;
playerNode.color_user = level.player;
/*
if ( isalive( olduser ) )
{
// send this guy somewhere else
node = olduser get_best_available_colored_node();
if ( !isdefined( node ) )
{
olduser no_node_to_go_to();
}
else
{
olduser setgoalnode( node );
olduser.goalradius = node.radius;
}
}
*/
for ( ;; )
{
if ( !isdefined( level.player.node ) )
break;
if ( level.player.node != playerNode )
break;
wait( 0.05 );
}
playerNode.color_user = undefined;
playerNode color_node_finds_a_user();
}
}
color_node_finds_a_user()
{
if ( isdefined( self.script_color_allies ) )
{
color_node_finds_user_from_colorcodes( self.script_color_allies, "allies" );
}
if ( isdefined( self.script_color_axis ) )
{
color_node_finds_user_from_colorcodes( self.script_color_axis, "axis" );
}
}
color_node_finds_user_from_colorcodes( colorCodeString, team )
{
if ( isdefined( self.color_user ) )
{
// If we successfully found a guy for this node then we shouldnt go find another
return;
}
colorCodes = strtok( colorCodeString, " " );
array_levelthread( colorCodes, ::color_node_finds_user_for_colorCode, team );
}
color_node_finds_user_for_colorCode( colorCode, team )
{
color = colorCode[ 0 ];
assertex( colorIsLegit( color ), "Color " + color + " is not legit" );
if ( !isdefined( level.currentColorForced[ team ][ color ] ) )
{
// AI of this color are not assigned to a colornode currently
return;
}
if ( level.currentColorForced[ team ][ color ] != colorCode )
{
// AI of this color are not currently assigned to our colorCode
return;
}
ai = get_force_color_guys( team, color );
if ( !ai.size )
{
return;
}
for ( i = 0; i < ai.size; i++ )
{
guy = ai[ i ];
if ( guy occupies_colorCode( colorCode ) )
{
continue;
}
// found a guy that should use this node, so assign and get out
guy take_color_node( self, colorCode );
return;
}
}
occupies_colorCode( colorCode )
{
if ( !isdefined( self.currentColorCode ) )
{
return false;
}
return self.currentColorCode == colorCode;
}
ai_sets_goal_with_delay( node )
{
self endon( "death" );
delay = my_current_node_delays();
if ( delay )
{
self endon( "stop_color_move" );
// can get a stop color notify during the delay
wait( delay );
}
thread ai_sets_goal( node );
}
ai_sets_goal( node )
{
// makes AI stop trying to run to their chain of nodes in _spawner go_to_node
self notify( "stop_going_to_node" );
set_goal_and_volume( node );
volume = level.colorCoded_volumes[ self.team ][ self.currentColorCode ];
if ( isdefined( self.script_careful ) )
{
thread careful_logic( node, volume );
}
}
set_goal_and_volume( node )
{
if ( isdefined( self._colors_go_line ) )
{
self thread maps\_anim::anim_single_queue( self, self._colors_go_line );
self._colors_go_line = undefined;
}
self setgoalnode( node );
if ( !self.fixedNode )
{
assertex( isdefined( node.radius ), "Node at origin " + node.origin + " has no .radius." );
self.goalradius = node.radius;
}
else
{
if ( isdefined( node.radius ) )
self.goalradius = node.radius;
}
volume = level.colorCoded_volumes[ self.team ][ self.currentColorCode ];
if ( isdefined( volume ) )
{
self setFixedNodeSafeVolume( volume );
}
else
{
self clearFixedNodeSafeVolume();
}
if ( isdefined( node.fixedNodeSafeRadius ) )
{
self.fixedNodeSafeRadius = node.fixedNodeSafeRadius;
}
else
{
self.fixedNodeSafeRadius = 64;
}
}
careful_logic( node, volume )
{
self endon( "death" );
self endon( "stop_being_careful" );
self endon( "stop_going_to_node" );
thread recover_from_careful_disable( node );
for ( ;; )
{
wait_until_an_enemy_is_in_safe_area( node, volume );
use_big_goal_until_goal_is_safe( node, volume );
self.fixednode = true;
set_goal_and_volume( node );
}
}
recover_from_careful_disable( node )
{
self endon( "death" );
self endon( "stop_going_to_node" );
self waittill( "stop_being_careful" );
self.fixednode = true;
set_goal_and_volume( node );
}
use_big_goal_until_goal_is_safe( node, volume )
{
self setgoalpos( self.origin );
self.goalradius = 1024;
self.fixednode = false;
if ( isdefined( volume ) )
{
for ( ;; )
{
wait( 1 );
if ( self isKnownEnemyInRadius( node.origin, self.fixedNodeSafeRadius ) )
continue;
if ( self isKnownEnemyInVolume( volume ) )
continue;
return;
}
}
else
{
for ( ;; )
{
// if ( !( self isKnownEnemyInRadius( node.origin, self.fixedNodeSafeRadius ) ) )
if ( !( self isKnownEnemyInRadius_tmp( node.origin, self.fixedNodeSafeRadius ) ) )
return;
wait( 1 );
}
}
}
isKnownEnemyInRadius_tmp( node_origin, safe_radius )
{
ai = getaiarray( "axis" );
for ( i = 0 ; i < ai.size ; i++ )
{
if ( distance2d( ai[ i ].origin, node_origin ) < safe_radius )
return true;
}
return false;
}
wait_until_an_enemy_is_in_safe_area( node, volume )
{
if ( isdefined( volume ) )
{
for ( ;; )
{
if ( self isKnownEnemyInRadius( node.origin, self.fixedNodeSafeRadius ) )
return;
if ( self isKnownEnemyInVolume( volume ) )
return;
wait( 1 );
}
}
else
{
for ( ;; )
{
if ( self isKnownEnemyInRadius_tmp( node.origin, self.fixedNodeSafeRadius ) )
return;
wait( 1 );
}
}
}
my_current_node_delays()
{
if ( !isdefined( self.node ) )
return false;
return self.node script_delay();
}
process_color_order_to_ai( node, trigger, counter )
{
thread decrementColorUsers( node );
self endon( "stop_color_move" );
self endon( "death" );
if ( isdefined( trigger ) )
{
trigger script_delay();
}
// waitSpread( delay_size * 0.5 );// they spread out the time they leave based on how many guys it is
if ( !my_current_node_delays() )
{
if ( isdefined( counter ) )
{
wait( counter * randomfloatrange( 0.2, 0.35 ) );
}
}
self ai_sets_goal( node );
// record the node so the guy can find out who has his node, and get that guys
self.color_ordered_node_assignment = node;
for ( ;; )
{
self waittill( "node_taken", taker );
if ( taker == level.player )
{
// give time for the player to claim the node
wait( 0.05 );
}
// lost our node so try to get a new one
node = get_best_available_new_colored_node();
if ( isdefined( node ) )
{
assertEx( !isalive( node.color_user ), "Node already had color user!" );
if ( isalive( self.color_node.color_user ) && self.color_node.color_user == self )
self.color_node.color_user = undefined;
self.color_node = node;
node.color_user = self;
self ai_sets_goal( node );
}
}
}
get_best_available_colored_node()
{
assertEx( self.team != "neutral" );
assertEx( isdefined( self.script_forceColor ), "AI with export " + self.export + " lost his script_forcecolor.. somehow." );
colorCode = level.currentColorForced[ self.team ][ self.script_forceColor ];
// nodes = level.arrays_of_colorCoded_nodes[ self.team ][ colorCode ];
nodes = get_prioritized_colorCoded_nodes( self.team, colorCode, self.script_forcecolor );
assertEx( 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" );
for ( i = 0; i < nodes.size; i++ )
{
if ( !isalive( nodes[ i ].color_user ) )
return nodes[ i ];
}
}
get_best_available_new_colored_node()
{
assertEx( self.team != "neutral" );
assertEx( 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 );
assertEx( 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" );
for ( i = 0; i < nodes.size; i++ )
{
if ( nodes[ i ] == self.color_node )
continue;
if ( !isalive( nodes[ i ].color_user ) )
return nodes[ i ];
}
}
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 ( distance( node.origin, self.origin ) < 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
// println( "waiting a second.." );
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 );
}
wait_for_killanimscript_or_time( timer )
{
self endon( "killanimscript" );
wait( timer );
}
reached_node_but_could_not_claim_it( node )
{
// reached our node but somebody else has it so we're gonna tell him to scram
// println( "Reached node " + node.origin );
ai = getaiarray();
guy = undefined;
for ( i = 0;i < ai.size;i++ )
{
if ( !isdefined( ai[ i ].node ) )
continue;
if ( ai[ i ].node != node )
continue;
ai[ i ] notify( "eject_from_my_node" );
wait( 1 );
self notify( "eject_from_my_node" );
return true;
}
return false;
}
decrementColorUsers( node )
{
node.color_user = self;
assertEx( !isdefined( self.color_node ), "Decrement had color_node" );
self.color_node = node;
/#
assertEx( !isdefined( self.color_node_debug_val ), "Guy had double color_node_debug_val" );
self.color_node_debug_val = true;
#/
self endon( "stop_color_move" );
self waittill( "death" );
self.color_node.color_user = undefined;
}
colorIsLegit( color )
{
for ( i = 0; i < level.colorList.size; i++ )
{
if ( color == level.colorList[ i ] )
return true;
}
return false;
}
add_volume_to_global_arrays( colorCode, team )
{
colors = strtok( colorCode, " " );
for ( p = 0; p < colors.size; p++ )
{
assert( !isdefined( level.colorCoded_volumes[ team ][ colors[ p ] ] ), "Multiple info_volumes exist with color code " + colors[ p ] );
level.colorCoded_volumes[ team ][ colors[ p ] ] = self;
}
}
add_node_to_global_arrays( colorCode, team )
{
self.color_user = undefined;
// assertEx( self.radius > 0, "Node " + self.type + " at origin " + self.origin + " does not have a radius set in Radiant." );
colors = strtok( colorCode, " " );
for ( p = 0; p < colors.size; p++ )
{
if ( isdefined( level.arrays_of_colorCoded_nodes[ team ] ) && isdefined( level.arrays_of_colorCoded_nodes[ team ][ colors[ p ] ] ) )
{
// array already exists so add this color coded node to that color code array.
level.arrays_of_colorCoded_nodes[ team ][ colors[ p ] ] = array_add( level.arrays_of_colorCoded_nodes[ team ][ colors[ p ] ], self );
continue;
}
// array doesn't exist so we have to initialize all the variables related to this color coding.
level.arrays_of_colorCoded_nodes[ team ][ colors[ p ] ][ 0 ] = self;
level.arrays_of_colorCoded_ai[ team ][ colors[ p ] ] = [];
level.arrays_of_colorCoded_spawners[ team ][ colors[ p ] ] = [];
}
}
/*
nodeusers()
{
array_thread( getallnodes(), ::nodeThink );
}
nodeThink()
{
if ( !isdefined( self.script_color_allies ) )
return;
for ( ;; )
{
if ( isdefined( self.color_users ) )
print3d( self.origin + ( 0, 0, 16 ), "N - " + self.color_users, ( 1, 1, 1 ) );
wait( 0.05 );
}
}
*/
left_color_node()
{
/#
self.color_node_debug_val = undefined;
#/
if ( !isdefined( self.color_node ) )
return;
// assertEx( self.color_node.color_user == self, "Color Node user wasnt self!" );
if ( isdefined( self.color_node.color_user ) && self.color_node.color_user == self )
self.color_node.color_user = undefined;
self.color_node = undefined;
self notify( "stop_color_move" );
}
GetColorNumberArray()
{
array = [];
if ( issubstr( self.classname, "axis" ) || issubstr( self.classname, "enemy" ) )
{
array[ "team" ] = "axis";
array[ "colorTeam" ] = self.script_color_axis;
}
if ( ( issubstr( self.classname, "ally" ) ) || ( issubstr( self.classname, "civilian" ) ) )
{
array[ "team" ] = "allies";
array[ "colorTeam" ] = self.script_color_allies;
}
if ( !isdefined( array[ "colorTeam" ] ) )
array = undefined;
return array;
}
/*
removeAIFromColorNumberArray()
{
colorNumberArray = GetColorNumberArray();
if ( !isdefined( colorNumberArray ) )
return;
team = colorNumberArray[ "team" ];
colorTeam = colorNumberArray[ "colorTeam" ];
// remove this spawner from any array it was in
colors = strtok( colorTeam, " " );
for ( i = 0;i < colors.size;i++ )
level.arrays_of_colorCoded_ai[ team ][ colors[ i ] ] = array_remove( level.arrays_of_colorCoded_ai[ team ][ colors[ i ] ], self );
self notify( "stop_color_move" );// clear out any existing color / number processes
}
*/
removeSpawnerFromColorNumberArray()
{
colorNumberArray = GetColorNumberArray();
if ( !isdefined( colorNumberArray ) )
return;
team = colorNumberArray[ "team" ];
colorTeam = colorNumberArray[ "colorTeam" ];
// remove this spawner from any array it was in
colors = strtok( colorTeam, " " );
for ( i = 0;i < colors.size;i++ )
level.arrays_of_colorCoded_spawners[ team ][ colors[ i ] ] = array_remove( level.arrays_of_colorCoded_spawners[ team ][ colors[ i ] ], self );
}
add_cover_node( type )
{
level.color_node_type_function[ type ][ true ][ "allies" ] = ::process_cover_node_with_last_in_mind_allies;
level.color_node_type_function[ type ][ true ][ "axis" ] = ::process_cover_node_with_last_in_mind_axis;
level.color_node_type_function[ type ][ false ][ "allies" ] = ::process_cover_node;
level.color_node_type_function[ type ][ false ][ "axis" ] = ::process_cover_node;
}
add_path_node( type )
{
level.color_node_type_function[ type ][ true ][ "allies" ] = ::process_path_node;
level.color_node_type_function[ type ][ false ][ "allies" ] = ::process_path_node;
level.color_node_type_function[ type ][ true ][ "axis" ] = ::process_path_node;
level.color_node_type_function[ type ][ false ][ "axis" ] = ::process_path_node;
}
// ColorNode respawn system
colorNode_spawn_reinforcement( classname, fromColor )
{
level endon( "kill_color_replacements" );
reinforcement = spawn_hidden_reinforcement( classname, fromColor );
if ( isdefined( level.friendly_startup_thread ) )
reinforcement thread [[ level.friendly_startup_thread ]]();
reinforcement thread colorNode_replace_on_death();
}
colorNode_replace_on_death()
{
level endon( "kill_color_replacements" );
assertex( isalive( self ), "Tried to do replace on death on something that was not alive" );
self endon( "_disable_reinforcement" );
if ( isdefined( self.replace_on_death ) )
return;
self.replace_on_death = true;
assertEx( !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;
// spawn a replacement yellow guy
thread colorNode_spawn_reinforcement( classname, self.script_forceColor );
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_from_array( correct_colored_friendlies );
correct_colored_friendlies = remove_without_classname( correct_colored_friendlies, classname );
if ( !correct_colored_friendlies.size )
{
// nobody of the correct color existed, so give them more time to spawn
wait( 2 );
continue;
}
correct_colored_guy = getclosest( level.player.origin, correct_colored_friendlies );
assertEx( 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 ];
}
}
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 ];
}
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 ( ;; )
{
flag_waitopen( "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;
difference_vec = level.player.origin - spawner.origin;
if ( length( difference_vec ) < 200 )
{
player_sees_spawner();
continue;
}
forward = anglesToForward( ( 0, level.player getplayerangles()[ 1 ], 0 ) );
difference = vectornormalize( difference_vec );
dot = vectordot( forward, difference );
if ( dot < 0.2 )
{
player_sees_spawner();
continue;
}
successes++ ;
if ( successes < 3 )
continue;
// player has been looking away for 3 seconds
flag_set( "player_looks_away_from_spawner" );
}
}
get_color_spawner( classname )
{
assertEx( isdefined( level.respawn_spawner ), "Tried to spawn a guy but level.respawn_spawner is not defined. Either set it to a spawner or use targetname trigger_friendly_respawn triggers." );
// if the classname is not set, just use the global respawn spawner
if ( !isdefined( classname ) )
{
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 ) )
{
return level.respawn_spawner;
}
spawner.origin = level.respawn_spawner.origin;
return spawner;
}
spawn_hidden_reinforcement( classname, fromColor )
{
level endon( "kill_color_replacements" );
spawn = undefined;
for ( ;; )
{
if ( !flag( "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 ( ;; )
{
flag_wait_either( "player_looks_away_from_spawner", "respawn_friendlies" );
flag_waitopen( "friendly_spawner_locked" );
if ( flag( "player_looks_away_from_spawner" ) || flag( "respawn_friendlies" ) )
break;
}
flag_set( "friendly_spawner_locked" );
}
spawner = get_color_spawner( classname );
spawner.count = 1;
// assertEx( spawner.spawnflags & 4, "Friendly color spawner with export " + spawner.export + " does not have UNDELETEABLE checkbox." );
spawn = spawner stalingradspawn();
if ( 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 ();
return spawn;
}
lock_spawner_for_awhile ()
{
flag_set( "friendly_spawner_locked" );
wait( 2 );
flag_clear( "friendly_spawner_locked" );
}
player_sees_spawner()
{
successes = 0;
flag_clear( "player_looks_away_from_spawner" );
}
kill_color_replacements()
{
// kills ALL color respawning
flag_clear( "friendly_spawner_locked" );
level notify( "kill_color_replacements" );
ai = getaiarray();
array_thread( ai, ::remove_replace_on_death );
}
remove_replace_on_death()
{
self.replace_on_death = undefined;
}