1888 lines
No EOL
51 KiB
Text
1888 lines
No EOL
51 KiB
Text
#include maps\_anim;
|
|
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\pel2_util;
|
|
#include maps\_vehicle_utility;
|
|
|
|
///////////////////////////////////
|
|
// UTILITY FUNCTIONS
|
|
///////////////////////////////////
|
|
|
|
////////////////////////////////////
|
|
// FUNCTION: retreat_truck`_behavior
|
|
// CALLED ON: a covered opel blitz truck
|
|
// PURPOSE: Sends a truck ahead to a rally point, waits until a trigger associated with the
|
|
// rally point is hit then proceeds to the next rally point.
|
|
// ADDITIONS NEEDED: None
|
|
///////////////////////////////////
|
|
retreat_truck_behavior()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self thread do_death_fx();
|
|
|
|
if( self.health <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self.health = 100;
|
|
self thread retreat_truck_behavior_node_kill();
|
|
self thread retreat_truck_player_hit_me();
|
|
|
|
movetrigger = GetEnt( self.script_noteworthy+" move trigger", "script_noteworthy" );
|
|
|
|
movetrigger waittill( "trigger" );
|
|
level notify( "retreaters", self );
|
|
|
|
if( !IsDefined( self.script_string ) )
|
|
{
|
|
for( i = 0; i < level.retreat_points.size; i++ )
|
|
{
|
|
if( isDefined( self ) && self.health > 0 && self.classname != "script_vehicle_corpse" )
|
|
{
|
|
wait_node = getVehicleNode( self.script_noteworthy+" "+level.retreat_points[i]+" wait", "script_noteworthy" );
|
|
|
|
if( isDefined( wait_node ) )
|
|
{
|
|
self setWaitNode( wait_node );
|
|
self waittill( "reached_wait_node" );
|
|
oldspeed = self GetSpeedMPH();
|
|
self setSpeed( 0, 6, 6 );
|
|
while( 1 )
|
|
{
|
|
if( !array_check_for_dupes( level.invalid_retreat_points, "stop "+level.retreat_points[i] +" wait") )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
self setSpeed( oldSpeed, oldSpeed/2 );
|
|
level notify( "retreaters", self );
|
|
}
|
|
else
|
|
{
|
|
wait(0.5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-- Empty I think... this retreater was never going to make it anyways
|
|
}
|
|
}
|
|
|
|
retreat_truck_player_hit_me()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while(1)
|
|
{
|
|
self waittill("damage", amt, guy, direction, origin, damage_type);
|
|
|
|
if(damage_type == "MOD_PROJECTILE")
|
|
{
|
|
if(IsPlayer(guy))
|
|
{
|
|
self notify("death");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Glocke: Built this function to keeps trucks and tanks from running into each other
|
|
retreat_truck_behavior_node_kill()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill( "kill retreat truck node" );
|
|
|
|
self.health = 1;
|
|
radiusDamage( self.origin, 256, 200, 200 );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_death_fx
|
|
// CALLED ON: an opel truck
|
|
// PURPOSE: Plays gibbing explosion if the game is not PG
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_death_fx()
|
|
{
|
|
self waittill( "death" );
|
|
|
|
if( is_mature() )
|
|
{
|
|
playfx( level._effect["truck_gib_explode"], self.origin );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_global_trigger
|
|
// CALLED ON: level
|
|
// PURPOSE: Waits until the trigger flagged area # all is hit then activates all remaining tanks in
|
|
// the area
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_global_trigger( area )
|
|
{
|
|
all_trigger = GetEnt( "area "+area+" trigger all", "script_noteworthy" );
|
|
trigger_array = GetEntArray( "area "+area+" trigger" );
|
|
all_trigger waittill( "trigger" );
|
|
for( i = 0; i < trigger_array.size; i++ )
|
|
{
|
|
if( isDefined( trigger_array[i] ) )
|
|
{
|
|
trigger_array[i] notify( "trigger" );
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: see2_veh_death_thread
|
|
// CALLED ON: panzers, tigers and panthers
|
|
// PURPOSE: This is used to play the custom fx that pop the vehicle turret, or just
|
|
// do the standard vehicle model swap. Turret pops happen on a percent basis based on
|
|
// which kind of tank has died.
|
|
// ADDITIONS NEEDED: The function used to spawn a turret model if the turret pop did not occur.
|
|
// This was causing intersecting physics solids and was killing the framerate
|
|
// so we need to do a model swap to the whole destroyed model here in a way
|
|
// that doesn't interfere with the rest of the vehicle death script stuff.
|
|
/////////////////////////////////
|
|
see2_veh_death_thread()
|
|
{
|
|
level notify( "destruction" );
|
|
turret_model = undefined;
|
|
turret_fx = undefined;
|
|
pop_threshold = 100;
|
|
if( self.vehicletype == "see2_panzeriv" )
|
|
{
|
|
turret_model = "vehicle_ger_tracked_panzer4_d_turret";
|
|
turret_fx = "panzer_turret_fly";
|
|
pop_threshold = 50;
|
|
}
|
|
else if( self.vehicletype == "see2_tiger" )
|
|
{
|
|
turret_model = "vehicle_ger_tracked_king_tiger_d_turret";
|
|
turret_fx = "tiger_turret_fly";
|
|
pop_threshold = 80;
|
|
}
|
|
else if( self.vehicletype == "see2_panther" )
|
|
{
|
|
turret_model = "vehicle_ger_tracked_panther_d_turret";
|
|
turret_fx = "panther_turret_fly";
|
|
pop_threshold = 60;
|
|
}
|
|
|
|
rand = randomint( 100 );
|
|
|
|
if( !isDefined( turret_fx ) || !isDefined( turret_model ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( rand > pop_threshold )
|
|
{
|
|
playfxontag( level._effect[turret_fx], self, "tag_turret" );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
turret = spawn("script_model", self.origin);
|
|
turret.angles = self GetTagAngles( "tag_turret" );
|
|
turret.origin = self GetTagOrigin( "tag_turret" );
|
|
turret setmodel(turret_model);
|
|
*/
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_tarp_flap
|
|
// CALLED ON: an animated artillery cover tarp.
|
|
// PURPOSE: This causes the tarp to animate and flap whenever the artillery fires a shot
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
#using_animtree( "see2_models" );
|
|
do_tarp_flap()
|
|
{
|
|
self endon( "stop tarp" );
|
|
self.animname = "arty tarp";
|
|
self UseAnimTree( #animtree );
|
|
targetEnt = undefined;
|
|
while( 1 )
|
|
{
|
|
while( !isDefined( targetEnt ) )
|
|
{
|
|
targetEnt = GetEnt(self.target, "targetname");
|
|
wait( 0.05 );
|
|
}
|
|
targetEnt waittill( "turret_fire" );
|
|
self anim_single_solo( self, "fire_flap" );
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: cleanup tarp
|
|
// CALLED ON: an animated artillery cover tarp
|
|
// PURPOSE: This deletes the tarp when the artillery is destroyed
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
cleanup_tarp()
|
|
{
|
|
targetEnt = undefined;
|
|
|
|
while( !isDefined( targetEnt ) )
|
|
{
|
|
targetEnt = GetEnt(self.target, "targetname");
|
|
wait( 0.05 );
|
|
}
|
|
targetEnt waittill( "death" );
|
|
self notify( "stop tarp" );
|
|
wait( 0.1 );
|
|
|
|
if(IsDefined(self.script_string))
|
|
{
|
|
sticks = [];
|
|
sticks = GetEntArray( self.script_string, "targetname" );
|
|
for(i=0; i < sticks.size; i++)
|
|
{
|
|
sticks[i] Delete();
|
|
}
|
|
}
|
|
|
|
self delete();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_arty_spawn
|
|
// CALLED ON: level
|
|
// PURPOSE: This spawns all artillery for an area. It also sets up the artillery count that is
|
|
// used to handle the destroy artillery objective.
|
|
// ADDITIONS NEEDED: Need to clean up the artillery tarps for non spawned artillery here
|
|
/////////////////////////////////
|
|
do_arty_spawn( areaNum )
|
|
{
|
|
extra_count = 0;
|
|
num_extra = get_players().size - 1;
|
|
trigger_array = getEntArray( "arty"+areaNum+" trigger", "targetname" );
|
|
for( i = 0; i < trigger_array.size; i++ )
|
|
{
|
|
should_spawn = false;
|
|
if( !isDefined( trigger_array[i].script_noteworthy ) )
|
|
{
|
|
should_spawn = true;
|
|
}
|
|
// Check to see if this is one of the artillery (targetnames arty 5 and arty 6). If it is, only spawn it if we have extra coop players
|
|
else if( isDefined( trigger_array[i].script_noteworthy ) && trigger_array[i].script_noteworthy == "extra" && extra_count < num_extra )
|
|
{
|
|
extra_count++;
|
|
should_spawn = true;
|
|
}
|
|
// If it is extra and we have already spawned as many extra as we have coop players, hide the base and don't spawn it.
|
|
else
|
|
{
|
|
//base = getEnt( trigger_array[i].script_string, "script_noteworthy" );
|
|
//base hide();
|
|
}
|
|
|
|
// Do gated spawn
|
|
if( should_spawn )
|
|
{
|
|
trigger_array[i] notify( "trigger" );
|
|
wait_network_frame();
|
|
}
|
|
}
|
|
|
|
wait( 1 );
|
|
|
|
for( j = 1; j < 6; j++ )
|
|
{
|
|
arty = getEnt( "arty "+j, "targetname" );
|
|
|
|
if( isDefined( arty ) )
|
|
{
|
|
level.enemy_armor = array_add( level.enemy_armor, arty );
|
|
arty thread cleanup_targeting();
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: self_inform_on_damage_trigger
|
|
// CALLED ON: a vehicle who will retreat
|
|
// PURPOSE: It waits until a player uses explosive damage against a damage trigger (usually at
|
|
// the same point as the tank) and then triggers the tank's move trigger performing a
|
|
// strategic retreat *See tank targetname lineveh 8 group0 to see an example of this*
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
self_inform_on_damage_trigger( event )
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "damage", damage, other, direction, origin, damage_type );
|
|
if ( maps\see2::explosive_damage( damage_type ) )
|
|
{
|
|
if( isDefined( other.script_team ) && other.script_team == "allies" )
|
|
{
|
|
self notify( event );
|
|
return;
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_player_proximity
|
|
// CALLED ON: a moving vehicle
|
|
// PURPOSE: This causes a vehicle to stop if a player reaches a specific distance from them
|
|
// ADDITIONS NEEDED: We may want to add a boolean to allow the tank to speed up if the player
|
|
// moves outside of the specified radius.
|
|
/////////////////////////////////
|
|
check_for_player_proximity( distance )
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( !isDefined( get_players() ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
for( i = 0; i < get_players().size; i++ )
|
|
{
|
|
if( distancesquared( get_players()[i].origin, self.origin ) < (distance * distance) )
|
|
{
|
|
speed = self getSpeedMPH();
|
|
if( speed > 0 )
|
|
{
|
|
self setSpeed( 0, speed/3, speed/2 );
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_spawngroup_generics
|
|
// CALLED ON: level
|
|
// PURPOSE: This spawns all the relevant vehicles for an area. Currently there are 4 areas
|
|
// group0 - area1 - Destroy artillery area
|
|
// group1 - area2 - Destroy Radiotower area
|
|
// group2 - area3 - Hardpoint/fuel depot
|
|
// group3 - area4 - Take Train station
|
|
// ADDITIONS NEEDED: Need to set up any addition moving or firing behaviors desired here.
|
|
/////////////////////////////////
|
|
setup_spawngroup_generics( groupnum )
|
|
{
|
|
max_group_tanks = 12;
|
|
level waittill( "area"+groupNum+" spawned" );
|
|
// Wait for spawn to finish
|
|
wait( 1 );
|
|
|
|
// setup looping tanks
|
|
for( i = 0; i < max_group_tanks; i++ )
|
|
{
|
|
tank = GetEnt( "loopveh "+i+" group"+groupNum, "script_noteworthy" );
|
|
if( isDefined( tank ) )
|
|
{
|
|
approach = tank check_for_approach( i, groupnum );
|
|
if( !approach )
|
|
{
|
|
tank thread loop_movement_behavior(undefined, undefined, i, groupnum);
|
|
}
|
|
tank thread moving_firing_behavior();
|
|
tank thread do_intermediate_damage_states();
|
|
level.enemy_armor = array_add( level.enemy_armor, tank );
|
|
tank thread cleanup_targeting();
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// setup advance_retreat tanks
|
|
for( i = 0; i < max_group_tanks; i++ )
|
|
{
|
|
tankname = "advance_retreat "+i+" group"+groupNum;
|
|
tank = getEnt( tankname, "script_noteworthy" );
|
|
if( isDefined( tank ) )
|
|
{
|
|
tank thread advance_retreat_behavior( i, groupNum );
|
|
tank thread moving_firing_behavior();
|
|
tank thread do_intermediate_damage_states();
|
|
level.enemy_armor = array_add( level.enemy_armor, tank );
|
|
tank thread cleanup_targeting();
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// setup linear tanks
|
|
for( i = 0; i < max_group_tanks; i++ )
|
|
{
|
|
tank = GetEnt( "lineveh "+i+" group"+groupNum, "script_noteworthy" );
|
|
if( isDefined( tank ) )
|
|
{
|
|
tank thread linear_movement_behavior(undefined, undefined, i, groupNum);
|
|
tank thread moving_firing_behavior();
|
|
tank thread do_intermediate_damage_states();
|
|
level.enemy_armor = array_add( level.enemy_armor, tank );
|
|
tank thread cleanup_targeting();
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// setup linear tanks with backing up
|
|
for( i = 0; i < max_group_tanks; i++ )
|
|
{
|
|
tank = GetEnt( "lineveh_with_backup "+i+" group"+groupNum, "script_noteworthy" );
|
|
if( isDefined( tank ) )
|
|
{
|
|
tank thread linear_movement_behavior_adjusted(undefined, undefined, i, groupNum);
|
|
tank thread moving_firing_behavior();
|
|
tank thread do_intermediate_damage_states();
|
|
level.enemy_armor = array_add( level.enemy_armor, tank );
|
|
tank thread cleanup_targeting();
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
// setup static tanks
|
|
for( i = 0; i < max_group_tanks; i++ )
|
|
{
|
|
tank = GetEnt( "staticveh "+i+" group"+groupNum, "script_noteworthy" );
|
|
if( isDefined( tank ) )
|
|
{
|
|
tank thread static_firing_behavior();
|
|
tank thread do_intermediate_damage_states();
|
|
level.enemy_armor = array_add( level.enemy_armor, tank );
|
|
tank thread cleanup_targeting();
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( groupnum == 3 )
|
|
{
|
|
level thread maps\see2::setup_airstrike_planes();
|
|
}
|
|
|
|
//setup tank retreats
|
|
level thread do_vehicle_retreats( groupNum );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_intermediate_damage_states
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: When the vehicle reaches below half health, it will do a model swap and play a looping
|
|
// smoke fx on the vehicle
|
|
// ADDITIONS NEEDED: The model swap is currently disabled due to issues with vehicle systems not
|
|
// recognizing the intermediate damaged model.
|
|
/////////////////////////////////
|
|
do_intermediate_damage_states()
|
|
{
|
|
self endon( "death" );
|
|
int_damage_model = undefined;
|
|
int_dmg_fx = undefined;
|
|
max_health = 100000000000;
|
|
damage_count = 0;
|
|
if( self.vehicletype == "see2_panzeriv" )
|
|
{
|
|
int_damage_model = "vehicle_ger_tracked_panzer4v1_dmg1";
|
|
int_dmg_fx = "panzer_int_dmg";
|
|
max_health = 1000;
|
|
}
|
|
if( self.vehicletype == "see2_panther" )
|
|
{
|
|
int_damage_model = "vehicle_ger_tracked_panther_dmg1";
|
|
int_dmg_fx = "panther_int_dmg";
|
|
max_health = 2000;
|
|
}
|
|
if( self.vehicletype == "see2_tiger" )
|
|
{
|
|
int_damage_model = "vehicle_ger_tracked_king_tiger_dmg1";
|
|
int_dmg_fx = "tiger_int_dmg";
|
|
max_health = 3000;
|
|
}
|
|
if( self.vehicletype == "see2_t34" )
|
|
{
|
|
int_damage_model = "vehicle_rus_tracked_t34_dmg1";
|
|
int_dmg_fx = "t34_int_dmg";
|
|
max_health = 2000;
|
|
}
|
|
if( self.vehicletype == "see2_t34" )
|
|
{
|
|
int_damage_model = "veh_rus_tracked_ot34_dmg1";
|
|
max_health = 3000;
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "damage", amount );
|
|
if( damage_count >= (max_health/2) )
|
|
{
|
|
//self setModel( int_damage_model );
|
|
if( isDefined( int_dmg_fx ) )
|
|
{
|
|
self thread play_int_dmg_fx( int_dmg_fx );
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
damage_count += amount;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: play_int_dmg_fx
|
|
// CALLED ON: vehicle
|
|
// PURPOSE: This is a threaded function that plays the intermediate damage fx for a tank
|
|
// until it is destroyed
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
play_int_dmg_fx( fx_name )
|
|
{
|
|
self endon( "death" );
|
|
while( 1 )
|
|
{
|
|
playfxontag( level._effect[fx_name], self, "tag_origin" );
|
|
wait( 0.3 );
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////
|
|
// MOVEMENT FUNCTIONS
|
|
///////////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: advance_retreat_behavior
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: This causes a vehicle to advance along a path once its move trigger has been hit. Once
|
|
// it has reached a percent damage globally chosen for the level, it finds the nearest
|
|
// retreat node belonging to it, sets up the switchNode system and begins backing up on
|
|
// the retreat path
|
|
// ADDITIONS NEEDED: Path switching still kind of looks bad, needs a second pass and needs speed
|
|
// scaling prior to doing the switch.
|
|
// CHANGES MADE:
|
|
// This behavior had functionality problems if a tank had already reached the very end of it's path.
|
|
/////////////////////////////////
|
|
advance_retreat_behavior( identifier, groupNum )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self thread check_for_player_proximity( 1500 );
|
|
|
|
self thread wait_for_retreat( identifier, groupNum);
|
|
start_node = getVehicleNode( self.target, "targetname" );
|
|
self.curr_node = getVehicleNode( start_node.target, "targetname" );
|
|
|
|
end_node = getVehicleNode( "line "+identifier+" group"+groupNum+" advance end", "script_noteworthy" );
|
|
switch_node = getVehicleNode( "line "+identifier+" group"+groupNum+" retreat start", "script_noteworthy" );
|
|
|
|
while( 1 )
|
|
{
|
|
self setWaitNode( self.curr_node );
|
|
|
|
self waittill( "reached_wait_node" );
|
|
|
|
self.curr_node = getVehicleNode( self.curr_node.target, "targetname" );
|
|
|
|
//-- Glocke: fix for the retreat breaking if the tank actually reached it's last node
|
|
if( !IsDefined(self.curr_node.target) )
|
|
{
|
|
self SetSpeed( 0, 100, 100 ); // stop the tank, because later we will need to set a switchnode and then resume speed
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_retreat
|
|
// CALLED ON: a vehicle running advance_retreat functionality
|
|
// PURPOSE: Waits until the damage threshold has been reached and starts vehicle retreating
|
|
// ADDITIONS NEEDED: Do a speed scale when changing directions
|
|
/////////////////////////////////
|
|
wait_for_retreat( identifier, groupNum )
|
|
{
|
|
self endon( "death" );
|
|
|
|
retreat_nodes = getVehicleNodeArray( "retreat "+identifier+" group"+groupNum, "script_noteworthy" );
|
|
|
|
if( retreat_nodes.size < 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
best_node = undefined;
|
|
best_dist = 10000000000;
|
|
while( 1 )
|
|
{
|
|
self waittill( "damage" );
|
|
|
|
if( ((self.health-self.healthbuffer) / (self.maxhealth-self.healthbuffer)) <= level.retreat_threshold )
|
|
{
|
|
for( i = 0; i < retreat_nodes.size; i++ )
|
|
{
|
|
dist = distanceSquared( retreat_nodes[i].origin, self.origin );
|
|
if( dist < best_dist )
|
|
{
|
|
best_node = retreat_nodes[i];
|
|
best_dist = dist;
|
|
}
|
|
}
|
|
|
|
if( isDefined( best_node ) )
|
|
{
|
|
if( self getSpeed() > 0 )
|
|
{
|
|
self setSwitchNode( self.curr_node, best_node );
|
|
self.curr_node = best_node;
|
|
self setWaitNode( best_node );
|
|
}
|
|
else
|
|
{
|
|
//-- In this case the best node is no longer accurate, we need a node
|
|
//-- that is close to the location of the switchnode, not the location of the tank.
|
|
for( i = 0; i < retreat_nodes.size; i++ )
|
|
{
|
|
dist = distanceSquared( retreat_nodes[i].origin, self.curr_node.origin );
|
|
if( dist < best_dist )
|
|
{
|
|
best_node = retreat_nodes[i];
|
|
best_dist = dist;
|
|
}
|
|
}
|
|
|
|
self setSwitchNode( self.curr_node, best_node );
|
|
self.curr_node = best_node;
|
|
self setWaitNode( best_node );
|
|
self ResumeSpeed( 5 );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_for_approach
|
|
// CALLED ON: a vehicle running looping movement behavior
|
|
// PURPOSE: This checks to see if there is a linear approach to a patrol loop and navigates this
|
|
// approach if it exists
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_for_approach( identifier, groupNum, signalArray )
|
|
{
|
|
if( isDefined( groupNum ) )
|
|
{
|
|
startLeadin = "loop "+identifier+" group"+groupNum+" leadin start";
|
|
endLeadin = "loop "+identifier+" group"+groupNum+" leadin end";
|
|
beginLoop = "loop "+identifier+" part0 group"+groupNum+ " start";
|
|
}
|
|
else
|
|
{
|
|
startLeadin = "loop "+identifier+" leadin start";
|
|
endLeadin = "loop "+identifier+" leadin end";
|
|
beginLoop = "loop "+identifier+" part0 start";
|
|
}
|
|
approachStartNode = GetVehicleNode( startLeadin, "script_noteworthy" );
|
|
approachEndNode = GetVehicleNode( endLeadin, "script_noteworthy" );
|
|
if( isDefined( approachStartNode ) && isDefined( approachEndNode ) )
|
|
{
|
|
self thread do_approach_to_loop( endLeadin, beginLoop, identifier, groupNum, signalArray );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_approach_to_loop
|
|
// CALLED ON: a loop vehicle
|
|
// PURPOSE: Causes the vehicle to move along the approach path until it reaches the end, then
|
|
// it switches to the loop
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_approach_to_loop( endLeadin, beginLoop, identifier, groupNum, signalArray )
|
|
{
|
|
destNode = GetVehicleNode( endLeadin, "script_noteworthy" );
|
|
switchNode = GetVehicleNode( beginLoop, "script_noteworthy" );
|
|
self thread moving_firing_behavior();
|
|
self setSwitchNode( destNode, switchNode );
|
|
self setWaitNode( switchNode );
|
|
self waittill( "reached_wait_node" );
|
|
|
|
self loop_movement_behavior( 10, 5, identifier, groupNum, signalArray );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: linear_movement_behavior
|
|
// CALLED ON: a linear movement vehicle
|
|
// PURPOSE: This causes a tank to move linearly along a series of linear path parts, making
|
|
// making decisions at junctions based on signals. The choice behavior is untested
|
|
// and probably does not work
|
|
// ADDITIONS NEEDED: None, not really used
|
|
/////////////////////////////////
|
|
linear_movement_behavior( speed, accel, identifier, groupNum, signalArray )
|
|
{
|
|
self thread wait_for_signals( signalArray, identifier );
|
|
if( isDefined( speed ) && isDefined( accel ) )
|
|
{
|
|
self setSpeed( speed, accel );
|
|
}
|
|
else if( isDefined( speed ) )
|
|
{
|
|
self setSpeed( speed );
|
|
}
|
|
|
|
self thread check_for_player_proximity( 1500 );
|
|
|
|
// continue moving through
|
|
for( i = 0; ; i++ )
|
|
{
|
|
self.currPart = i;
|
|
if( isDefined( groupNum ) )
|
|
{
|
|
destName = "line "+identifier+" group"+groupNum+" part"+i+" end";
|
|
destNode = GetVehicleNode( destName, "script_noteworthy" );
|
|
switchName = "line"+identifier+" part"+(i+1)+" group"+groupNum+" start";
|
|
switchNode = GetVehicleNode( switchName, "script_noteworthy" );
|
|
}
|
|
else
|
|
{
|
|
destName = "line"+identifier+" part"+i+" end";
|
|
destNode = GetVehicleNode( destName, "script_noteworthy" );
|
|
switchName = "line"+identifier+" part"+(i+1)+" start";
|
|
switchNode = GetVehicleNode( switchName, "script_noteworthy" );
|
|
}
|
|
if( !isDefined(destNode) || !isDefined(switchNode) )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
self setSwitchNode( destNode, switchNode );
|
|
self setWaitNode( switchNode );
|
|
self waittill( "reached_wait_node" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
linear_movement_behavior_adjusted( speed, accel, identifier, groupNum, signalArray )
|
|
{
|
|
self endon("death");
|
|
|
|
//self thread check_for_player_proximity( 1500 );
|
|
self thread linear_movement_behavior_adjusted_hit_react();
|
|
|
|
//-- if I reached the end of my node, wait until I get shot and then roll backward.
|
|
self waittill("reached_end_node");
|
|
|
|
if(!IsDefined(self.script_noteworthy))
|
|
{
|
|
return;
|
|
}
|
|
|
|
backup_path = GetVehicleNode( self.script_noteworthy + " path", "script_noteworthy" );
|
|
self AttachPath( backup_path );
|
|
self SetSpeed(0, 50, 50);
|
|
self.attached_to_backup_path_but_not_moving = true;
|
|
|
|
//-- See if the backup_path ends on a script_string, if it does, then reset that to the new backup path
|
|
temp_node = backup_path;
|
|
while( IsDefined(temp_node.target) )
|
|
{
|
|
temp_node = GetVehicleNode( temp_node.target, "targetname" );
|
|
}
|
|
|
|
if( IsDefined(temp_node.script_noteworthy) )
|
|
{
|
|
self.script_noteworthy = temp_node.script_noteworthy;
|
|
}
|
|
else
|
|
{
|
|
self.script_noteworthy = undefined;
|
|
}
|
|
|
|
time_hit = 0;
|
|
|
|
while(1)
|
|
{
|
|
self waittill("damage", amount, attacker, direction, point, type);
|
|
|
|
if(type == "MOD_PROJECTILE")
|
|
{
|
|
time_hit++;
|
|
}
|
|
|
|
if( IsPlayer(attacker) )
|
|
{
|
|
if(self.vehicletype == "see2_panther")
|
|
{
|
|
if(time_hit >= 1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
self StartPath();
|
|
self ResumeSpeed(5);
|
|
self.attached_to_backup_path_but_not_moving = undefined;
|
|
|
|
self thread linear_movement_behavior_adjusted( speed, accel, identifier, groupNum, signalArray );
|
|
|
|
}
|
|
|
|
linear_movement_behavior_adjusted_hit_react()
|
|
{
|
|
self endon("death");
|
|
wait(2); //let the vehicle get up to speed
|
|
|
|
while(1)
|
|
{
|
|
self waittill("damage", amount, attacker, direction, point, type);
|
|
|
|
if(type != "MOD_PROJECTILE")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( IsPlayer(attacker) )
|
|
{
|
|
old_speed = self GetSpeedMPH();
|
|
|
|
self SetSpeed( old_speed * 0.7, 5, 5 );
|
|
|
|
if( !IsDefined(self.attached_to_backup_path_but_not_moving) )
|
|
{
|
|
wait(1.5);
|
|
self ResumeSpeed(5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: loop_movement_behavior
|
|
// CALLED ON: a loop vehicle
|
|
// PURPOSE: This causes a vehicle to switch between the parts of a loop path by setting switch
|
|
// nodes. It also has signal based choice behavior similar to linear movement behavior
|
|
// but it also doesn't work.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
loop_movement_behavior( speed, accel, identifier, groupNum, signalArray )
|
|
{
|
|
if( isDefined( speed ) && isDefined( accel ) )
|
|
{
|
|
self setSpeed( speed, accel );
|
|
}
|
|
else if( isDefined( speed ) )
|
|
{
|
|
self setSpeed( speed );
|
|
}
|
|
|
|
for( i = 0; ; i++ )
|
|
{
|
|
self set_path_wait_points( groupNum );
|
|
self.currPart = i;
|
|
if( isDefined( groupNum ) )
|
|
{
|
|
destName = "loop "+identifier+" group"+groupNum+" part"+i+" end";
|
|
destNode = GetVehicleNode( destName, "script_noteworthy" );
|
|
switchName = "loop "+identifier+" part"+(i+1)+" group"+groupNum+" start";
|
|
switchNode = GetVehicleNode( switchName, "script_noteworthy" );
|
|
}
|
|
else
|
|
{
|
|
destName = "loop"+identifier+" part"+i+" end";
|
|
destNode = GetVehicleNode( destName, "script_noteworthy" );
|
|
switchName = "loop"+identifier+" part"+(i+1)+" start";
|
|
switchNode = GetVehicleNode( switchName, "script_noteworthy" );
|
|
}
|
|
if( !isDefined(switchNode) )
|
|
{
|
|
if( isDefined( groupNum ) )
|
|
{
|
|
switchNode = GetVehicleNode( "loop "+identifier+" part0 group"+groupNum+" start", "script_noteworthy" );
|
|
}
|
|
else
|
|
{
|
|
switchNode = GetVehicleNode( "loop "+identifier+" part0 start", "script_noteworthy" );
|
|
}
|
|
i = -1;
|
|
}
|
|
wait( 0.05 );
|
|
self setSwitchNode( destNode, switchNode );
|
|
self setWaitNode( switchNode );
|
|
self waittill( "reached_wait_node" );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_vehicle_retreats
|
|
// CALLED ON: level
|
|
// PURPOSE: Sets up threads for all retreat triggers
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_vehicle_retreats( groupNum )
|
|
{
|
|
retreatTriggers = GetEntArray( "retreat trigger group"+groupNum, "script_noteworthy" );
|
|
for( i = 0; i < retreatTriggers.size; i++ )
|
|
{
|
|
retreatTriggers[i] thread wait_for_vehicle_retreat();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_vehicle_retreat
|
|
// CALLED ON: a retreat trigger
|
|
// PURPOSE: This waits till the retreat trigger is set off by a player or allied tank then
|
|
// activates its targeted trigger.
|
|
// ADDITIONS NEEDED:
|
|
/////////////////////////////////
|
|
wait_for_vehicle_retreat()
|
|
{
|
|
self thread self_inform_on_damage_trigger( "start retreat" );
|
|
|
|
self waittill( "start retreat" );
|
|
|
|
if( !isDefined( self.target ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetEnt(self.target, "targetname") notify( "trigger" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: custom_array_remove
|
|
// CALLED ON: level
|
|
// PURPOSE: Removes an element from an array, while preserving order.
|
|
// ADDITIONS NEEDED:
|
|
/////////////////////////////////
|
|
custom_array_remove( array, element )
|
|
{
|
|
new_array = [];
|
|
for( i = 0; i < array.size; i++ )
|
|
{
|
|
if( array[i] == element )
|
|
{
|
|
continue;
|
|
}
|
|
new_array[new_array.size] = array[i];
|
|
}
|
|
return new_array;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_arrive
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: Waits until a vehicle arrives at a reach node then causes it to stop
|
|
// ADDITIONS NEEDED:
|
|
/////////////////////////////////
|
|
wait_for_arrive()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "reached_wait_node" );
|
|
self.current_node = self.target_node;
|
|
// This is an incredibly badly named call for stopping the vehicle - LDS
|
|
self returnplayercontrol();
|
|
wait( 0.05 );
|
|
self setSpeed( 0, 6, 6 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_player_advance
|
|
// CALLED ON: level
|
|
// PURPOSE: This waits until an advance trigger is hit then sets that as the desired advance level
|
|
// for all friendly AI tanks
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_player_advance()
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", guy);
|
|
if( !isPlayer( guy ) )
|
|
{
|
|
continue;
|
|
}
|
|
level.current_advance_level = self.script_noteworthy;
|
|
self delete();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: lerp_to_stop
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: When requested, a vehicle will lerp to a stop over a short period of time
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
lerp_to_stop()
|
|
{
|
|
self endon( "death" );
|
|
while( 1 )
|
|
{
|
|
current_speed = self getspeedMPH();
|
|
current_speed -= (current_speed * 0.5 * 0.05);
|
|
if( current_speed < 1 )
|
|
{
|
|
current_speed = 0;
|
|
}
|
|
self setSpeed( current_speed, current_speed/2, current_speed/2 );
|
|
if( current_speed == 0 )
|
|
{
|
|
return;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: set_path_wait_points
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: This is the wait point choice stuff that doesn't work for line movement and loop movement
|
|
// behavior.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
set_path_wait_points( lineOrLoop, identifier, groupNum, partNum)
|
|
{
|
|
nextNode = GetEnt(lineOrLoop+" "+identifier+" group"+groupNum+" part"+partNum+" start", "targetname" );
|
|
while( 1 )
|
|
{
|
|
if( isDefined( nextNode ) )
|
|
{
|
|
if( isDefined( nextNode.script_string ) )
|
|
{
|
|
self setWaitNode( nextNode );
|
|
self waittill( "reached_wait_node" );
|
|
self waittill( nextNode.script_string );
|
|
}
|
|
if( isDefined( nextNode.target ) )
|
|
{
|
|
nextNode = GetEnt( nextNode.target, "targetname" );
|
|
}
|
|
else
|
|
{
|
|
nextNode = undefined;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_signals
|
|
// CALLED ON: a line or loop vehicle
|
|
// PURPOSE: This is more of the choice stuff that doesn't work
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_signals( signalArray, identifier, loop )
|
|
{
|
|
if( !isDefined( loop ) )
|
|
loop = false;
|
|
check_array = [];
|
|
if( isDefined( signalArray ) )
|
|
{
|
|
for( i = 0; i < signalArray.size; i++ )
|
|
{
|
|
if( !loop )
|
|
{
|
|
check_array[i].forkpoint = "line"+identifier+"part"+(signalArray[i].part-1)+" end";
|
|
check_array[i].targetpoint = "line"+identifier+"part"+signalArray[i].part+signalArray[i].letter+" start";
|
|
}
|
|
else
|
|
{
|
|
check_array[i].forkpoint = "loop"+identifier+"part"+(signalArray[i].part-1)+" end";
|
|
check_array[i].targetpoint = "loop"+identifier+"part"+signalArray[i].part+signalArray[i].letter+" start";
|
|
test_node = GetVehicleNode( check_array[i].targetpoint, "script_noteworthy" );
|
|
if( !isDefined( test_node ) )
|
|
{
|
|
check_array[i].targetpoint = "loop"+identifier+"part0"+signalArray[i].letter+" start";
|
|
}
|
|
}
|
|
check_array[i].waitsignal = signalArray[i].waitsignal;
|
|
check_array[i].part = signalArray[i].part-1;
|
|
self thread wait_for_signal( check_array[i] );
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_signal
|
|
// CALLED ON: a line or loop vehicle
|
|
// PURPOSE: Yep, choice stuff, doesn't work.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_signal( signal )
|
|
{
|
|
self waittill( signal.waitsignal );
|
|
if( self.currPart == signal.part )
|
|
{
|
|
destNode = GetVehicleNode( signal.forkpoint, "script_noteworthy" );
|
|
switchNode = GetVehicleNode( signal.targetpoint, "script_noteworthy" );
|
|
if( isDefined( destNode ) && isDefined( switchNode ) )
|
|
{
|
|
self setSwitchNode( destNode, switchNode );
|
|
self thread wait_for_switch_then_update(switchNode, signal.part);
|
|
}
|
|
}
|
|
|
|
self thread wait_for_signal( signal );
|
|
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_switch_then_update
|
|
// CALLED ON: a line or loop vehicle
|
|
// PURPOSE: Choice. blah. blah. blah. Does not work. blah. blah.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_switch_then_update( node, part )
|
|
{
|
|
self endon( "new switch command" );
|
|
wait( 0.05 );
|
|
self notify( "new switch command" );
|
|
|
|
self setWaitNode( node );
|
|
self waittill( "reached_wait_node" );
|
|
self.currPart = part + 1;
|
|
}
|
|
|
|
////////////////////////////////////////////
|
|
// COMBAT FUNCTIONS
|
|
////////////////////////////////////////////
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: arty_behavior
|
|
// CALLED ON: a flak 88
|
|
// PURPOSE: Waits until a player enters its trigger, then targets and tries to kill the player
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
arty_behavior()
|
|
{
|
|
trigger = GetEnt( self.targetname+" trigger", "script_noteworthy" );
|
|
damage_trigger = GetEnt( self.targetname+" damage trigger", "script_noteworthy" );
|
|
damage_trigger thread maps\see2::inform_on_damage_trigger( damage_trigger.script_noteworthy );
|
|
trigger thread maps\see2::inform_on_touch_trigger( trigger.script_noteworthy );
|
|
while( 1 )
|
|
{
|
|
level waittill_either( damage_trigger.script_noteworthy, trigger.script_noteworthy );
|
|
current_target = self maps\_vehicle::get_nearest_target( level.player_tanks );
|
|
if( isDefined( current_target ) )
|
|
{
|
|
self.customTarget = current_target;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: arty_custom_targeting
|
|
// CALLED ON: a flak 88
|
|
// PURPOSE: causes an artillery to switch back to its phantom target if told to
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
arty_custom_targeting()
|
|
{
|
|
while( 1 )
|
|
{
|
|
level waittill( "target this", ent );
|
|
self.customTarget = level.customTarget;
|
|
level waittill( "stop target this" );
|
|
self.customTarget = GetStruct( self.target, "targetname" );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: moving_firing_behavior
|
|
// CALLED ON: an enemy vehicle
|
|
// PURPOSE: This gets the closest friendly armor unit. If it is close enough to target it tracks
|
|
// it with its turret. If it gets within the firing radius, the tank calls request_target
|
|
// and begins firing on the entity. If it goes outside of this radius it will stop
|
|
// firing and acquire new targets, potentially.
|
|
// ADDITIONS NEEDED: Add speed based inaccuracy
|
|
/////////////////////////////////
|
|
moving_firing_behavior()
|
|
{
|
|
if( self.vehicletype == "opel" )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self notify( "kill old firing behavior" );
|
|
wait( 0.05 );
|
|
self endon( "kill old firing behavior" );
|
|
self endon( "death" );
|
|
|
|
if( !isDefined( get_players() ) )
|
|
{
|
|
while( 1 )
|
|
{
|
|
if( isDefined( get_players() ) )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
current_target = undefined;
|
|
best_target = undefined;
|
|
while( 1 )
|
|
{
|
|
if( isDefined( level.custom_target ) )
|
|
{
|
|
best_target = level.custom_target;
|
|
current_target = level.custom_target;
|
|
myOrigin = self.origin + (0,0,200);// gettagorigin( "tag_barrel" );
|
|
theirOrigin = current_target.origin + (0, 0, 200);
|
|
trace = bullettrace( myOrigin, theirOrigin, false, undefined );
|
|
if( trace["fraction"] < 0.95 || current_target == self)
|
|
{
|
|
current_target = undefined;
|
|
wait( 3 );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( !isDefined( current_target ) || current_target.health <= 0 || distancesquared( current_target.origin, self.origin ) > (level.see2_max_tank_target_dist*level.see2_max_tank_target_dist) || current_target.classname == "script_vehicle_corpse")
|
|
{
|
|
best_target = self maps\_vehicle::get_nearest_target( level.player_tanks );
|
|
current_target = self maps\see2::request_target( best_target );
|
|
myOrigin = self.origin + (0,0,200);// gettagorigin( "tag_barrel" );
|
|
theirOrigin = current_target.origin + (0, 0, 200);
|
|
trace = bullettrace( myOrigin, theirOrigin, false, undefined );
|
|
if( trace["fraction"] < 0.95 || current_target == self)
|
|
{
|
|
current_target = undefined;
|
|
wait( 3 );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( distancesquared( current_target.origin, self.origin ) > (level.see2_max_tank_firing_dist*level.see2_max_tank_firing_dist) )
|
|
{
|
|
wait( 3 );
|
|
continue;
|
|
}
|
|
|
|
if( self.health < 1 )
|
|
{
|
|
self notify( "death" );
|
|
return;
|
|
}
|
|
|
|
self thread set_tank_accuracy( current_target, best_target );
|
|
self thread wait_fire_weapon( current_target );
|
|
|
|
while( isDefined( current_target ) && isDefined( best_target ) && distancesquared( current_target.origin, self.origin ) < level.see2_max_tank_target_dist*level.see2_max_tank_target_dist && current_target.classname != "script_vehicle_corpse" )
|
|
{
|
|
if( isDefined( self.my_target_point ) )
|
|
{
|
|
self setturrettargetvec( self.my_target_point );
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
self notify( "need new target" );
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: notify_on_advance_trigger
|
|
// CALLED ON: level
|
|
// PURPOSE: setup the array of friendlies to tell about new advance orders
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
notify_on_advance_trigger()
|
|
{
|
|
if( !isDefined( level.support_ai ) )
|
|
{
|
|
level.support_ai = [];
|
|
}
|
|
if( array_check_for_dupes( level.support_ai, self ) )
|
|
{
|
|
level.support_ai = array_add( level.support_ai, self );
|
|
}
|
|
|
|
self waittill( "death" );
|
|
|
|
level.support_ai = array_remove( level.support_ai, self );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: setup_friendly_advance_triggers
|
|
// CALLED ON: level
|
|
// PURPOSE: Threads the necessary functions on all advance triggers
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
setup_friendly_advance_triggers()
|
|
{
|
|
advance_triggers = GetEntArray( "friendly advance trigger", "targetname" );
|
|
for( i = 0; i < advance_triggers.size; i++ )
|
|
{
|
|
advance_triggers[i] thread wait_for_player_advance();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_depth_setup
|
|
// CALLED ON: a friendly AI
|
|
// PURPOSE: Sets the depth of all nodes in a friendly path for easy outing advance orders that
|
|
// go backwards
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_depth_setup()
|
|
{
|
|
current_node = GetVehicleNode( self.target, "targetname" );
|
|
current_depth = 0;
|
|
next_node = undefined;
|
|
self.current_path_depth = current_depth;
|
|
|
|
while( 1 )
|
|
{
|
|
if(isDefined(current_node))
|
|
{
|
|
current_node.path_depth = current_depth;
|
|
if( !isDefined( current_node.target ) )
|
|
{
|
|
break;
|
|
}
|
|
current_node = GetVehicleNode( current_node.target, "targetname" );
|
|
current_depth++;
|
|
|
|
if(current_depth%10 == 0)
|
|
{
|
|
wait(0.05);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_player_support
|
|
// CALLED ON: a friendly AI
|
|
// PURPOSE: This causes a friendly AI to exhibit the advance behavior and friendly firing
|
|
// behavior required of friendly tanks.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_player_support()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "stop supporting" );
|
|
|
|
self.scanning = true;
|
|
self do_depth_setup();
|
|
|
|
self thread do_friendly_firing();
|
|
self thread do_turret_scanning();
|
|
self thread wait_for_arrive();
|
|
self thread notify_on_advance_trigger();
|
|
self thread check_should_stop();
|
|
|
|
while( 1 )
|
|
{
|
|
if( isDefined( level.current_advance_level ) )
|
|
{
|
|
current_support_positions = getVehicleNodeArray( level.current_advance_level, "script_noteworthy" );
|
|
}
|
|
else
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
my_node = undefined;
|
|
|
|
for( i = 0; i < current_support_positions.size; i++ )
|
|
{
|
|
if( current_support_positions[i].script_string == self.script_noteworthy )
|
|
{
|
|
my_node = current_support_positions[i];
|
|
}
|
|
}
|
|
|
|
if( !isDefined( self.target_node ) || (isDefined( my_node ) && (my_node != self.target_node) && my_node.path_depth > self.current_path_depth) )
|
|
{
|
|
self.target_node = my_node;
|
|
self.current_path_depth = my_node.path_depth;
|
|
self setWaitNode( my_node );
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: cleanup_targeting
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: Cleans the vehicle out of the targeting arrays on death
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
cleanup_targeting()
|
|
{
|
|
self waittill( "death" );
|
|
|
|
if( !array_check_for_dupes( level.enemy_armor, self ) )
|
|
{
|
|
level.enemy_armor = array_remove( level.enemy_armor, self );
|
|
}
|
|
|
|
if( !array_check_for_dupes( level.player_tanks, self ) )
|
|
{
|
|
level.player_tanks = array_remove( level.player_tanks, self );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: check_should_stop
|
|
// CALLED ON: a friendly AI
|
|
// PURPOSE: This keeps friendly tanks from barrelling through one another, each has a script_int
|
|
// (the host's is 0) and they stop if within 1000 units of any tank with a lower scrip_int
|
|
// than theirs. They continue moving once this is no longer the case.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
check_should_stop()
|
|
{
|
|
self endon( "death" );
|
|
|
|
distance = 1000;
|
|
while( 1 )
|
|
{
|
|
self.should_stop = false;
|
|
for( i = 0; i < level.player_tanks.size; i++ )
|
|
{
|
|
if( level.player_tanks[i].script_int < self.script_int )
|
|
{
|
|
if( distanceSquared( self.origin, level.player_tanks[i].origin ) < distance*distance )
|
|
{
|
|
self.should_stop = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !isDefined( self.current_node ) || !isDefined( self.target_node ) )
|
|
{
|
|
self.should_stop = true;
|
|
}
|
|
else if( self.current_node == self.target_node )
|
|
{
|
|
self.should_stop = true;
|
|
}
|
|
|
|
if( !self.should_stop )
|
|
{
|
|
self setSpeed( 12, 6 );
|
|
}
|
|
else
|
|
{
|
|
self.should_stop = true;
|
|
self returnplayercontrol();
|
|
wait( 0.05 );
|
|
self setSpeed( 0, 6, 6 );
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_friendly_firing
|
|
// CALLED ON: a friendly AI
|
|
// PURPOSE: This causes the friendlies to acquire and fire on enemy tanks
|
|
// ADDITIONS NEEDED: From Corky - Friendlies need to be less accurate
|
|
/////////////////////////////////
|
|
do_friendly_firing()
|
|
{
|
|
self endon( "death" );
|
|
get_players()[0] endon( "death" );
|
|
|
|
main_turret_dist = 2500;
|
|
hull_dist = 2000;
|
|
fire_time = 5;
|
|
time_since_fire = 0;
|
|
|
|
while( 1 )
|
|
{
|
|
main_turret_target = undefined;
|
|
hull_target = undefined;
|
|
|
|
main_turret_target = self maps\_vehicle::get_nearest_target( level.enemy_armor );
|
|
hull_target = self maps\_vehicle::get_nearest_target( level.enemy_infantry );
|
|
|
|
if( isDefined( main_turret_target ) && main_turret_target.classname != "script_vehicle_corpse" )
|
|
{
|
|
if( distanceSquared( main_turret_target.origin, self.origin ) < (main_turret_dist * main_turret_dist) )
|
|
{
|
|
self notify ( "stop scanning" );
|
|
self.scanning = false;
|
|
trace = bullettrace( self.origin+(0, 0, 200), main_turret_target.origin+(0, 0, 200), false, main_turret_target );
|
|
self setTurretTargetEnt( main_turret_target );
|
|
if( trace["fraction"] > 0.95 )
|
|
{
|
|
self fireweapon();
|
|
}
|
|
}
|
|
else if( distanceSquared( main_turret_target.origin, self.origin ) < (2 * main_turret_dist * main_turret_dist) )
|
|
{
|
|
self notify ( "stop scanning" );
|
|
self.scanning = false;
|
|
self setTurretTargetVec( vectorToAngles( main_turret_target.origin - self.origin ) );
|
|
}
|
|
else
|
|
{
|
|
self.scanning = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self.scanning = true;
|
|
}
|
|
|
|
if( isDefined( hull_target ) )
|
|
{
|
|
if( distanceSquared( hull_target.origin, self.origin ) < (hull_dist * hull_dist) )
|
|
{
|
|
self aim_and_fire_hull( hull_target, fire_time );
|
|
}
|
|
else
|
|
{
|
|
self notify( "need new flamethrower target" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self notify( "need new flamethrower target" );
|
|
}
|
|
wait( 3 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_turret_scanning
|
|
// CALLED ON: a friendly vehicle
|
|
// PURPOSE: Makes the friendly vehicle scan with its turret when its self.scanning is set to true.
|
|
// Makes them feel a little less wooden
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_turret_scanning()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
if( self.scanning )
|
|
{
|
|
angles = ( randomintrange(-5, 10), randomintrange( -45, 45 ), 0 );
|
|
angles += self.angles;
|
|
self setTurretTargetVec( self.origin + (5000*anglesToForward( angles )) );
|
|
}
|
|
wait( randomintrange( 6, 8 ) );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: aim_and_fire_hull
|
|
// CALLED ON: a friendly vehicle
|
|
// PURPOSE: This causes them to fire their hull gun in a quick burst
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
aim_and_fire_hull( targetEnt, burst_time )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self maps\_vehicle::mgon();
|
|
|
|
wait( burst_time );
|
|
|
|
self maps\_vehicle::mgoff();
|
|
|
|
self endon( "need new flamethrower target" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: set_tank_accuracy
|
|
// CALLED ON: an enemy vehicle
|
|
// PURPOSE: This requests a delayed position from the breadcrumb system. Delay is based on level
|
|
// difficulty and distance from target
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
set_tank_accuracy( curr_target, best_target, min_speed_for_motion, time_before_accurate, max_offset )
|
|
{
|
|
self endon( "need new target" );
|
|
self endon( "death" );
|
|
|
|
lastpos = curr_target.origin;
|
|
time_stationary = 0;
|
|
time_since_last_adjust = 0;
|
|
while( 1 )
|
|
{
|
|
if( !isDefined( curr_target ) || !isDefined( best_target ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
delay_frac = distanceSquared( self.origin, curr_target.origin ) / (3500 * 3500);
|
|
if( delay_frac > 1 )
|
|
{
|
|
delay_frac = 1;
|
|
}
|
|
curr_delay = 0.5 + level.see2_base_lag_time*delay_frac;
|
|
|
|
if( curr_target != best_target )
|
|
{
|
|
target_pos = maps\see2_breadcrumbs::get_delayed_position( best_target, curr_delay );
|
|
}
|
|
else
|
|
{
|
|
target_pos = curr_target.origin;
|
|
}
|
|
if( target_pos == ( 0, 0, 0 ) )
|
|
{
|
|
target_pos = curr_target.origin;
|
|
}
|
|
if( distancesquared( curr_target.origin, self.origin ) > (1500*1500) )
|
|
{
|
|
target_pos = target_pos + (curr_target.origin - best_target.origin );
|
|
}
|
|
|
|
if( target_pos[0] > 1152 && target_pos[0] < 4928 && target_pos[1] > -12672 && target_pos[1] < -11200 ) //-- hope the map doesn't move...
|
|
{
|
|
self setTurretTargetVec(target_pos+(0,0,24));
|
|
}
|
|
else
|
|
{
|
|
self setTurretTargetVec( target_pos+(0, 0, 80) );
|
|
}
|
|
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: static_firing_behavior
|
|
// CALLED ON: an enemy vehicle
|
|
// PURPOSE: Functions the same as ::moving_firing_behavior, but all ranges for target acquisition
|
|
// and firing are longer.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
static_firing_behavior()
|
|
{
|
|
if( self.vehicletype == "opel" )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self notify( "kill old firing behavior" );
|
|
wait( 0.05 );
|
|
self endon( "kill old firing behavior" );
|
|
self endon( "death" );
|
|
|
|
if( !isDefined( get_players() ) )
|
|
{
|
|
while( 1 )
|
|
{
|
|
if( isDefined( get_players() ) )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
current_target = undefined;
|
|
best_target = undefined;
|
|
while( 1 )
|
|
{
|
|
if( isDefined( level.custom_target ) )
|
|
{
|
|
best_target = level.custom_target;
|
|
current_target = level.custom_target;
|
|
myOrigin = self.origin + (0,0,200);// gettagorigin( "tag_barrel" );
|
|
theirOrigin = current_target.origin + (0, 0, 200);
|
|
trace = bullettrace( myOrigin, theirOrigin, false, undefined );
|
|
if( trace["fraction"] < 0.95 || current_target == self)
|
|
{
|
|
current_target = undefined;
|
|
wait( 3 );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( !isDefined( current_target ) || current_target.health <= 0 || distancesquared( current_target.origin, self.origin ) > (level.see2_max_tank_target_dist*1.25*level.see2_max_tank_target_dist*1.25) || current_target.classname == "script_vehicle_corpse")
|
|
{
|
|
best_target = self maps\_vehicle::get_nearest_target( level.player_tanks );
|
|
current_target = self maps\see2::request_target( best_target );
|
|
myOrigin = self.origin + (0,0,200);// gettagorigin( "tag_barrel" );
|
|
theirOrigin = current_target.origin + (0, 0, 200);
|
|
trace = bullettrace( myOrigin, theirOrigin, false, undefined );
|
|
if( trace["fraction"] < 0.95 || current_target == self)
|
|
{
|
|
current_target = undefined;
|
|
wait( 3 );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( distancesquared( current_target.origin, self.origin ) > (level.see2_max_tank_firing_dist*1.25*level.see2_max_tank_firing_dist*1.25) )
|
|
{
|
|
wait( 3 );
|
|
continue;
|
|
}
|
|
|
|
if( self.health < 1 )
|
|
{
|
|
self notify( "death" );
|
|
return;
|
|
}
|
|
|
|
self thread set_tank_accuracy( current_target, best_target );
|
|
self thread wait_fire_weapon( current_target );
|
|
|
|
while( isDefined( current_target ) && isDefined( best_target ) && distancesquared( current_target.origin, self.origin ) < level.see2_max_tank_target_dist*level.see2_max_tank_target_dist && current_target.classname != "script_vehicle_corpse" )
|
|
{
|
|
if( isDefined( self.my_target_point ) )
|
|
{
|
|
self setturrettargetvec( self.my_target_point );
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
self notify( "need new target" );
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_fire_weapon
|
|
// CALLED ON: a vehicle
|
|
// PURPOSE: Fires the main cannon every so often if the vehicle has a target
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_fire_weapon( current_target )
|
|
{
|
|
self endon( "need new target" );
|
|
self endon( "death" );
|
|
|
|
if( self == current_target )
|
|
{
|
|
breaker = 1;
|
|
}
|
|
|
|
someone_is_close = false;
|
|
my_target = undefined;
|
|
|
|
while( 1 )
|
|
{
|
|
angles = vectortoangles( current_target.origin - self.origin );
|
|
turret_angles = self GetTagAngles( "tag_barrel" );
|
|
if( abs(angles[1] - turret_angles[1]) > 20 )
|
|
{
|
|
if( distanceSquared(self.origin, current_target.origin) < 300 * 300) //-- your target is really really close, then kill him and add a little modifier to it
|
|
{
|
|
someone_is_close = true;
|
|
my_target = undefined;
|
|
}
|
|
else
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
old_speed = self GetSpeedMPH();
|
|
self SetSpeed(0, 5, 5);
|
|
|
|
while(self GetSpeedMPH() > 0.1)
|
|
{
|
|
wait(0.05);
|
|
}
|
|
|
|
wait(0.2);
|
|
if(someone_is_close) //-- find the closest Player Tank
|
|
{
|
|
player_tanks = [];
|
|
players = get_players();
|
|
for(i = 0; i < players.size; i++)
|
|
{
|
|
player_tanks[i] = players[i].myTank;
|
|
}
|
|
|
|
my_target = player_tanks[0];
|
|
for( j=0 ; j<player_tanks.size; j++)
|
|
{
|
|
if( DistanceSquared(current_target.origin, my_target.origin) > DistanceSquared(current_target.origin, player_tanks[j].origin) )
|
|
{
|
|
my_target = player_tanks[j];
|
|
}
|
|
}
|
|
|
|
if(!IsDefined(current_target.too_close_damage_modifier) && ( DistanceSquared(my_target.origin, current_target.origin) < 300 * 300 ) )
|
|
{
|
|
my_target.too_close_damage_modifier = 3;
|
|
}
|
|
}
|
|
|
|
self fireweapon();
|
|
|
|
level notify( "enemy vehicle fired" );
|
|
|
|
if( !IsDefined(self.attached_to_backup_path_but_not_moving) )
|
|
{
|
|
self SetSpeed(old_speed, 5, 5);
|
|
}
|
|
|
|
wait(0.5);
|
|
if(IsDefined(my_target))
|
|
{
|
|
//-- reset the damage scale
|
|
my_target.too_close_damage_modifier = undefined;
|
|
}
|
|
|
|
wait( randomfloatrange( 4.5, 7.0) );
|
|
}
|
|
} |