1515 lines
No EOL
43 KiB
Text
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;
|
|
} |