1033 lines
No EOL
28 KiB
Text
1033 lines
No EOL
28 KiB
Text
#include maps\_anim;
|
|
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\pel2_util;
|
|
#include maps\_vehicle_utility;
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: main
|
|
// CALLED ON: level
|
|
// PURPOSE: sets up all relevant drone data structures and variables
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
main()
|
|
{
|
|
character\char_rus_r_rifle::precache();
|
|
character\char_ger_wrmcht_k98::precache();
|
|
character\char_rus_h_reznov_coat::precache();
|
|
character\char_rus_p_chernova::precache();
|
|
precacheModel( "vehicle_rus_tracked_t34" );
|
|
level.drone_spawnFunction["allies"] = character\char_rus_r_rifle::main;
|
|
level.drone_spawnFunction["axis"] = character\char_ger_wrmcht_k98::main;
|
|
level.droneCustomDeath = ::see2_drone_death;
|
|
maps\_drones::init();
|
|
|
|
level._effect["water_exploder"] = loadfx("weapon/tank/fx_tank_water");
|
|
level._effect["dirt_exploder"] = loadfx("weapon/tank/fx_tank_dirt");
|
|
level._effect["dummy_tank_fire"] = loadfx("weapon/muzzleflashes/fx_tank_t34_fire_flash");
|
|
level._effect["dummy_tank_explode"] = loadfx("explosions/large_vehicle_explosion");
|
|
|
|
level.num_drone_areas = 3;
|
|
|
|
custom_level_run_cycles();
|
|
see2_droneanim_init();
|
|
level thread init_drone_manager();
|
|
level thread start_drone_waves();
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: custom_level_run_cycles
|
|
// CALLED ON: level
|
|
// PURPOSE: sets up custom run cycles for drones
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
#using_animtree ("fakeshooters");
|
|
custom_level_run_cycles()
|
|
{
|
|
// for demonstrating custom run cycles
|
|
// these need to exist in fakeshooters, as well as have
|
|
// #using_animtree ("fakeshooters") be called above this function
|
|
level.drone_run_cycle["run_deep_a"] = %ai_run_deep_water_a;
|
|
level.drone_run_cycle["run_deep_b"] = %ai_run_deep_water_b;
|
|
|
|
level.drone_trigger_spawn_time = 3;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_kill_trigger
|
|
// CALLED ON: level
|
|
// PURPOSE: Waits until a trigger is hit and then stops a particular areas drones
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_kill_trigger( areaNum )
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", guy );
|
|
if( isPlayer( guy ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
level.valid[areaNum] = false;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_master_trigger
|
|
// CALLED ON: level
|
|
// PURPOSE: Waits until a trigger is hit then starts the drones for a specific area
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_master_trigger( areaNum )
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", guy );
|
|
if( isPlayer( guy ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
level.valid[areaNum] = true;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: start_drone_waves
|
|
// CALLED ON: level
|
|
// PURPOSE: Handles the spawning of drones and the playing of fx in all valid areas in the map
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
start_drone_waves()
|
|
{
|
|
level.trigger_arrays = [];
|
|
level.valid = [];
|
|
|
|
for( i = 1; i <= level.num_drone_areas; i++ )
|
|
{
|
|
level thread do_random_explosions( i );
|
|
level thread do_random_smoke( i );
|
|
master_trigger = getEnt("area "+i+" master drone trigger", "script_noteworthy");
|
|
master_trigger thread wait_for_master_trigger( i );
|
|
level.trigger_arrays[i] = getEntArray( "area "+i+" drone trigger", "script_noteworthy" );
|
|
level.valid[i] = false;
|
|
kill_trigger = getEnt( "area "+i+" drone kill trigger", "script_noteworthy" );
|
|
kill_trigger thread wait_for_kill_trigger( i );
|
|
}
|
|
|
|
level waittill( "controls_active" );
|
|
|
|
//NETWORK: possibility of just removing drones from co-op
|
|
if( get_players().size > 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
min_time_between_waves = 1;
|
|
max_time_between_waves = 4;
|
|
|
|
for( i = 1; i < level.num_drone_areas; i++ )
|
|
{
|
|
for( j = 0; j < level.trigger_arrays[i].size; j++ )
|
|
{
|
|
level.trigger_arrays[i][j].cooldown_timer = 0;
|
|
level.trigger_arrays[i][j] thread do_cooldown();
|
|
}
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
players = get_players();
|
|
for( k = 1; k <= level.num_drone_areas; k++ )
|
|
{
|
|
if( !level.valid[k] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
loop_size = level.trigger_arrays[k].size;
|
|
|
|
//NETWORK: FULL REMOVAL OF DRONES DURING C0-OP
|
|
//if(get_players().size > 1 && level.trigger_arrays[k].size > 2)
|
|
//{
|
|
loop_size = level.trigger_arrays[k].size - 2;
|
|
//}
|
|
|
|
//for( m = 0; m < level.trigger_arrays[k].size; m++ )
|
|
for( m = 0; m < loop_size; m++ )
|
|
{
|
|
level.trigger_arrays[k][m] notify( "trigger" );
|
|
wait_network_frame();
|
|
}
|
|
}
|
|
wait( randomfloatrange( min_time_between_waves, max_time_between_waves ) );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_cooldown
|
|
// CALLED ON: a drone trigger
|
|
// PURPOSE: Counts down a timer used to invalidate a trigger for x seconds after spawn
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_cooldown()
|
|
{
|
|
self endon( "kill drones" );
|
|
|
|
while( 1 )
|
|
{
|
|
if( isDefined( self.cooldown_timer ) && self.cooldown_timer > 0 )
|
|
{
|
|
self.cooldown_timer -= 0.05;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: find_best_drone_triggers
|
|
// CALLED ON: an entity
|
|
// PURPOSE: Finds the closest num valid triggers to an entity (in case multiple areas of drones are
|
|
// active or triggers are on cooldown)
|
|
// ADDITIONS NEEDED:
|
|
/////////////////////////////////
|
|
find_best_drone_triggers( num )
|
|
{
|
|
best_dist = 10000000000;
|
|
best_trigger = undefined;
|
|
worst_dist = 0;
|
|
worst_trigger = undefined;
|
|
return_array = [];
|
|
for( i = 0; i < level.drone_triggers.size; i++ )
|
|
{
|
|
if( !isDefined( level.drone_triggers[i].cooldown_timer ) || level.drone_triggers[i].cooldown_timer <= 0 )
|
|
{
|
|
dist = distanceSquared( level.drone_triggers[i].origin, level.centroid );
|
|
if( dist < best_dist )
|
|
{
|
|
best_dist = dist;
|
|
best_trigger = level.drone_triggers[i];
|
|
}
|
|
|
|
if( return_array.size < num || dist < worst_dist )
|
|
{
|
|
if( return_array.size == num )
|
|
{
|
|
return_array = array_remove( return_array, worst_trigger );
|
|
return_array = array_add( return_array, level.drone_triggers[i] );
|
|
worst_trigger = get_worst_drone_trigger( return_array );
|
|
worst_dist = distanceSquared( worst_trigger.origin, level.centroid );
|
|
}
|
|
else
|
|
{
|
|
return_array = array_add( return_array, level.drone_triggers[i] );
|
|
}
|
|
if( dist > worst_dist )
|
|
{
|
|
worst_dist = dist;
|
|
worst_trigger = level.drone_triggers[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return return_array;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: get_worst_drone_trigger
|
|
// CALLED ON: level
|
|
// PURPOSE: Gets the worst trigger for comparison
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
get_worst_drone_trigger( array )
|
|
{
|
|
worst_dist = 0;
|
|
worst_trigger = undefined;
|
|
for( i = 0; i < array.size; i++ )
|
|
{
|
|
dist = distanceSquared( array[i].origin, level.centroid ) > worst_dist;
|
|
if( dist )
|
|
{
|
|
worst_dist = dist;
|
|
worst_trigger = array[i];
|
|
}
|
|
}
|
|
return worst_trigger;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: Remove drone triggers
|
|
// CALLED ON: level
|
|
// PURPOSE: Deletes drone triggers with a specific ID
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
remove_drone_triggers( remove_id )
|
|
{
|
|
remove_list = [];
|
|
for( i = 0; i < level.drone_triggers.size; i++ )
|
|
{
|
|
if( level.drone_triggers[i].script_string == remove_id )
|
|
{
|
|
remove_list = array_add( remove_list, level.drone_triggers[i] );
|
|
}
|
|
}
|
|
|
|
for( j = 0; j < remove_list.size; j++ )
|
|
{
|
|
level.drone_triggers = array_remove( level.drone_triggers, remove_list[j] );
|
|
remove_list[j] notify( "kill drones" );
|
|
remove_list[j] delete();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_random_explosions
|
|
// CALLED ON: level
|
|
// PURPOSE: Plays dirt explosions and does radius damage to simulate artillery fire on a
|
|
// area's drones
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_random_explosions( wave )
|
|
{
|
|
level endon( "stop wave "+wave );
|
|
|
|
exploder_array = getEntArray( "exploder wave"+wave, "script_noteworthy" );
|
|
|
|
while( 1 )
|
|
{
|
|
if( !isDefined( exploder_array ) || exploder_array.size < 1 )
|
|
{
|
|
break;
|
|
}
|
|
rand = randomintrange( 2, 5 );
|
|
wait( rand );
|
|
|
|
if( !level.valid[wave] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
rand = randomint( exploder_array.size-1 );
|
|
if( exploder_array[rand].targetname == "dirt_exploder" )
|
|
{
|
|
playfx( level._effect["dirt_exploder"], exploder_array[rand].origin );
|
|
}
|
|
else
|
|
{
|
|
playfx( level._effect["water_exploder"], exploder_array[rand].origin );
|
|
}
|
|
radiusDamage( exploder_array[rand].origin, 500, 500, 500 );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_random_smoke
|
|
// CALLED ON: level
|
|
// PURPOSE: Spawns non-vision blocking smoke to cover drone advance for an area
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_random_smoke( wave )
|
|
{
|
|
smoke_array = getEntArray( "smoke wave"+wave, "script_noteworthy" );
|
|
for( i = 0; i < smoke_array.size; i++ )
|
|
{
|
|
smoke_array[i] thread do_smoke_at_intervals( wave );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_smoke_at_intervals
|
|
// CALLED ON: a smoke node
|
|
// PURPOSE: Plays the smoke grenade fx on a random distribution on a smoke node
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_smoke_at_intervals( wave )
|
|
{
|
|
level endon( "stop wave "+wave );
|
|
|
|
wait( randomintrange( 1, 22 ) );
|
|
|
|
while( 1 )
|
|
{
|
|
if( level.valid[wave] )
|
|
{
|
|
playfx( level._effect["drone_smoke"], self.origin );
|
|
}
|
|
|
|
rand = randomintrange( 25, 30 );
|
|
wait( rand );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_dummy_vehicles
|
|
// CALLED ON: level
|
|
// PURPOSE: Spawns dummy vehicles and moves them along a path, animates them and does fake firing
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_dummy_vehicles( wave, model, maxTimeBeforeDestroy, minSpeed, maxSpeed, staggerAmt )
|
|
{
|
|
level.vehicle_starts = GetEntArray( "wave"+wave+" tank start", "script_noteworthy" );
|
|
level.valid_path = [];
|
|
|
|
for( i = 0; i < level.vehicle_starts.size; i++ )
|
|
{
|
|
level.valid_path = array_add( level.valid_path, true );
|
|
thread do_single_dummy_vehicle( i, model, maxTimeBeforeDestroy, minSpeed, maxSpeed, staggerAmt );
|
|
wait_network_frame();
|
|
}
|
|
|
|
level.static_vehicles = GetEntArray( "wave"+wave+" dummy tank", "script_noteworthy" );
|
|
|
|
for( i = 0; i < level.static_vehicles.size; i++ )
|
|
{
|
|
level.static_vehicles[i] thread do_single_static_dummy_vehicle( model, maxTimeBeforeDestroy );
|
|
wait_network_frame();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_single_static_dummy_vehicle
|
|
// CALLED ON: a dummy vehicle node
|
|
// PURPOSE: Creates a static dummy vehicle that will switch to a destroyed state after a
|
|
// random amount of time
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_single_static_dummy_vehicle( model, maxTimeBeforeDestroy )
|
|
{
|
|
self endon( "destroy dummy vehicle" );
|
|
myModel = spawn( "script_model", groundpos(self.origin) );
|
|
myModel setModel( model );
|
|
myModel.angles = self.angles;
|
|
self thread wait_for_destroy( maxTimeBeforeDestroy, myModel );
|
|
while( 1 )
|
|
{
|
|
wait( randomint( 6, 10 ) );
|
|
playfxontag( level._effect["dummy_tank_fire"], myModel, "tag_flash" );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: wait_for_destroy
|
|
// CALLED ON: a static dummy vehicle
|
|
// PURPOSE: Waits a random amount of time then tells the dummy vehicle to destroy itself.
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
wait_for_destroy( maxTimeBeforeDestroy, model )
|
|
{
|
|
if( maxTimeBeforeDestroy < 60 )
|
|
{
|
|
maxTimeBeforeDestroy = 60;
|
|
}
|
|
wait( randomfloat( 59, maxTimeBeforeDestroy ) );
|
|
playfx( level._effect["dummy_tank_explode"], model.origin );
|
|
model setModel( model.model+"_dmg" );
|
|
self notify( "destroy dummy vehicle" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_single_dummy_vehicle
|
|
// CALLED ON: a start node
|
|
// PURPOSE: Spawns a vehicle, move it along the path, animates it and does fake firing
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
#using_animtree( "see2_models" );
|
|
do_single_dummy_vehicle( pathnum, model, maxTimeBeforeDestroy, minSpeed, maxSpeed, staggerAmt )
|
|
{
|
|
// TODO: create endon for handling multiple paths working simultaneously
|
|
wait( randomfloat( staggerAmt ) );
|
|
speed = randomfloatrange( minSpeed, maxSpeed );
|
|
time = 0;
|
|
toNode = GetEnt( level.vehicle_starts[pathnum].target, "targetname" );
|
|
fromNode = level.vehicle_starts[pathnum];
|
|
toVec = toNode.origin - fromNode.origin;
|
|
myModel = spawn( "script_model", fromNode.origin+(0,0,15) );
|
|
myModel.angles = vectortoangles( toVec );
|
|
myModel setModel( model );
|
|
myModel.animname = "dummy";
|
|
myModel setAnimTree();
|
|
myModel notify( "stop anim" );
|
|
myModel play_vehicle_anim( "tank_scan_straight" );
|
|
myModel thread do_fake_firing();
|
|
self thread run_timer( time );
|
|
while( level.valid_path[pathnum] )
|
|
{
|
|
if( isDefined( fromNode.target ) )
|
|
{
|
|
toNode = GetEnt( fromNode.target, "targetname" );
|
|
if( isDefined( fromNode.script_string ) )
|
|
{
|
|
myModel notify( "stop anim" );
|
|
myModel play_vehicle_anim( fromNode.script_string );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
toNode = level.vehicle_starts[pathnum];
|
|
}
|
|
maxTime = distance( fromNode.origin, toNode.origin )/speed;
|
|
goalAngles = vectortoangles( toNode.origin - fromNode.origin );
|
|
if( toNode != level.vehicle_starts[pathnum] )
|
|
{
|
|
//origin notify( "stop lerping" );
|
|
//origin thread lerpToOrient( goalAngles );
|
|
myModel.goalAngles = myModel.angles;
|
|
myModel thread smoothOrient( 0.1 );
|
|
myModel thread lerpToPos( toNode.origin, speed, maxTime );
|
|
//origin thread hug_ground( model );
|
|
myModel waittill( "continue" );
|
|
}
|
|
else
|
|
{
|
|
myModel delete();
|
|
myModel notify( "stop lerping" );
|
|
wait( 1 );
|
|
myModel = spawn( "script_model", toNode.origin );
|
|
myModel setModel( model );
|
|
myModel.angles = VectorToAngles( GetEnt(toNode.target, "targetname" ).origin - toNode.origin );
|
|
myModel.animname = "dummy";
|
|
myModel setAnimTree();
|
|
myModel notify( "stop anim" );
|
|
myModel play_vehicle_anim( "tank_scan_straight" );
|
|
myModel thread do_fake_firing();
|
|
wait( 0.05 );
|
|
}
|
|
if( time > maxTimeBeforeDestroy )
|
|
{
|
|
destroyDummyVehicle( pathnum, model, myModel );
|
|
}
|
|
else
|
|
{
|
|
percentToDestroy = time/maxTimeBeforeDestroy;
|
|
rand = randomfloat( 1 );
|
|
if( percentToDestroy > rand )
|
|
{
|
|
destroyDummyVehicle( pathnum, model, myModel );
|
|
}
|
|
else
|
|
{
|
|
fromNode = toNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: play_vehicle_anim
|
|
// CALLED ON: a dummy vehicle model
|
|
// PURPOSE: Animates the turret, the animation can be overridden by script_string values on
|
|
// path nodes
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
play_vehicle_anim( anime )
|
|
{
|
|
self endon( "stop anim" );
|
|
|
|
self SetFlaggedAnimKnobRestart( "blend_anim" + anime, level.scr_anim[self.animname][anime], 1, 0.2, 1 );
|
|
self waittillmatch( "blend_anim" + anime, "end" );
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: do_fake_firing
|
|
// CALLED ON: a dummy vehicle model
|
|
// PURPOSE: Plays the firing fx on a random basis
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
do_fake_firing()
|
|
{
|
|
while( isDefined( self ) )
|
|
{
|
|
playfxontag( level._effect["dummy_tank_fire"], self, "tag_flash" );
|
|
wait( randomint( 6, 10 ) );
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: destroyDummyVehcile
|
|
// CALLED ON: a dummy vehicle
|
|
// PURPOSE: Nothing, apparently
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
destroyDummyVehicle( pathnum, model, origin )
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: smoothOrient
|
|
// CALLED ON: a dummy vehicle model
|
|
// PURPOSE: Tries to smooth the vehicle's orientation to hug the ground and make any shift in
|
|
// orientation more gradual to avoid jerkiness
|
|
// ADDITIONS NEEDED: Maybe allow for different amounts of deviation for pitch, yaw and roll
|
|
/////////////////////////////////
|
|
smoothOrient( max_dev_per_frame )
|
|
{
|
|
self endon( "stop lerping" );
|
|
|
|
while( 1 )
|
|
{
|
|
if( self.angles == self.goalAngles )
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
normGoalAngles = angle_normalize180( self.goalAngles );
|
|
normAngles = angle_normalize180( self.angles );
|
|
diff1 = self.goalAngles - self.angles;
|
|
normGoalAngles = ( normGoalAngles[0], normGoalAngles[1], normGoalAngles[2] );
|
|
normAngles = ( normAngles[0], normAngles[1], normAngles[2] );
|
|
diff2 = normGoalAngles - normAngles;
|
|
finalDiff = [];
|
|
// first check if it is the -180/180 border
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
if( abs(diff1[i]) < abs(diff2[i]) )
|
|
{
|
|
finalDiff = array_add( finalDiff, diff1[i] );
|
|
}
|
|
else
|
|
{
|
|
finalDiff = array_add( finalDiff, diff2[i] );
|
|
}
|
|
}
|
|
if( abs( finalDiff[0]) > max_dev_per_frame )
|
|
{
|
|
if( finalDiff[0] > 0 )
|
|
{
|
|
self.angles = (self.angles[0] + max_dev_per_frame, self.angles[1], self.angles[2]);
|
|
}
|
|
else
|
|
{
|
|
self.angles = (self.angles[0] - max_dev_per_frame, self.angles[1], self.angles[2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self.angles = (self.goalAngles[0], self.angles[1], self.angles[2]);
|
|
}
|
|
|
|
if( abs( finalDiff[1]) > max_dev_per_frame )
|
|
{
|
|
if( finalDiff[1] > 0 )
|
|
{
|
|
self.angles = (self.angles[0], self.angles[1]+ max_dev_per_frame, self.angles[2]);
|
|
}
|
|
else
|
|
{
|
|
self.angles = (self.angles[0], self.angles[1]- max_dev_per_frame, self.angles[2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self.angles = (self.angles[0], self.goalAngles[1], self.angles[2]);
|
|
}
|
|
|
|
|
|
if( abs(finalDiff[2]) > max_dev_per_frame )
|
|
{
|
|
if( finalDiff[2] > 0 )
|
|
{
|
|
self.angles = (self.angles[0], self.angles[1], self.angles[2]+max_dev_per_frame);
|
|
}
|
|
else
|
|
{
|
|
self.angles = (self.angles[0], self.angles[1], self.angles[2]-max_dev_per_frame);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self.angles = (self.angles[0], self.angles[1], self.goalAngles[2]);
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: lerpToPos
|
|
// CALLED ON: a dummy vehicle model
|
|
// PURPOSE: Moves an entity to a point over time
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
lerpToPos( goalPos, speed, maxTime )
|
|
{
|
|
self endon( "stop lerping" );
|
|
startPos = self.origin;
|
|
toVec = goalPos - self.origin;
|
|
toVec = vectorNormalize( toVec );
|
|
time = 0;
|
|
|
|
while( 1 )
|
|
{
|
|
scaled_move = ( toVec[0] * speed * 0.05, toVec[1] * speed * 0.05, toVec[2] * speed * 0.05 );
|
|
self.origin += scaled_move;
|
|
self.origin = groundpos( self.origin + (0, 0, 100) );
|
|
proj_loc = self.origin + scaled_move + (0, 0, 100);
|
|
self.goalAngles = vectorToAngles( groundpos(proj_loc) - self.origin );
|
|
wait( 0.05 );
|
|
time += 0.05;
|
|
if( vec_approx_equals( self.origin, startPos, goalPos, 0.05 ) || time > maxTime )
|
|
{
|
|
self notify( "continue" );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: vec_approx_equals
|
|
// CALLED ON: level
|
|
// PURPOSE: Checks to see if the difference between two vectors is within a percent difference
|
|
// of the difference between the second and another vector.
|
|
// ADDITIONS NEEDED: I am not sure exactly why I wrote this, but it seems to work
|
|
/////////////////////////////////
|
|
vec_approx_equals( vec, vec1, vec2, diff )
|
|
{
|
|
// Need to fix this function
|
|
if( abs(vec[0] - vec2[0]) <= abs(vec1[0]-vec2[0])*diff )
|
|
{
|
|
if( abs(vec[1] - vec2[1]) <= abs(vec1[1]-vec2[1])*diff )
|
|
{
|
|
if( abs(vec[2] - vec2[2]) <= abs(vec1[2]-vec2[2])*diff )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: run_timer
|
|
// CALLED ON: level
|
|
// PURPOSE: increments a time value
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
run_timer( time )
|
|
{
|
|
wait( 0.05 );
|
|
time += 0.05;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: angle_normalize180
|
|
// CALLED ON: level
|
|
// PURPOSE: Normalizes a vec3 of angles to the [-180, 180] range
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
angle_normalize180( angles )
|
|
{
|
|
retAngles = [];
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
scaledAngle = angles[i] * (1.0 / 360.0);
|
|
|
|
if( (scaledAngle + 0.5) > 1 )
|
|
{
|
|
floor = 1;
|
|
}
|
|
else
|
|
{
|
|
floor = 0;
|
|
}
|
|
retAngles[i] = (scaledAngle - floor) * 360.0;
|
|
}
|
|
|
|
return retAngles;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: see2_droneanim_init
|
|
// CALLED ON: level
|
|
// PURPOSE: Initializes drone animations to use in this level
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
#using_animtree ("generic_human");
|
|
see2_droneanim_init()
|
|
{
|
|
//println("scriptprint - extra drone anims created");
|
|
|
|
level.drone_anims[ "stand" ][ "idle" ] = %drone_stand_idle;
|
|
level.drone_anims[ "stand" ][ "run" ] = %drone_stand_run;
|
|
level.drone_anims[ "stand" ][ "reload" ] = %exposed_crouch_reload;
|
|
|
|
level.drone_anims[ "stand" ][ "death" ] = [];
|
|
level.drone_anims[ "stand" ][ "death" ][0] = %drone_stand_death;
|
|
level.drone_anims[ "stand" ][ "death" ][1] = %death_explosion_up10;
|
|
level.drone_anims[ "stand" ][ "death" ][2] = %death_explosion_back13;
|
|
level.drone_anims[ "stand" ][ "death" ][3] = %death_explosion_forward13;
|
|
level.drone_anims[ "stand" ][ "death" ][4] = %death_explosion_left11;
|
|
level.drone_anims[ "stand" ][ "death" ][5] = %death_explosion_right13;
|
|
|
|
//-- explosions particular to pby ships
|
|
level.drone_anims[ "stand" ][ "death" ][6] = %ch_pby_explosion_back;
|
|
level.drone_anims[ "stand" ][ "death" ][7] = %ch_pby_explosion_front;
|
|
level.drone_anims[ "stand" ][ "death" ][8] = %ch_pby_explosion_right;
|
|
level.drone_anims[ "stand" ][ "death" ][9] = %ch_pby_explosion_left;
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: see2_drone_death
|
|
// CALLED ON: a drone
|
|
// PURPOSE: Checks for different damage types and plays appropriate death anims.
|
|
// ADDITIONS NEEDED: More support for burning deaths
|
|
/////////////////////////////////
|
|
see2_drone_death()
|
|
{
|
|
self endon("no_drone_death_thread");
|
|
//println("scriptprint - custom death thread running");
|
|
|
|
if(!IsDefined(level.drone_death_queue))
|
|
{
|
|
ASSERT(false, "The drone death manager has not been inited");
|
|
}
|
|
|
|
drone = self;
|
|
damage_type = self;
|
|
damage_ori = self;
|
|
death_index = 0;
|
|
|
|
// Wait until the drone reaches 0 health
|
|
while( isdefined( drone ) )
|
|
{
|
|
drone waittill( "damage", amount, attacker, damage_dir, damage_ori, damage_type);
|
|
if ( drone.health <= 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( damage_type == "MOD_BURNED" )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
println(damage_type);
|
|
|
|
if(damage_type == "MOD_PROJECTILE" || damage_type == "MOD_PROJECTILE_SPLASH")
|
|
{
|
|
drone.special_death_fx = "drone_burst";
|
|
}
|
|
|
|
drone unlink();
|
|
|
|
if(damage_type == "MOD_EXPLOSIVE" || damage_type == "MOD_PROJECTILE_SPLASH" )
|
|
{
|
|
/*
|
|
ref_point = [];
|
|
ref_point[0] = drone.origin + ( AnglesToForward(drone.angles) * 5 ); //front
|
|
ref_point[1] = drone.origin + ( AnglesToForward(drone.angles) * -5 ); //rear
|
|
ref_point[2] = drone.origin + ( AnglesToRight(drone.angles) * -5 ); //left
|
|
ref_point[3] = drone.origin + ( AnglesToRight(drone.angles) * 5 ); //right
|
|
|
|
closest_point = ref_point[0];
|
|
index = 0;
|
|
|
|
for(i = 1 ; i < ref_point.size; i++)
|
|
{
|
|
if(Distance(ref_point[i], damage_ori) < Distance(closest_point, damage_ori))
|
|
{
|
|
closest_point = ref_point[i];
|
|
index = i;
|
|
}
|
|
}
|
|
|
|
//Figure out if the drone is falling off of a ship or not
|
|
new_point = 0;
|
|
trace = 0;
|
|
|
|
switch(index) //index 0/forward, 1/back, 2/left, 3,right
|
|
{
|
|
case 0:
|
|
new_point = drone.origin + (AnglesToForward(drone.angles) * 264);
|
|
drone.angles = VectorToAngles(damage_ori - drone.origin);
|
|
break;
|
|
case 1:
|
|
new_point = drone.origin + (AnglesToForward(drone.angles) * -264);
|
|
drone.angles = VectorToAngles(drone.origin - damage_ori);
|
|
break;
|
|
case 2:
|
|
new_point = drone.origin + (AnglesToRight(drone.angles) * -264);
|
|
drone.angles = VectorToAngles(damage_ori - drone.origin) - (0, 90, 0);
|
|
break;
|
|
case 3:
|
|
new_point = drone.origin + (AnglesToRight(drone.angles) * 264);
|
|
drone.angles = VectorToAngles(damage_ori - drone.origin) + (0, 90, 0);
|
|
break;
|
|
}
|
|
trace = BulletTrace(new_point, new_point - (0,0,2000), true, undefined);
|
|
|
|
//-- stopped here to go to lunch, you want to check the traces vertical position and then decide which animation to play
|
|
if(trace["position"][2] < (new_point[2] - 32))
|
|
{
|
|
switch(index)
|
|
{
|
|
case 0:
|
|
index = 4;
|
|
break;
|
|
case 1:
|
|
index = 5;
|
|
break;
|
|
case 2:
|
|
index = 6;
|
|
break;
|
|
case 3:
|
|
index = 7;
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
index = randomint( 4 );
|
|
//-- decide which death anim to play
|
|
switch(index)
|
|
{
|
|
case 0:
|
|
death_index = 2; // [3] = %death_explosion_forward13;
|
|
break;
|
|
case 1:
|
|
death_index = 3; // [2] = %death_explosion_back13;
|
|
break;
|
|
case 2:
|
|
death_index = 5; // [4] = %death_explosion_left11;
|
|
break;
|
|
case 3:
|
|
death_index = 4; // [5] = %death_explosion_right13;
|
|
break;
|
|
case 4:
|
|
death_index = 6; //front
|
|
break;
|
|
case 5:
|
|
death_index = 7; //back
|
|
break;
|
|
case 6:
|
|
death_index = 8; //left
|
|
break;
|
|
case 7:
|
|
death_index = 9; //right
|
|
break;
|
|
}
|
|
|
|
if(IsDefined(drone.combust))
|
|
{
|
|
drone thread torch_ai(0.1);
|
|
}
|
|
}
|
|
else if( damage_type == "MOD_BURNED" )
|
|
{
|
|
drone thread torch_ai(0.1);
|
|
death_index = 0;
|
|
}
|
|
else
|
|
{
|
|
death_index = 0;
|
|
}
|
|
|
|
// Make drone die
|
|
drone notify( "death" );
|
|
drone stopAnimScripted();
|
|
|
|
if(IsDefined(drone.special_death_fx))
|
|
{
|
|
drone.special_death_fx = "drone_burst";
|
|
PlayFXOnTag(level._effect[drone.special_death_fx], drone, "J_SpineLower");
|
|
}
|
|
|
|
drone.need_notetrack = true;
|
|
drone maps\_drone::drone_play_anim( level.drone_anims[ "stand" ][ "death"][death_index] );
|
|
|
|
|
|
drone add_me_to_the_death_queue();
|
|
/*
|
|
wait 10;
|
|
|
|
if ( isdefined( drone ) )
|
|
drone delete();
|
|
*/
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: add_me_to_death_queue
|
|
// CALLED ON: a drone
|
|
// PURPOSE: Tells the drone manager that the drone needs to be killed
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
add_me_to_the_death_queue()
|
|
{
|
|
level.drone_death_queue[level.drone_death_queue.size] = self;
|
|
level notify("drone_manager_process");
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: init_drone_manager
|
|
// CALLED ON: level
|
|
// PURPOSE: inits the drone manager that handles drone deaths, etc
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
init_drone_manager()
|
|
{
|
|
MAX_DEAD_DRONES = 10;
|
|
level.drone_death_queue = [];
|
|
|
|
while(1)
|
|
{
|
|
level waittill("drone_manager_process");
|
|
|
|
if(level.drone_death_queue.size > MAX_DEAD_DRONES)
|
|
{
|
|
while( level.drone_death_queue.size > MAX_DEAD_DRONES )
|
|
{
|
|
removed_guy = level.drone_death_queue[0];
|
|
new_drone_queue = [];
|
|
|
|
for( i = 1 ; i < (level.drone_death_queue.size); i++)
|
|
{
|
|
new_drone_queue[i-1] = level.drone_death_queue[i];
|
|
}
|
|
|
|
if(IsDefined(removed_guy))
|
|
{
|
|
removed_guy Delete();
|
|
}
|
|
level.drone_death_queue = new_drone_queue;
|
|
}
|
|
}
|
|
//else
|
|
//{
|
|
//-- we are ok on the number of dead drones
|
|
//}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// FUNCTION: torch_ai
|
|
// CALLED ON: a drone
|
|
// PURPOSE: lights an AI on Fire
|
|
// ADDITIONS NEEDED: None
|
|
/////////////////////////////////
|
|
torch_ai( delay )
|
|
{
|
|
tagArray = [];
|
|
tagArray[tagArray.size] = "J_Wrist_RI";
|
|
tagArray[tagArray.size] = "J_Wrist_LE";
|
|
tagArray[tagArray.size] = "J_Elbow_LE";
|
|
tagArray[tagArray.size] = "J_Elbow_RI";
|
|
tagArray[tagArray.size] = "J_Knee_RI";
|
|
tagArray[tagArray.size] = "J_Knee_LE";
|
|
tagArray[tagArray.size] = "J_Ankle_RI";
|
|
tagArray[tagArray.size] = "J_Ankle_LE";
|
|
|
|
tagArray = maps\_utility::array_randomize( tagArray );
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
PlayFxOnTag( level._effect["character_fire_death_sm"], self, tagArray[i] );
|
|
|
|
if( IsDefined( delay ) )
|
|
{
|
|
wait( delay );
|
|
}
|
|
}
|
|
|
|
PlayFxOnTag( level._effect["character_fire_death_torso"], self, "J_SpineLower" );
|
|
} |