3150 lines
79 KiB
Plaintext
3150 lines
79 KiB
Plaintext
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\_anim;
|
|
#using_animtree( "generic_human" );
|
|
|
|
/**** ride stuff ****/
|
|
ride_uaz_mount()
|
|
{
|
|
flag_wait( "wait_for_player" );
|
|
|
|
trigger = spawn( "trigger_radius", self gettagorigin( "tag_passenger" ), 0, 72, 64);
|
|
trigger enablelinkto();
|
|
trigger linkto( self, "tag_passenger", ( 0,-48,-48 ), (0,0,0) );
|
|
trigger waittill( "trigger" );
|
|
|
|
flag_set( "uaz_mounted" );
|
|
trigger delete();
|
|
}
|
|
|
|
uaz_control()
|
|
{
|
|
level.player endon( "death" );
|
|
|
|
level endon( "c130_takeoff_missed" );
|
|
level endon( "uaz_park_crash" );
|
|
|
|
flag_wait( "uaz_mounted" );
|
|
|
|
// make invulnerable while doing mount animation.
|
|
level.player EnableInvulnerability();
|
|
|
|
level.player allowProne( false );
|
|
level.player allowCrouch( false );
|
|
level.player allowStand( true );
|
|
|
|
enablePlayerWeapons( false );
|
|
level.player.rig = spawn_anim_model( "player_rig" );
|
|
level.player.rig hide();
|
|
|
|
level.player.rig linkto( self, "tag_body" );
|
|
self thread anim_single_solo( level.player.rig, "boneyard_uaz_mount" , "tag_body" );
|
|
self thread ride_uaz_door();
|
|
|
|
level.player PlayerLinkToBlend( level.player.rig, "tag_player", 0.5 );
|
|
wait 0.5;
|
|
level.player.rig show();
|
|
level.player PlayerLinkToDelta( level.player.rig, "tag_player", 0.75, 180, 180, 75, 25, true );
|
|
|
|
self waittill( "boneyard_uaz_mount" );
|
|
|
|
// once mounted make vulnerable again.
|
|
level.player DisableInvulnerability();
|
|
|
|
self thread player_rig_adjust_height();
|
|
|
|
level.player.rig hide();
|
|
enablePlayerWeapons( true );
|
|
level.player LerpViewAngleClamp( 0.5, 0.5, 0, 180, 180, 75, 35 );
|
|
|
|
flag_wait( "uaz_player_control" );
|
|
flag_set( "uaz_player_in_control" );
|
|
|
|
level.player LerpViewAngleClamp( 0.5, 0.5, 0, 15, 15, 75, 5 );
|
|
wait 0.5;
|
|
enablePlayerWeapons( false );
|
|
level.player.rig show();
|
|
self anim_single_solo( level.player.rig, "grab_wheel" , "tag_body" );
|
|
thread dof_foreground();
|
|
|
|
self thread uaz_steer();
|
|
|
|
flag_wait( "uaz_park" );
|
|
wait 0.25;
|
|
|
|
self playsound( "scn_bone_uaz_hits_ramp" );
|
|
Earthquake( .5, 2, level.player.origin, 1024 );
|
|
|
|
self thread park_uaz();
|
|
}
|
|
|
|
dof_foreground()
|
|
{
|
|
time = 2.0;
|
|
|
|
start = level.dofDefault;
|
|
end[ "nearStart" ] = 8;
|
|
end[ "nearEnd" ] = 27;
|
|
end[ "nearBlur" ] = 4;
|
|
|
|
end[ "farStart" ] = 5000;
|
|
end[ "farEnd" ] = 10000;
|
|
end[ "farBlur" ] = 2;
|
|
|
|
blend_dof( start, end, time );
|
|
}
|
|
|
|
player_rig_adjust_height()
|
|
{
|
|
level endon( "uaz_player_control" );
|
|
|
|
stand = false;
|
|
|
|
while( true )
|
|
{
|
|
player_angle = level.player.angles[1];
|
|
uaz_angle = self.angles[1];
|
|
dif_angle = abs( int( uaz_angle - player_angle ) % 360 );
|
|
|
|
if ( dif_angle > 130 && dif_angle < 300 )
|
|
{
|
|
if ( !stand )
|
|
{
|
|
self anim_single_solo( level.player.rig, "boneyard_uaz_stand" , "tag_body" );
|
|
stand = true;
|
|
}
|
|
}
|
|
else if ( stand )
|
|
{
|
|
self anim_single_solo( level.player.rig, "boneyard_uaz_sit" , "tag_body" );
|
|
stand = false;
|
|
}
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
uaz_steer()
|
|
{
|
|
level endon( "uaz_stop_player_control" );
|
|
level endon( "uaz_park" );
|
|
level endon( "uaz_park_crash" );
|
|
self notify( "newpath" );
|
|
|
|
level.player setstance( "stand" );
|
|
level.player allowProne( false );
|
|
level.player allowCrouch( false );
|
|
level.player allowStand( true );
|
|
enablePlayerWeapons( false );
|
|
|
|
self thread uaz_steer_viewmodel();
|
|
|
|
lookahead = 200;
|
|
|
|
//calculate initial goal_offset
|
|
angle1 = vectortoangles( level.c130.origin - self.origin )[1];
|
|
angle2 = self.angles[1];
|
|
angle = angle1 - angle2;
|
|
goal_offset = tan( angle ) * lookahead;
|
|
|
|
|
|
self thread modulate_uaz_crashability();
|
|
|
|
angles_ent = spawn( "script_origin", (0,0,0) );
|
|
goal_offset = 0;
|
|
input_multiplier = 5; //5
|
|
|
|
goal_offset_range = 3;
|
|
|
|
// used to make the player slide off the road
|
|
off_road_offset = 544;
|
|
|
|
start_time = gettime();
|
|
blend_in_control_time = 1.5;
|
|
blend_in_control_time *= 1000; // convert to milliseconds
|
|
|
|
blend_out_control_time = 2.0;
|
|
blend_out_control_time *= 1000; // convert to milliseconds
|
|
|
|
left_dot_limit = 0.78;
|
|
right_dot_limit = 0.78;
|
|
|
|
speed = 50; //35
|
|
goal_speed = 95;
|
|
time = 8.0; //9.5
|
|
full_accel = ( goal_speed - speed ) / ( time * 20 );
|
|
slow_accel = full_accel * 0.5;
|
|
full_accel *= 4;
|
|
accel = slow_accel;
|
|
|
|
|
|
while( true )
|
|
{
|
|
// -1 to 1 for left/right
|
|
input = level.player GetNormalizedMovement()[1];
|
|
input *= -1; // swap it for the direction we're going in
|
|
goal_offset += input * input_multiplier;
|
|
goal_offset = clamp( goal_offset, goal_offset_range * -1, goal_offset_range );
|
|
|
|
|
|
|
|
// ============================================================================================
|
|
// This section overrides the player's steering if he steers too hard left or right
|
|
// ============================================================================================
|
|
angles_runway = vectortoangles( ( 0, 100, 0 ) );
|
|
forward_runway = anglestoforward( angles_runway );
|
|
|
|
my_forward = anglestoforward( self.angles );
|
|
dot = vectordot( my_forward, forward_runway );
|
|
|
|
my_right = anglestoright( self.angles );
|
|
|
|
// turning too hard?
|
|
if ( vectordot( my_right, forward_runway ) > 0 )
|
|
{
|
|
if ( dot < left_dot_limit )
|
|
{
|
|
// if you turn left too far, start turning right
|
|
goal_offset = goal_offset_range * -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( dot < right_dot_limit )
|
|
{
|
|
// if you turn right too far, start turning left
|
|
goal_offset = goal_offset_range;
|
|
}
|
|
}
|
|
// ============================================================================================
|
|
|
|
|
|
|
|
// use a reference ent so we can easily add angles to it
|
|
angles_ent.angles = ( 0, self.angles[1], 0 );
|
|
angles_ent addyaw( goal_offset );
|
|
forward = anglestoforward( angles_ent.angles );
|
|
goal = self.origin + forward * lookahead;
|
|
|
|
|
|
if ( self.origin[ 0 ] < level.c130.origin[ 0 ] - 360 )
|
|
{
|
|
goal = level.c130.ramp_trigger.origin;
|
|
}
|
|
|
|
|
|
// ============================================================================================
|
|
// This section blends the goalpos of the UAZ at the start and end of the ride
|
|
// ============================================================================================
|
|
|
|
// find the spot the uaz should drive to if it were driving straight towards
|
|
// the c130.
|
|
if ( flag( "player_loses_control_of_uaz" ) )
|
|
{
|
|
// once the player loses control, blend out control of the uaz
|
|
difference = gettime() - level.control_loss_time;
|
|
difference /= blend_out_control_time;
|
|
difference = clamp( difference, 0, 1 );
|
|
|
|
//Line( goal, level.c130.ramp_trigger.origin, (1,0,0) );
|
|
|
|
// merge in our desired direction and merge out the ideal goal.
|
|
goal = level.c130.ramp_trigger.origin * difference + goal * ( 1 - difference );
|
|
}
|
|
else
|
|
{
|
|
angles_to_c130 = vectortoangles( ( level.c130.origin + ( off_road_offset, 0, 0 ) ) - self.origin );
|
|
forward_to_c130 = anglestoforward( angles_to_c130 );
|
|
straight_to_c130_goal = self.origin + forward_to_c130 * lookahead;
|
|
|
|
// blend in control over time, so we dont oversteer early on,
|
|
// we blend a value from 0 to 1 over blend_in_control_time seconds.
|
|
difference = gettime() - start_time;
|
|
difference /= blend_in_control_time;
|
|
difference = clamp( difference, 0, 1 );
|
|
|
|
// merge in our desired direction and merge out the ideal goal.
|
|
goal = goal * difference + straight_to_c130_goal * ( 1 - difference );
|
|
}
|
|
// ============================================================================================
|
|
|
|
// Line( goal, level.c130.ramp_trigger.origin, (1,0,0) );
|
|
|
|
|
|
if ( speed >= 60 )
|
|
accel = full_accel;
|
|
// calculate the speed
|
|
speed += accel;
|
|
speed = clamp( speed, 0, goal_speed );
|
|
|
|
// drive
|
|
self vehicleDriveTo( goal, speed );
|
|
//line( self.origin, goal );
|
|
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
modulate_uaz_crashability()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "dying" );
|
|
|
|
self VehPhys_EnableCrashing();
|
|
wait( 2 );
|
|
self VehPhys_DisableCrashing();
|
|
|
|
for ( ;; )
|
|
{
|
|
// after 2 seconds, you can only only crash if you're off the road
|
|
// this is cause if you get side swiped you crash in a really
|
|
// weird way that is hard to comprehend
|
|
if ( abs( level.uaz.origin[0] - level.c130.origin[0] ) > 450 )
|
|
{
|
|
self VehPhys_EnableCrashing();
|
|
}
|
|
else
|
|
{
|
|
self VehPhys_disableCrashing();
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
uaz_steer_viewmodel()
|
|
{
|
|
level endon( "uaz_park" );
|
|
level endon( "uaz_park_crash" );
|
|
|
|
steering = 0;
|
|
|
|
while( true )
|
|
{
|
|
input = level.player GetNormalizedMovement()[1] * -1;
|
|
input += ( self Vehicle_GetSteering() * 0.25 );
|
|
input = clamp( input, -1, 1 );
|
|
|
|
steering += input * 0.1; // ( 1.1 - abs( input ) );
|
|
if ( abs( steering ) > abs( input ) )
|
|
steering = input;
|
|
|
|
fraction = abs( steering );
|
|
|
|
level.player.rig SetAnim( level.scr_anim[ "player_rig" ][ "steer_straight" ], 1-fraction, 0.1, 1 );
|
|
|
|
level.uaz SetAnim( level.scr_anim[ "generic" ][ "steer_straight" ], 1 - (fraction/2), 0.1, 1 );
|
|
level.uaz ClearAnim( level.scr_anim[ "generic" ][ "old_steering" ], 0 );
|
|
|
|
if ( steering < 0 )
|
|
{
|
|
level.player.rig SetAnim( level.scr_anim[ "player_rig" ][ "steer_right" ], fraction, 0.1, 1 );
|
|
level.player.rig SetAnim( level.scr_anim[ "player_rig" ][ "steer_left" ], 0, 0.1, 1 );
|
|
|
|
level.uaz SetAnim( level.scr_anim[ "generic" ][ "steer_right" ], fraction/2, 0.1, 1 );
|
|
level.uaz SetAnim( level.scr_anim[ "generic" ][ "steer_left" ], 0, 0.1, 1 );
|
|
}
|
|
else
|
|
{
|
|
level.player.rig SetAnim( level.scr_anim[ "player_rig" ][ "steer_right" ], 0, 0.1, 1 );
|
|
level.player.rig SetAnim( level.scr_anim[ "player_rig" ][ "steer_left" ], fraction, 0.1, 1 );
|
|
|
|
level.uaz SetAnim( level.scr_anim[ "generic" ][ "steer_right" ], 0, 0.1, 1 );
|
|
level.uaz SetAnim( level.scr_anim[ "generic" ][ "steer_left" ], fraction/2, 0.1, 1 );
|
|
}
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
ride_uaz_fake_price_fire()
|
|
{
|
|
self endon( "death" );
|
|
while( true )
|
|
{
|
|
axis = getaiarray( "axis" );
|
|
if ( axis.size == 0 )
|
|
break;
|
|
|
|
axis = array_randomize( axis );
|
|
burst = randomintrange( 5, 15 );
|
|
target_origin = axis[0] geteye();
|
|
for ( i = 0; i<burst; i++ )
|
|
{
|
|
if ( isalive( axis[0] ) )
|
|
target_origin = axis[0] geteye();
|
|
MagicBullet( self.weapon, level.price geteye() + (0,0,16), target_origin );
|
|
wait 0.1;
|
|
}
|
|
wait RandomFloatRange( 1.5, 3 );
|
|
}
|
|
}
|
|
|
|
#using_animtree( "vehicles" );
|
|
ride_uaz_door()
|
|
{
|
|
anim_model = self maps\_vehicle_aianim::getanimatemodel();
|
|
// anim_model setflaggedanimknob( "uaz_door_anim", level.scr_anim[ "generic" ][ "boneyard_UAZ_door" ], 1, .2, 1 );
|
|
anim_model setflaggedanim( "uaz_door_anim", level.scr_anim[ "generic" ][ "boneyard_UAZ_door" ], 1, .2, 1 );
|
|
anim_model waittillmatch( "uaz_door_anim", "end" );
|
|
anim_model ClearAnim( level.scr_anim[ "generic" ][ "boneyard_UAZ_door" ], 0 );
|
|
|
|
/*
|
|
flag_wait( "wait_for_player" );
|
|
anim_model = self maps\_vehicle_aianim::getanimatemodel();
|
|
anim_model maps\_vehicle_aianim::setanimrestart_once( %uaz_passenger_exit_into_stand_door, false );
|
|
|
|
flag_wait( "uaz_mounted" );
|
|
anim_model ClearAnim( %uaz_passenger_exit_into_stand_door, 0 );
|
|
anim_model maps\_vehicle_aianim::setanimrestart_once( %uaz_passenger_enter_from_huntedrun_door, true );
|
|
*/
|
|
}
|
|
|
|
park_uaz()
|
|
{
|
|
park_goal = getent( "uaz_park", "script_noteworthy" ); // use uaz_park for the final animation
|
|
park_goal setmodel( "tag_origin" );
|
|
|
|
tag_origin = park_goal spawn_tag_origin();
|
|
tag_origin linkto( park_goal, "tag_origin", (0,0,0), (0,0,0) );
|
|
|
|
forward = anglestoforward( park_goal.angles );
|
|
goal = self.origin + forward * 500;
|
|
self vehicleDriveTo( goal, 60 );
|
|
|
|
// re add rook so that he stays attached to the truck
|
|
self.riders = array_add( self.riders, level.rook );
|
|
|
|
dummy = self maps\_vehicle::vehicle_to_dummy();
|
|
dummy HidePart( "tag_glass_front_d" );
|
|
dummy HidePart( "tag_mirror_left" );
|
|
dummy HidePart( "tag_mirror_right" );
|
|
|
|
self.dummy = dummy;
|
|
|
|
level.player.rig linkto( dummy, "tag_body" );
|
|
dummy linkto( tag_origin, "tag_origin", (0,0,0), (0,0,0) );
|
|
dummy animscripted( "uaz_parked", tag_origin.origin, tag_origin.angles, level.scr_anim[ "generic" ][ "boneyard_jeep_evac" ] );
|
|
|
|
thread park_rumble();
|
|
|
|
level.player.rig SetAnimKnob( level.scr_anim[ "player_rig" ][ "boneyard_player_evac" ], 1, 0.2, 1 );
|
|
}
|
|
|
|
park_rumble()
|
|
{
|
|
// quick rumble hack so that I don't have to do a new one.
|
|
level.player PlayRumbleOnEntity( "artillery_rumble" );
|
|
wait .5;
|
|
level.player PlayRumbleOnEntity( "damage_heavy" );
|
|
wait .2;
|
|
level.player PlayRumbleOnEntity( "damage_light" );
|
|
wait .2;
|
|
level.player PlayRumbleOnEntity( "damage_light" );
|
|
wait .2;
|
|
level.player PlayRumbleOnEntity( "damage_light" );
|
|
}
|
|
|
|
#using_animtree( "generic_human" );
|
|
ride_bump()
|
|
{
|
|
self endon( "death" );
|
|
|
|
array_thread( getvehiclenodearray( "jolt_node", "script_noteworthy" ), ::ride_bump_node );
|
|
|
|
self.max_jolt = 0.1;
|
|
max_speed = 80;
|
|
|
|
offset = [];
|
|
offset[0] = ( 23, 33, 64 );
|
|
offset[1] = ( -23, -33, 64 );
|
|
|
|
while( true )
|
|
{
|
|
speed = min( self.veh_speed, max_speed );
|
|
jolt_scale = self.max_jolt / max_speed;
|
|
if ( speed > 0 && self.max_jolt > 0 )
|
|
{
|
|
jolt = speed * jolt_scale;
|
|
self joltbody( self.origin + offset[ randomint(2) ], jolt );
|
|
}
|
|
|
|
max_random = ( ( max_speed - speed ) / 100 ) + 0.1; // range 0.1 to 0.9
|
|
wait 0.2 + randomfloat( max_random );
|
|
}
|
|
}
|
|
|
|
ride_bump_node()
|
|
{
|
|
self waittill( "trigger", vehicle );
|
|
vehicle.max_jolt = self.script_physicsjolt / 1000; // script_physicsjolt is a int so have to / 1000
|
|
}
|
|
|
|
ride_rumble_setup()
|
|
{
|
|
array = getentarray( "rumble_trigger", "targetname" );
|
|
array_thread( array, ::ride_rumble );
|
|
}
|
|
|
|
ride_rumble()
|
|
{
|
|
self waittill( "trigger" );
|
|
|
|
rumble = "grenade_rumble";
|
|
if ( isdefined( self.script_rumble ) )
|
|
rumble = self.script_rumble;
|
|
|
|
level.player PlayRumbleOnEntity( rumble );
|
|
}
|
|
|
|
vehicle_event_node_setup()
|
|
{
|
|
vnode_arr = getvehiclenodearray( "vehicle_event", "script_noteworthy" );
|
|
array_thread( vnode_arr, ::vehicle_event_node );
|
|
|
|
vnode_arr = getvehiclenodearray( "vehicle_dialogue", "script_noteworthy" );
|
|
array_thread( vnode_arr, ::vehicle_dialogue_node );
|
|
}
|
|
|
|
vehicle_dialogue_node()
|
|
{
|
|
self waittill( "trigger" );
|
|
|
|
// don't player this one line if the flag is set. Yes it's ugly, but so be it.
|
|
if ( flag( "ride_minigun_gunner_dead" ) && self.script_soundalias == "byard_pri_thedrivers" )
|
|
return;
|
|
|
|
level.price dialogue_queue( self.script_soundalias );
|
|
}
|
|
|
|
vehicle_event_node()
|
|
{
|
|
// doing the handling of vehicle nodes here since the global script isn't working right now.
|
|
// besides it lets me do more stuff to.
|
|
while( true )
|
|
{
|
|
self waittill( "trigger", vehicle );
|
|
assert( isdefined( vehicle ) );
|
|
|
|
vehicle notify( "event", self.script_parameters );
|
|
|
|
if ( isdefined( self.script_vehicleaianim ) )
|
|
{
|
|
vehicle Maps\_vehicle::vehicle_ai_event( self.script_vehicleaianim );
|
|
/#
|
|
println( self.script_vehicleaianim );
|
|
#/
|
|
}
|
|
}
|
|
}
|
|
|
|
vehicle_event_handler()
|
|
{
|
|
self endon( "death" );
|
|
vehicle = self;
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "event", event );
|
|
|
|
if ( !isdefined( event ) )
|
|
continue;
|
|
|
|
switch( event )
|
|
{
|
|
case "enable_crashing":
|
|
vehicle VehPhys_EnableCrashing();
|
|
break;
|
|
case "disable_crashing":
|
|
vehicle VehPhys_DisableCrashing();
|
|
break;
|
|
case "allow_frontal":
|
|
vehicle VehPhys_EnableCrashing();
|
|
vehicle thread vehicle_track_frontal();
|
|
break;
|
|
case "end_induced_death":
|
|
vehicle notify( "end_induced_death" );
|
|
break;
|
|
case "start_induced_death":
|
|
vehicle thread vehicle_player_induced_death();
|
|
break;
|
|
case "frontal":
|
|
vehicle notify( "dying" );
|
|
vehicle.dontunloadonend = true;
|
|
array_thread( vehicle.riders, ::vehicle_frontal_guy, vehicle );
|
|
vector = rotate_vector( ( 0, 0, 50 ), vehicle.angles );
|
|
vehicle VehPhys_launch( vector, false );
|
|
wait randomfloatrange( 0, 0.2 );
|
|
vehicle maps\_vehicle::godoff();
|
|
vehicle maps\_vehicle::force_kill();
|
|
break;
|
|
case "crash":
|
|
vehicle notify( "dying" );
|
|
vehicle.dontunloadonend = true;
|
|
|
|
if ( issubstr( vehicle.classname, "pickup" ) )
|
|
{
|
|
array_thread( vehicle.riders, ::vehicle_crash_guy, vehicle );
|
|
vehicle thread vehicle_crash_launch_guys();
|
|
}
|
|
else
|
|
vehicle Maps\_vehicle::vehicle_ai_event( "idle" );
|
|
|
|
vehicle VehPhys_EnableCrashing();
|
|
vector = rotate_vector( ( 64, -256, 0 ), vehicle.angles );
|
|
vehicle VehPhys_launch( vector, false );
|
|
|
|
vehicle waittill_still( 4, 200 );
|
|
vehicle maps\_vehicle::godoff();
|
|
vehicle maps\_vehicle::force_kill();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
vehicle_crash_guy( vehicle )
|
|
{
|
|
if ( self.vehicle_position == 0 )
|
|
return;
|
|
|
|
self.deathanim = undefined;
|
|
self.noragdoll = undefined;
|
|
vehicle.riders = array_remove( vehicle.riders, self );
|
|
|
|
self.ragdoll_immediate = true;
|
|
self kill();
|
|
}
|
|
|
|
vehicle_crash_launch_guys()
|
|
{
|
|
wait 0.1; // .1 longer wait then the one in vehicle_crash_guy
|
|
expl_origin = self gettagorigin( "tag_guy1" );
|
|
PhysicsExplosionCylinder( expl_origin, 300, 300, 2 );
|
|
}
|
|
|
|
vehicle_frontal_guy( vehicle )
|
|
{
|
|
if ( self.vehicle_position == 0 )
|
|
return;
|
|
|
|
self.deathanim = undefined;
|
|
self.noragdoll = undefined;
|
|
vehicle.riders = array_remove( vehicle.riders, self );
|
|
vehicle waittill( "death" );
|
|
|
|
wait 0.05;
|
|
self unlink();
|
|
if ( isalive( self ) )
|
|
self kill( level.player.origin, level.player );
|
|
}
|
|
|
|
vehicle_player_induced_death()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "dying" );
|
|
self endon( "end_induced_death" );
|
|
|
|
// we don't wan't them to crash.
|
|
damage_limit_arr = [];
|
|
damage_limit_arr[0] = 2500;
|
|
damage_limit_arr[1] = 2500;
|
|
damage_limit_arr[2] = 3000;
|
|
damage_limit_arr[3] = 3500;
|
|
|
|
difficulty = level.gameskill;
|
|
|
|
damage_limit = damage_limit_arr[ difficulty ];
|
|
total_damage = 0;
|
|
|
|
while( total_damage < damage_limit )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, dflags );
|
|
if ( attacker != level.player )
|
|
continue;
|
|
if ( type == "MOD_PROJECTILE" ) // always crash from direct hit rpgs.
|
|
break;
|
|
total_damage += damage;
|
|
}
|
|
|
|
self thread vehicle_crash_turn();
|
|
}
|
|
|
|
vehicle_track_frontal()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "dying" );
|
|
|
|
crash_limit = 5000;
|
|
while( true )
|
|
{
|
|
self waittill( "veh_jolt", vector );
|
|
force = abs( vector[0] ) + abs( vector[1] ) + abs( vector[2] );
|
|
if ( force > crash_limit )
|
|
self notify( "event", "frontal" );
|
|
}
|
|
}
|
|
|
|
vehicle_crash_turn( left_turn, wait_time )
|
|
{
|
|
// right turn
|
|
offset = ( 64, -256, 16 );
|
|
vector = ( 64, 256, 0 );
|
|
|
|
if ( isdefined( left_turn ) )
|
|
{
|
|
// left turn default
|
|
offset = ( 64, 256, 16 );
|
|
vector = ( 64, -256, 0 );
|
|
}
|
|
|
|
offset = rotate_vector( offset, self.angles );
|
|
goal_origin = self.origin + offset;
|
|
|
|
self notify( "newpath" );
|
|
self notify( "dying" );
|
|
|
|
self.dontunloadonend = true;
|
|
|
|
if ( issubstr( self.classname, "pickup" ) )
|
|
{
|
|
array_thread( self.riders, ::vehicle_crash_guy, self );
|
|
self thread vehicle_crash_launch_guys();
|
|
}
|
|
else
|
|
self Maps\_vehicle::vehicle_ai_event( "idle" );
|
|
|
|
self VehPhys_EnableCrashing();
|
|
self vehicleDriveTo( goal_origin, self.veh_speed );
|
|
|
|
while( abs( self Vehicle_GetSteering() ) < 1 )
|
|
wait 0.05;
|
|
|
|
vector = rotate_vector( vector, self.angles );
|
|
self VehPhys_launch( vector, false );
|
|
|
|
if ( !isdefined( wait_time ) )
|
|
wait_time = 4;
|
|
|
|
self waittill_still( wait_time, 200 );
|
|
self maps\_vehicle::godoff();
|
|
self maps\_vehicle::force_kill();
|
|
}
|
|
|
|
waittill_still( timeout, still_point )
|
|
{
|
|
mytimeOutEnt( timeout ) endon ( "timeout" );
|
|
|
|
if ( !isdefined( still_point ) )
|
|
still_point = 50;
|
|
|
|
velocity = self Vehicle_GetVelocity();
|
|
velocity = abs( velocity[0] ) + abs( velocity[1] ) + abs( velocity[2] );
|
|
|
|
while( velocity > still_point )
|
|
{
|
|
velocity = self Vehicle_GetVelocity();
|
|
velocity = abs( velocity[0] ) + abs( velocity[1] ) + abs( velocity[2] );
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
launch_object_setup()
|
|
{
|
|
array_thread( getentarray( "launch_object", "targetname" ), ::launch_object );
|
|
}
|
|
|
|
launch_object()
|
|
{
|
|
obj_array = getentarray( self.target, "targetname" );
|
|
self waittill( "trigger", vehicle );
|
|
|
|
force_multiplier_arr = [];
|
|
force_multiplier_arr[ "com_parkingcone01" ] = 2;
|
|
force_multiplier_arr[ "bc_military_tire04" ] = 25;
|
|
force_multiplier_arr[ "bc_military_tire05" ] = 25;
|
|
force_multiplier_arr[ "com_junktire" ] = 25;
|
|
force_multiplier_arr[ "road_barrier_post" ] = 4; // old 22
|
|
|
|
foreach( obj in obj_array )
|
|
{
|
|
// playes a light rumble for each impact.
|
|
if ( vehicle == level.uaz )
|
|
level.player PlayRumbleOnEntity( "damage_light" );
|
|
|
|
velocity = vehicle vehicle_getvelocity();
|
|
angles1 = vectortoangles( ( obj.origin + (0,0,6) ) - vehicle.origin );
|
|
angles2 = vectortoangles( velocity );
|
|
|
|
// add a slight random angle so that the stacks don't look bad.
|
|
if ( obj_array.size > 2 )
|
|
{
|
|
random_angels = (0, randomint( 30 ) -15, 0 );
|
|
angles1 += random_angels;
|
|
}
|
|
|
|
rotation = ( angles1 - angles2 );
|
|
velocity = rotate_vector( velocity, rotation );
|
|
|
|
force_multiplier = 2;
|
|
if ( isdefined( force_multiplier_arr[ obj.model ] ) )
|
|
force_multiplier = force_multiplier_arr[ obj.model ];
|
|
|
|
// lets me overide if needed.
|
|
if ( isdefined( obj.script_accel ) )
|
|
force_multiplier = force_multiplier * obj.script_accel;
|
|
|
|
//play sound from hit
|
|
if ( isdefined( obj.script_soundalias ) )
|
|
level thread play_sound_in_space( obj.script_soundalias, obj.origin );
|
|
|
|
velocity *= force_multiplier;
|
|
|
|
direction = vectornormalize( velocity * -1 );
|
|
contact_point = obj.origin + direction * 16;
|
|
obj PhysicsLaunchClient( contact_point, velocity );
|
|
|
|
|
|
dir = vectornormalize( velocity );
|
|
// line( contact_point, contact_point + 100 * dir, (1,0,0), 1, 0, 100 );
|
|
}
|
|
}
|
|
|
|
mytimeOutEnt( timeOut )
|
|
{
|
|
ent = spawnstruct();
|
|
ent delaythread( timeOut, ::send_notify, "timeout" );
|
|
return ent;
|
|
}
|
|
|
|
collapse_wing()
|
|
{
|
|
flag_wait( "wing_fall" );
|
|
exploder( "wing_fall" );
|
|
|
|
wing = getent( "wing", "targetname" );
|
|
pivot_1 = getent( "pivot_1", "targetname" );
|
|
pivot_2 = getent( "pivot_2", "targetname" );
|
|
|
|
pivot_1.angles = vectortoangles( pivot_2.origin - pivot_1.origin );
|
|
pivot_2.angles = vectortoangles( pivot_1.origin - pivot_2.origin );
|
|
|
|
wing linkto( pivot_1 );
|
|
pivot_2 linkto( pivot_1 );
|
|
|
|
ground_1 = PhysicsTrace( pivot_2.origin + (0,0,-64), pivot_2.origin + (0,0,-256) );
|
|
ground_2 = PhysicsTrace( pivot_1.origin + (0,0,-65), pivot_1.origin + (0,0,-256) );
|
|
|
|
angles = vectortoangles( ground_1 - pivot_1.origin );
|
|
pivot_1 rotateto( angles, .4, .3, 0 );
|
|
pivot_1 waittill( "rotatedone" );
|
|
angles = vectortoangles( (ground_1 + (0,0,40) ) - pivot_1.origin );
|
|
pivot_1 rotateto( angles, .25, 0, .25 );
|
|
pivot_1 waittill( "rotatedone" );
|
|
angles = vectortoangles( ground_1 - pivot_1.origin );
|
|
pivot_1 rotateto( angles, .25, .25, 0 );
|
|
pivot_1 waittill( "rotatedone" );
|
|
angles = vectortoangles( (ground_1 + (0,0,10) ) - pivot_1.origin );
|
|
pivot_1 rotateto( angles, .1, 0, .1 );
|
|
pivot_1 waittill( "rotatedone" );
|
|
angles = vectortoangles( ground_1 - pivot_1.origin );
|
|
pivot_1 rotateto( angles, .1, .1, 0 );
|
|
pivot_1 waittill( "rotatedone" );
|
|
|
|
wing unlink();
|
|
pivot_2 unlink();
|
|
wing linkto( pivot_2 );
|
|
|
|
angles = vectortoangles( ground_2 - pivot_2.origin );
|
|
pivot_2 rotateto( angles, .3, .2, 0 );
|
|
pivot_2 waittill( "rotatedone" );
|
|
angles = vectortoangles( (ground_2 + (0,0,20) ) - pivot_2.origin );
|
|
pivot_2 rotateto( angles, .2, 0, .2 );
|
|
pivot_2 waittill( "rotatedone" );
|
|
angles = vectortoangles( ground_2 - pivot_2.origin );
|
|
pivot_2 rotateto( angles, .2, .2, 0 );
|
|
pivot_2 waittill( "rotatedone" );
|
|
angles = vectortoangles( (ground_2 + (0,0,5) ) - pivot_2.origin );
|
|
pivot_2 rotateto( angles, .1, 0, .1 );
|
|
pivot_2 waittill( "rotatedone" );
|
|
angles = vectortoangles( ground_2 - pivot_2.origin );
|
|
pivot_2 rotateto( angles, .1, .1, 0 );
|
|
pivot_2 waittill( "rotatedone" );
|
|
}
|
|
|
|
/**** C130 STUFF ****/
|
|
assemble_c130()
|
|
{
|
|
c130 = getent( "c130_flight", "targetname" );
|
|
lower_hatch = getent( "lower_hatch", "targetname" );
|
|
upper_hatch = getent( "upper_hatch", "targetname" );
|
|
hinge_arr = getentarray( "hinge", "targetname" );
|
|
|
|
lower_hatch_origin = getent( "lower_hatch_origin", "targetname" );
|
|
upper_hatch_origin = getent( "upper_hatch_origin", "targetname" );
|
|
hinge_origin_arr = getentarray( "hinge_origin", "targetname" );
|
|
|
|
array_call( getentarray( c130.target, "targetname" ), ::linkto, c130 );
|
|
array_call( getentarray( lower_hatch.target, "targetname" ), ::linkto, lower_hatch );
|
|
array_call( getentarray( upper_hatch.target, "targetname" ), ::linkto, upper_hatch );
|
|
array_call( getentarray( hinge_arr[0].target, "targetname" ), ::linkto, hinge_arr[0] );
|
|
array_call( getentarray( hinge_arr[1].target, "targetname" ), ::linkto, hinge_arr[1] );
|
|
|
|
array_thread( getentarray( "c130_spark_origin", "script_noteworthy" ), ::c130_sparks );
|
|
|
|
hinge_arr[0].angles = ( 175, 90, 0 );
|
|
hinge_arr[1].angles = ( 170, 90, 0 );
|
|
|
|
lower_hatch thread c130_rotate_hatch( hinge_arr[0], hinge_origin_arr[0].origin, 0.25, 1.85, 12.1 );
|
|
lower_hatch thread c130_rotate_hatch( hinge_arr[1], hinge_origin_arr[1].origin, 0.3, 1.5, 11.5 );
|
|
|
|
hinge_delay = 4.9;
|
|
c130 thread c130_rotate_hatch( lower_hatch, lower_hatch_origin.origin, 0.015, 5.9, 3 );
|
|
c130 thread c130_rotate_hatch( upper_hatch, upper_hatch_origin.origin, 0.01, hinge_delay );
|
|
|
|
thread extra_row_of_sparks_from_ramp( lower_hatch );
|
|
|
|
c130.ramp_trigger = getent( "ramp_trigger", "targetname" );
|
|
c130.ramp_trigger enablelinkto();
|
|
c130.ramp_trigger LinkTo( c130 );
|
|
|
|
array_thread( getentarray( "ramp_death_trigger", "targetname" ), ::ramp_death_trigger, c130 );
|
|
|
|
array_thread( getentarray( "c130_propeller", "script_noteworthy" ), ::c130_propeller );
|
|
|
|
return c130;
|
|
}
|
|
|
|
extra_row_of_sparks_from_ramp( my_hatch )
|
|
{
|
|
for ( ;; )
|
|
{
|
|
level waittill( "hinge_stopped", hatch );
|
|
if ( hatch == my_hatch )
|
|
break;
|
|
}
|
|
|
|
hinges = getentarray( "hinge", "targetname" );
|
|
|
|
hinges[0] PlayLoopSound( "veh_c130_ramp_scrape_loop" );
|
|
|
|
start_origin = level.c130.origin[ 2 ];
|
|
for ( ;; )
|
|
{
|
|
// stop if c130 takes off
|
|
if ( level.c130.origin[ 2 ] > start_origin + 50 )
|
|
{
|
|
hinges[0] StopLoopSound();
|
|
return;
|
|
}
|
|
sparks = randomintrange( 0, 2 );
|
|
for ( i = 0; i < sparks; i++ )
|
|
{
|
|
thread spawn_spark_between_hinges( hinges );
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
spawn_spark_between_hinges( hinges )
|
|
{
|
|
ent = spawn_tag_origin();
|
|
org_dif = randomfloat(1.3);
|
|
ent.origin = hinges[0].origin * org_dif + hinges[1].origin * ( 1 - org_dif );
|
|
ent.origin += ( 0, -10, -14 );
|
|
ent.angles = ( 308, 270, 0 );
|
|
ent linkto( hinges[ 0 ] );
|
|
|
|
spark_fx = getfx( "scrape_sparks" );
|
|
PlayFXOnTag( spark_fx, ent, "tag_origin" );
|
|
delay = 4;
|
|
delay *= 20;
|
|
for ( i = 0; i < delay; i++ )
|
|
{
|
|
// Print3d( ent.origin, "x", (1,0,0), 1, 1 );
|
|
wait( 0.05 );
|
|
}
|
|
wait( 4 );
|
|
ent delete();
|
|
}
|
|
|
|
|
|
ramp_death_trigger( c130 )
|
|
{
|
|
level endon( "uaz_park" );
|
|
|
|
self enablelinkto();
|
|
self LinkTo( c130 );
|
|
|
|
flag_wait( "uaz_player_in_control" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "trigger", vehicle );
|
|
if ( vehicle == level.uaz )
|
|
break;
|
|
}
|
|
|
|
level notify( "uaz_park_crash" );
|
|
|
|
level.uaz vehicle_crash_turn( self.script_parameters, 1.5 );
|
|
|
|
level.player kill();
|
|
// missionfailedwrapper();
|
|
}
|
|
|
|
ride_uaz_player_launch()
|
|
{
|
|
throw = ( -300,600,500 );
|
|
enablePlayerWeapons( false );
|
|
level.player.rig unlink();
|
|
level.player.rig RotateVelocity( ( 300, 300, 300 ), 10 );
|
|
level.player.rig MoveGravity( throw, 1);
|
|
level.player.rig hide();
|
|
}
|
|
|
|
c130_rotate_hatch( hatch, hinge_origin, rate, stop_time, delay )
|
|
{
|
|
wait_time = 5;
|
|
hinge = spawn( "script_model", hinge_origin );
|
|
hinge.angles = ( 0, 0, 0 );
|
|
hinge setmodel( "tag_origin" );
|
|
hinge linkto( self );
|
|
hatch.origin = hinge_origin;
|
|
hatch linkto( hinge );
|
|
|
|
flag_wait( "c130_hatch_open" );
|
|
wait( wait_time );
|
|
|
|
if ( isdefined( delay ) )
|
|
wait delay;
|
|
|
|
hinge.animname = "c130_propeller";
|
|
hinge assign_animtree();
|
|
|
|
hinge_anim = level.scr_anim[ "c130_propeller" ][ "rotate" ][0];
|
|
|
|
hinge AnimScripted( "rotate_hatch", hinge.origin, hinge.angles, hinge_anim );
|
|
hinge SetFlaggedAnim( "rotate_hatch", hinge_anim, 1, 0, rate );
|
|
wait stop_time;
|
|
hinge SetFlaggedAnim( "rotate_hatch", hinge_anim, 1, 0, 0 );
|
|
level notify( "hinge_stopped", hatch );
|
|
}
|
|
|
|
c130_propeller()
|
|
{
|
|
wait randomfloat( 2 );
|
|
self.animname = "c130_propeller";
|
|
self assign_animtree();
|
|
self anim_loop_solo( self, "rotate" );
|
|
}
|
|
|
|
c130_sparks()
|
|
{
|
|
flag_wait( "c130_hatch_open" );
|
|
origin_offset = (0,-32,0);
|
|
angles_offset = (90,0,0);
|
|
|
|
ent = self spawn_tag_origin();
|
|
ent.origin += origin_offset;
|
|
ent.angles = angles_offset;
|
|
ent linkto( self );
|
|
//ent thread maps\_debug::drawtagforever( "tag_origin" );
|
|
|
|
wait 5;
|
|
wait 12;
|
|
wait( 0.65 );
|
|
|
|
spark_fx = getfx( "scrape_sparks" );
|
|
while( true )
|
|
{
|
|
PlayFXOnTag( spark_fx, ent, "tag_origin" );
|
|
wait randomfloatrange( 0.1, 0.3 );
|
|
}
|
|
}
|
|
|
|
/**** FLYBY STUFF ****/
|
|
flyby_rumble()
|
|
{
|
|
level.player PlayRumbleOnEntity( "c130_flyby" );
|
|
level thread screenshake( .2, 5, 1, 4 );
|
|
|
|
wait 1.5;
|
|
level.player thread blend_movespeedscale( 0.1, 1.5 );
|
|
|
|
loops = int( 0.75 * 20 );
|
|
up_target_angle = -8;
|
|
for ( i = 0; i < loops; i++ )
|
|
{
|
|
player_angles = level.player getplayerangles();
|
|
up_angle = player_angles[0];
|
|
|
|
angle_step = ( up_target_angle - up_angle ) / ( loops - (i) );
|
|
angle_step = cap_value( angle_step, -1.5, 1.5 );
|
|
|
|
level.player setplayerangles( player_angles + ( angle_step, 0, 0 ) );
|
|
wait 0.05;
|
|
}
|
|
|
|
level.player thread blend_movespeedscale( 1, 1 );
|
|
}
|
|
|
|
RUMBLE_FRAMES_PER_SEC = 10;
|
|
screenshake( scale, duration, fade_in, fade_out)
|
|
{
|
|
if ( !isdefined( fade_in ) )
|
|
fade_in = 0;
|
|
if ( !isdefined( fade_out ) )
|
|
fade_out = 0;
|
|
|
|
assert( ( fade_in + fade_out ) <= duration );
|
|
|
|
frame_count = duration * RUMBLE_FRAMES_PER_SEC;
|
|
fade_in_frame_count = fade_in * RUMBLE_FRAMES_PER_SEC;
|
|
if ( fade_in_frame_count > 0 )
|
|
fade_in_scale_step = scale / fade_in_frame_count;
|
|
else
|
|
fade_in_scale_step = scale;
|
|
|
|
fade_out_frame_count = fade_out * RUMBLE_FRAMES_PER_SEC;
|
|
fade_out_start_frame = frame_count - fade_out_frame_count;
|
|
if ( fade_out_frame_count > 0 )
|
|
fade_out_scale_step = scale / fade_out_frame_count;
|
|
else
|
|
fade_out_scale_step = scale;
|
|
|
|
delay = 1/RUMBLE_FRAMES_PER_SEC;
|
|
scale = 0;
|
|
for ( i = 0; i < frame_count; i++ )
|
|
{
|
|
if ( i <= fade_in_frame_count )
|
|
scale += fade_in_scale_step;
|
|
|
|
if ( i > fade_out_start_frame )
|
|
scale -= fade_out_scale_step;
|
|
|
|
earthquake( scale, delay, level.player.origin, 500 );
|
|
wait delay;
|
|
}
|
|
}
|
|
|
|
angel_flare_burst( flare_count )
|
|
{
|
|
// Angel Flare Swirl
|
|
PlayFXOnTag( getfx( "angel_flare_swirl" ), self, "tag_flash_flares" );
|
|
|
|
self PlaySound( "ac130_flare_burst" );
|
|
|
|
// Angel Flare Trails
|
|
for( i=0; i<flare_count; i++ )
|
|
{
|
|
self thread angel_flare();
|
|
wait randomfloatrange( 0.1, 0.25 );
|
|
}
|
|
}
|
|
|
|
angel_flare()
|
|
{
|
|
if ( !isdefined( level.anim_index ) )
|
|
level.anim_index = 0;
|
|
|
|
rig = spawn_anim_model( "angel_flare_rig" );
|
|
|
|
rig.origin = self gettagorigin( "tag_flash_flares" );
|
|
rig.angles = self gettagangles( "tag_flash_flares" );
|
|
|
|
fx_id = level._effect[ "angel_flare_geotrail" ];
|
|
|
|
|
|
anim_count = level.scr_anim[ "angel_flare_rig" ][ "ac130_angel_flares" ].size;
|
|
animation = level.scr_anim[ "angel_flare_rig" ][ "ac130_angel_flares" ][ level.anim_index % anim_count ];
|
|
level.anim_index++;
|
|
|
|
|
|
// animation = random( level.scr_anim[ "angel_flare_rig" ][ "ac130_angel_flares" ] );
|
|
|
|
rig SetFlaggedAnim( "flare_anim", animation, 1, 0, 1 );
|
|
|
|
wait 0.1;
|
|
PlayFXOnTag( fx_id, rig, "flare_left_top" );
|
|
PlayFXOnTag( fx_id, rig, "flare_right_top" );
|
|
wait 0.05;
|
|
PlayFXOnTag( fx_id, rig, "flare_left_bot" );
|
|
PlayFXOnTag( fx_id, rig, "flare_right_bot" );
|
|
|
|
rig waittillmatch( "flare_anim", "end" );
|
|
|
|
StopFXOnTag( fx_id, rig, "flare_left_top" );
|
|
StopFXOnTag( fx_id, rig, "flare_right_top" );
|
|
StopFXOnTag( fx_id, rig, "flare_left_bot" );
|
|
StopFXOnTag( fx_id, rig, "flare_right_bot" );
|
|
}
|
|
|
|
/***** THREE WAY FIGHTING STUFF *****/
|
|
|
|
threewayfight_setup()
|
|
{
|
|
add_global_spawn_function( "axis" , ::threewayfight_threads );
|
|
add_global_spawn_function( "team3" , ::threewayfight_threads );
|
|
|
|
// doesn't count ai waiting to be deleted. Won't affect ai riding vehicles or ai with valid script_noteworthy
|
|
level.ai_cap[ "axis" ] = 15;
|
|
level.ai_cap[ "team3" ] = 15;
|
|
level.ai_cap[ "axis_alive" ] = 0;
|
|
level.ai_cap[ "team3_alive" ] = 0;
|
|
|
|
level thread switch_colors();
|
|
}
|
|
|
|
threewayfight_threads()
|
|
{
|
|
// don't do this on vehicles or other non sentient ents
|
|
if ( !issentient( self ) )
|
|
return;
|
|
|
|
state = "default";
|
|
if ( isdefined( self.script_noteworthy ) )
|
|
state = self.script_noteworthy;
|
|
|
|
difficulty = level.gameskill;
|
|
if ( difficulty == 0 )
|
|
difficulty = 1;
|
|
|
|
// might work to work around the self.ridingvehicle issue.
|
|
waittillframeend;
|
|
|
|
switch( state )
|
|
{
|
|
case "scripted_guy":
|
|
break;
|
|
case "enemy_dog":
|
|
self thread enemy_dog();
|
|
break;
|
|
case "struggle_guy":
|
|
self thread struggle_guy();
|
|
break;
|
|
case "rpg_guy":
|
|
self thread rpg_guy();
|
|
break;
|
|
case "spray_guy":
|
|
self thread spray_guy();
|
|
break;
|
|
case "minigun_guy": // NO BREAK PAST THIS POINT!!!
|
|
self.health *= difficulty;
|
|
if ( level.gameskill >= 2 )
|
|
{
|
|
// make hardened and veteran harder
|
|
self setthreatbiasgroup( "aware_of_player" );
|
|
}
|
|
default:
|
|
// delete generic guys that would push the count above the limit
|
|
// there is probably a better way to do this.
|
|
if ( level.ai_cap[ self.team ] < level.ai_cap[ self.team + "_alive" ] && !isdefined( self.ridingvehicle ) )
|
|
{
|
|
/#
|
|
println( "^2didn't spawn " + self.team + " guy because of ai_cap." );
|
|
#/
|
|
self waittill( "finished spawning" );
|
|
self delete();
|
|
}
|
|
else
|
|
{
|
|
self thread ai_cap_count();
|
|
self threewayfight_threads_defaults();
|
|
}
|
|
}
|
|
}
|
|
|
|
threewayfight_threads_defaults()
|
|
{
|
|
self bad_aim(); // returns
|
|
self thread react_to_player();
|
|
self thread player_enemy();
|
|
self thread delay_awareness();
|
|
|
|
if ( self.team == "team3" && randomint( 4 ) == 0 )
|
|
self enable_heat_behavior( true );
|
|
}
|
|
|
|
ai_cap_count()
|
|
{
|
|
ai_team = self.team;
|
|
level.ai_cap[ ai_team + "_alive" ]++;
|
|
self waittill_any( "death", "tobedeleted", "long_death" );
|
|
level.ai_cap[ ai_team + "_alive" ]--;
|
|
|
|
assertEx( level.ai_cap[ ai_team + "_alive" ] >= 0, "How did this get to be less then 0?" );
|
|
}
|
|
|
|
enemy_dog()
|
|
{
|
|
//???
|
|
}
|
|
|
|
delay_awareness()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "tobedeleted" );
|
|
|
|
if ( isdefined( self.script_noteworthy ) && self.script_noteworthy == "delay_awareness" )
|
|
{
|
|
if ( isdefined( self.ridingvehicle ) )
|
|
{
|
|
self setthreatbiasgroup( "ignore_player" );
|
|
self waittill( "jumpedout" );
|
|
wait 2;
|
|
self setthreatbiasgroup( self.team );
|
|
}
|
|
else
|
|
{
|
|
self setthreatbiasgroup( "ignore_player" );
|
|
self waittill_any_timeout( 3, "damage", "goal" );
|
|
self setthreatbiasgroup( self.team );
|
|
}
|
|
}
|
|
}
|
|
|
|
react_to_player()
|
|
{
|
|
// if the player shoots at an AI the threatbias for that AI will be switched
|
|
// to one that doesn't make the player less of a threat.
|
|
|
|
self endon( "death" );
|
|
self endon( "tobedeleted" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill_stack_add( "bulletwhizby" );
|
|
self waittill_stack_add( "damage" );
|
|
|
|
self waittill( "waittill_stack", msg );
|
|
|
|
if (
|
|
( msg == "bulletwhizby" && self.waittill_stack.a == level.player )
|
|
||
|
|
( msg == "damage" && self.waittill_stack.b == level.player )
|
|
)
|
|
break;
|
|
}
|
|
|
|
self waittill_stack_clear(); // removes variables used by waittill_stack.
|
|
self setthreatbiasgroup( "aware_of_player" );
|
|
}
|
|
|
|
player_enemy()
|
|
{
|
|
// default values are 30000 and 3.
|
|
level.advanceToEnemyInterval = 120000; // less often.
|
|
// level.advanceToEnemyGroupMax = 1;
|
|
|
|
default_MaxDist = self.engagemaxdist;
|
|
default_FalloffDist = self.engagemaxfalloffdist;
|
|
|
|
// Change engagement dist when fighting the player.
|
|
self endon( "death" );
|
|
self endon( "tobedeleted" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "enemy" );
|
|
|
|
if ( Isplayer( self.enemy ) )
|
|
{
|
|
// reset dists
|
|
self SetEngagementMaxDist( default_MaxDist, default_FalloffDist );
|
|
}
|
|
else
|
|
{
|
|
// lets the ai fight from further away. This stops them from moving around when they are not fighting the player.
|
|
self SetEngagementMaxDist( default_MaxDist * 2, default_FalloffDist * 2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
bad_aim()
|
|
{
|
|
// makes the ai less likely to kill each other, but still attack the player with full Accuracy.
|
|
|
|
self endon( "death" );
|
|
self.attackerAccuracy = 0.1;
|
|
}
|
|
|
|
struggle_guy()
|
|
{
|
|
// Is supposed to spawn close to an enemy struggle guy and then do a melee sequence.
|
|
// will eventually forced what melee animations to use.
|
|
|
|
self endon( "death" );
|
|
old_sightdist = self.maxsightdistsqrd;
|
|
self.maxsightdistsqrd = 96*96;
|
|
self.dontEverShoot = true;
|
|
wait 1;
|
|
self.maxsightdistsqrd = old_sightdist;
|
|
self.dontEverShoot = undefined;
|
|
self threewayfight_threads_defaults();
|
|
}
|
|
|
|
turret_track_target( turret, heli )
|
|
{
|
|
heli thread fake_target();
|
|
|
|
mg_guy = turret getturretowner();
|
|
assert( isdefined( mg_guy ) );
|
|
mg_guy endon( "death" );
|
|
heli endon( "death" );
|
|
self endon( "death" );
|
|
|
|
while( true )
|
|
{
|
|
etarget = random( heli.fake_target );
|
|
turret animscripts\hummer_turret\common::set_manual_target( etarget, 3, 5 );
|
|
mg_guy.ignoreall = true;
|
|
turret animscripts\hummer_turret\common::set_manual_target( etarget, 1, 3 );
|
|
mg_guy.ignoreall = false;
|
|
}
|
|
}
|
|
|
|
fake_target()
|
|
{
|
|
self.fake_target = [];
|
|
|
|
right = anglestoright( self.angles );
|
|
|
|
offset = right * 320;
|
|
self.fake_target[0] = spawn( "script_origin", self.origin + offset );
|
|
self.fake_target[0] linkto( self );
|
|
offset = right * -320;
|
|
self.fake_target[1] = spawn( "script_origin", self.origin + offset );
|
|
self.fake_target[1] linkto( self );
|
|
|
|
self waittill( "death" );
|
|
self.fake_target[0] delete();
|
|
self.fake_target[1] delete();
|
|
}
|
|
|
|
littlebird_reaction( path_name )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "reaction_end" );
|
|
|
|
paths = getentarray( path_name, "script_noteworthy" );
|
|
total_damage = 0;
|
|
|
|
index = 0;
|
|
|
|
while( true )
|
|
{
|
|
flag_wait( "littlebird_react" );
|
|
|
|
self waittill_stack_add( "damage" );
|
|
self waittill_stack_add( "reached_dynamic_path_end" );
|
|
self waittill_stack_add( "react" );
|
|
|
|
self waittill( "waittill_stack" , msg );
|
|
|
|
if ( msg == "damage" )
|
|
{
|
|
amount = self.waittill_stack.a;
|
|
damager = self.waittill_stack.b;
|
|
|
|
if ( !flag( "littlebird_react" ) ) // || damager != level.player ) - let heli react to any damage
|
|
continue;
|
|
|
|
total_damage += amount;
|
|
if ( total_damage < 400 )
|
|
continue;
|
|
}
|
|
|
|
total_damage = 0;
|
|
|
|
current_path = paths[ index ];
|
|
index++;
|
|
if ( index > paths.size )
|
|
{
|
|
paths = array_randomize( paths );
|
|
index = 0;
|
|
}
|
|
|
|
/#
|
|
// debug shit
|
|
level thread mark_heli_path( current_path );
|
|
#/
|
|
|
|
self SetMaxPitchRoll( 25, 25 );
|
|
self maps\_vehicle::set_heli_move( "fast" );
|
|
self Vehicle_SetSpeed( 65, 25, 25 );
|
|
|
|
self thread maps\_vehicle::vehicle_paths( current_path );
|
|
|
|
self waittill_either( "goal", "near_goal" );
|
|
wait 2;
|
|
// self maps\_vehicle::set_heli_move( "fast" );
|
|
// self SetMaxPitchRoll( 25, 25 );
|
|
self thread littlebird_notarget_timeout();
|
|
}
|
|
}
|
|
|
|
littlebird_notarget_timeout()
|
|
{
|
|
self endon( "reaction_end" );
|
|
self endon( "react" );
|
|
self endon( "death" );
|
|
|
|
wait 3;
|
|
|
|
timeout = 5000;
|
|
start_time = gettime();
|
|
|
|
while ( true )
|
|
|
|
{
|
|
wait 0.05;
|
|
if ( isdefined( self.mgturret[0] getturrettarget(0) ) || isdefined( self.mgturret[1] getturrettarget(0) ) )
|
|
{
|
|
start_time = gettime();
|
|
continue;
|
|
}
|
|
if ( start_time + timeout < gettime() )
|
|
break;
|
|
}
|
|
|
|
self notify( "react" );
|
|
}
|
|
|
|
road_rocket_guys_clear()
|
|
{
|
|
self endon( "death" );
|
|
self waittill( "rpg_guy_done" );
|
|
self.ignoreme = false;
|
|
self.health = 25;
|
|
self thread move_and_delete();
|
|
}
|
|
|
|
rpg_guy()
|
|
{
|
|
// rpg guy will show up and fire one rgp at a random target and then become a normal guy.
|
|
// target node needs to target atleast one script_origin, or the ai have a self.rocket_target
|
|
|
|
assertEX( issubstr( ToLower( self.classname ), "rpg" ), "Actor with export: " + self.export + " doesn't have an RPG!" );
|
|
|
|
self endon( "death" );
|
|
|
|
self disable_ai_color();
|
|
self set_ignoreme( true );
|
|
self.combatmode = "no_cover";
|
|
self.rpg_guy = true;
|
|
|
|
goal = getnode( self.target, "targetname" );
|
|
if ( isdefined( goal ) )
|
|
{
|
|
self setgoalnode( goal );
|
|
}
|
|
else
|
|
{
|
|
goal = getent( self.target, "targetname" );
|
|
self setgoalpos( goal.origin );
|
|
}
|
|
|
|
if ( isdefined( goal.radius ) )
|
|
self.goalradius = goal.radius;
|
|
else
|
|
self.goalradius = 32;
|
|
|
|
timeout = 10;
|
|
|
|
if ( !isdefined( goal.script_shotcount ) || goal.script_shotcount == 0 )
|
|
goal.script_shotcount = 1;
|
|
|
|
self.a.rockets = goal.script_shotcount;
|
|
|
|
for ( i=0; i<goal.script_shotcount; i++ )
|
|
{
|
|
if ( isdefined( goal.target ) )
|
|
{
|
|
target_arr = getentarray( goal.target, "targetname" );
|
|
etarget = random( target_arr );
|
|
}
|
|
else if ( isdefined( self.rocket_target ) )
|
|
{
|
|
etarget = self.rocket_target;
|
|
timeout = 30;
|
|
}
|
|
else
|
|
{
|
|
assertMsg( "rpg guy didn't get a target" );
|
|
return;
|
|
}
|
|
|
|
if ( isdefined( etarget.script_parameters ) && etarget.script_parameters == "straight" )
|
|
self SetStableMissile( true );
|
|
|
|
if ( issentient( etarget ) )
|
|
{
|
|
ent = spawn( "script_origin", etarget.origin );
|
|
ent linkto( etarget );
|
|
self SetEntityTarget( ent, 1 );
|
|
}
|
|
else
|
|
self SetEntityTarget( etarget, 1 );
|
|
|
|
self waittill_notify_or_timeout( "missile_fire", timeout );
|
|
etarget notify( "rpg_fired" );
|
|
}
|
|
|
|
wait 2;
|
|
|
|
self ClearEntityTarget();
|
|
self enable_ai_color();
|
|
self set_ignoreme( false );
|
|
self.combatmode = "cover";
|
|
self.rpg_guy = undefined;
|
|
|
|
self threewayfight_threads_defaults();
|
|
self notify( "rpg_guy_done" );
|
|
}
|
|
|
|
spray_guy()
|
|
{
|
|
// spray guy will show up and fire for a few seconds and then become a normal guy.
|
|
// target node needs to target atleast two script_origins.
|
|
// set script_wait on the spawner to control how long he will spray.
|
|
|
|
self endon( "death" );
|
|
self disable_ai_color();
|
|
|
|
self.spray_guy = true;
|
|
|
|
node = getnode( self.target, "targetname" );
|
|
if ( isdefined( node.radius ) )
|
|
self.goalradius = node.radius;
|
|
else
|
|
self.goalradius = 32;
|
|
self setgoalnode( node );
|
|
|
|
self.no_pistol_switch = true;
|
|
self.ignoresuppression = true;
|
|
// self.maxFaceEnemyDist = 0;
|
|
self.noRunReload = true;
|
|
// self.ammoCheatInterval = 2000;
|
|
self.disableBulletWhizbyReaction = true;
|
|
self.combatmode = "no_cover";
|
|
self SetThreatBiasGroup( "lowthreat" );
|
|
|
|
target_arr = getentarray( node.target, "targetname" );
|
|
assert( isdefined( target_arr ) && target_arr.size > 1 );
|
|
target_arr = array_randomize( target_arr );
|
|
|
|
self SetEntityTarget( target_arr[0], 1 );
|
|
if ( isdefined( self.script_wait ) )
|
|
move_time = self.script_wait;
|
|
else
|
|
move_time = randomfloatrange( 1, 2.5 );
|
|
|
|
self waittill_notify_or_timeout( "goal", 5 );
|
|
|
|
assert( move_time >= 1 );
|
|
target_arr[0] MoveTo( target_arr[1].origin, move_time, move_time/2, move_time/4 );
|
|
wait move_time;
|
|
|
|
self ClearEntityTarget();
|
|
self enable_ai_color();
|
|
|
|
self.no_pistol_switch = undefined;
|
|
self.ignoresuppression = false;
|
|
// self.maxFaceEnemyDist = 512;
|
|
self.noRunReload = undefined;
|
|
self.disableBulletWhizbyReaction = undefined;
|
|
self.combatmode = "cover";
|
|
self SetThreatBiasGroup( self.team );
|
|
self.spray_guy = undefined;
|
|
|
|
self threewayfight_threads_defaults();
|
|
}
|
|
|
|
smoke()
|
|
{
|
|
target_arr = getentarray( self.target, "targetname" );
|
|
target_ent = random( target_arr );
|
|
|
|
vec = target_ent.origin - self.origin + ( 0,0,200 );
|
|
MagicGrenademanual( "smoke_grenade_american", self.origin + (0,0,80), vec, 1.5 );
|
|
}
|
|
|
|
delete_excess_setup()
|
|
{
|
|
array_thread( getentarray( "delete_excess", "targetname" ), ::delete_excess_trigger );
|
|
level thread delete_excess();
|
|
}
|
|
|
|
delete_excess_trigger()
|
|
{
|
|
self waittill( "trigger" );
|
|
level.delete_excess_safe_volume = undefined;
|
|
|
|
if ( isdefined( self.target ) )
|
|
level.delete_excess_safe_volume = getent( self.target, "targetname" );
|
|
|
|
flag_set( "delete_excess" );
|
|
waittillframeend; // lets everyone that waits for the flag continue their thread.
|
|
flag_clear( "delete_excess" );
|
|
}
|
|
|
|
delete_excess_exeption( guy )
|
|
{
|
|
if ( isdefined( guy.ridingvehicle ) )
|
|
return true;
|
|
if ( isdefined( guy.spray_guy ) )
|
|
return true;
|
|
if ( isdefined( guy.rpg_guy ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
delete_excess()
|
|
{
|
|
// Delete enemies that are far away because new ones will be spawned in.
|
|
while( true )
|
|
{
|
|
flag_wait( "delete_excess" );
|
|
|
|
safe_volume = level.delete_excess_safe_volume;
|
|
ai_arr = getaiarray( "axis", "team3" );
|
|
dist = 1600*1600;
|
|
|
|
foreach ( guy in ai_arr )
|
|
{
|
|
if ( delete_excess_exeption( guy ) )
|
|
continue;
|
|
if ( isdefined( safe_volume ) && guy istouching( safe_volume ) )
|
|
continue;
|
|
else if ( !isdefined( safe_volume ) && DistanceSquared( guy.origin, level.player.origin ) < dist )
|
|
continue;
|
|
|
|
guy thread move_and_delete( true );
|
|
}
|
|
|
|
flag_waitopen( "delete_excess" );
|
|
}
|
|
}
|
|
|
|
higround_littlebird_hunt_btr80( node_name )
|
|
{
|
|
self endon( "stop_hunt" );
|
|
self endon( "death" );
|
|
level.btr80 endon( "death" );
|
|
|
|
self SetLookAtEnt( level.btr80 );
|
|
|
|
goal_arr = getentarray( node_name, "targetname" );
|
|
old_goal = undefined;
|
|
|
|
while( true )
|
|
{
|
|
btr80_vect = VectorNormalize( level.btr80.origin - level.player.origin);
|
|
goal = undefined;
|
|
hi_dot = -1;
|
|
|
|
foreach ( ent in goal_arr )
|
|
{
|
|
goal_vect = VectorNormalize( ent.origin - level.player.origin);
|
|
dot = VectorDot( btr80_vect, goal_vect );
|
|
if ( dot > hi_dot )
|
|
{
|
|
hi_dot = dot;
|
|
goal = ent;
|
|
}
|
|
}
|
|
|
|
assert( isdefined( goal ) );
|
|
|
|
if ( !isdefined(old_goal) || goal != old_goal )
|
|
{
|
|
self Vehicle_SetSpeed( 50, 20, 20 );
|
|
// self maps\_vehicle::set_heli_move( "faster" );
|
|
self SetMaxPitchRoll( 25, 25 );
|
|
|
|
old_goal = goal;
|
|
flag_clear( "littlebird_at_goal" );
|
|
self thread maps\_vehicle::vehicle_paths( goal );
|
|
self thread littlebird_at_goal();
|
|
// self maps\_vehicle::set_heli_move( "fast" );
|
|
// self SetMaxPitchRoll( 25, 25 );
|
|
}
|
|
wait 0.1;
|
|
}
|
|
}
|
|
|
|
littlebird_at_goal()
|
|
{
|
|
level endon( "littlebird_at_goal" );
|
|
|
|
while( self.veh_speed > 15 )
|
|
{
|
|
self waittill_either( "goal", "near_goal" );
|
|
}
|
|
|
|
flag_set( "littlebird_at_goal" );
|
|
}
|
|
|
|
higround_littlebird_aligned( end_msg )
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( isdefined( end_msg ) )
|
|
level endon( end_msg );
|
|
|
|
while( true )
|
|
{
|
|
flag_wait( "littlebird_at_goal" );
|
|
btr80_origin = level.btr80.origin + ( 0, 0, 128 );
|
|
heli_origin = self.origin + ( 0, 0, -56 );
|
|
// only fov check
|
|
if ( player_looking_at( heli_origin, 0.85, true, self ) && player_looking_at( btr80_origin, 0.70, true, level.btr80 ) )
|
|
break;
|
|
wait .5;
|
|
|
|
if ( flag( "no_align" ) )
|
|
break;
|
|
}
|
|
}
|
|
|
|
higround_littlebird_failed_attack()
|
|
{
|
|
self endon( "death" );
|
|
|
|
center_ent = getent( "btr80_miss_target", "targetname" );
|
|
temp_miss_target_arr = getentarray( center_ent.target, "targetname" );
|
|
temp_miss_target_arr = array_index_by_script_index( temp_miss_target_arr );
|
|
|
|
miss_target_arr = [];
|
|
for ( i=0; i<temp_miss_target_arr.size; i++ )
|
|
{
|
|
ent = spawn( "script_origin", temp_miss_target_arr[i].origin );
|
|
miss_target_arr[i] = ent;
|
|
}
|
|
|
|
rotation = VectorToAngles( self.origin - level.btr80.origin );
|
|
goal_offset = level.btr80.origin - center_ent.origin;
|
|
|
|
foreach ( ent in miss_target_arr )
|
|
{
|
|
ent rotate_with_offset( rotation, center_ent.origin, goal_offset );
|
|
}
|
|
|
|
self fire_missile( miss_target_arr, 3 );
|
|
|
|
// delete target ents
|
|
delayThread( 2, ::array_call, miss_target_arr, ::delete );
|
|
}
|
|
|
|
/*******************************/
|
|
/*******************************/
|
|
/*** Vehicle ai anim threads ***/
|
|
/*******************************/
|
|
/*******************************/
|
|
|
|
uaz_unload_groups()
|
|
{
|
|
unload_groups = [];
|
|
unload_groups[ "driver" ] = [];
|
|
unload_groups[ "all" ] = [];
|
|
|
|
group = "driver";
|
|
unload_groups[ group ][ unload_groups[ group ].size ] = 0;
|
|
|
|
group = "all";
|
|
unload_groups[ group ][ unload_groups[ group ].size ] = 0;
|
|
unload_groups[ group ][ unload_groups[ group ].size ] = 1;
|
|
unload_groups[ group ][ unload_groups[ group ].size ] = 2;
|
|
|
|
unload_groups[ "default" ] = unload_groups[ "all" ];
|
|
|
|
return unload_groups;
|
|
}
|
|
|
|
// Copied from Jeepride, get rid of the once I don't need, when I know which that is
|
|
vehicle_aianimthread_setup()
|
|
{
|
|
level.vehicle_aianimthread[ "hide_attack_forward" ] = ::guy_hide_attack_forward;
|
|
level.vehicle_aianimcheck[ "hide_attack_forward" ] = ::guy_hide_attack_forward_check;
|
|
|
|
level.vehicle_aianimthread[ "hidetoback_attack" ] = ::guy_hidetoback_startingback;
|
|
level.vehicle_aianimcheck[ "hidetoback_attack" ] = ::guy_hidetoback_check;
|
|
|
|
level.vehicle_aianimthread[ "back_attack" ] = ::guy_back_attack;
|
|
level.vehicle_aianimcheck[ "back_attack" ] = ::guy_hidetoback_check;
|
|
|
|
level.vehicle_aianimthread[ "hide_attack_left" ] = ::guy_hide_attack_left;
|
|
level.vehicle_aianimcheck[ "hide_attack_left" ] = ::guy_hide_attack_left_check;
|
|
|
|
level.vehicle_aianimthread[ "hide_attack_left_standing" ] = ::guy_hide_attack_left_standing;
|
|
level.vehicle_aianimcheck[ "hide_attack_left_standing" ] = ::guy_hidetoback_check;
|
|
|
|
level.vehicle_aianimthread[ "hide_attack_back" ] = ::guy_hide_attack_back;
|
|
level.vehicle_aianimcheck[ "hide_attack_back" ] = ::guy_hide_attack_back_check;
|
|
|
|
level.vehicle_aianimthread[ "hide_starting_back" ] = ::guy_hide_starting_back;
|
|
level.vehicle_aianimcheck[ "hide_starting_back" ] = ::guy_hidetoback_check;
|
|
|
|
level.vehicle_aianimthread[ "hide_starting_left" ] = ::guy_hide_startingleft;
|
|
level.vehicle_aianimcheck[ "hide_starting_left" ] = ::guy_backtohide_check;
|
|
|
|
level.vehicle_aianimthread[ "backtohide" ] = ::guy_backtohide;
|
|
level.vehicle_aianimcheck[ "backtohide" ] = ::guy_backtohide_check;
|
|
|
|
level.vehicle_aianimthread[ "react" ] = ::guy_react;
|
|
level.vehicle_aianimcheck[ "react" ] = ::guy_react_check;
|
|
|
|
level.vehicle_aianimthread[ "free_attack" ] = ::guy_free_attack;
|
|
level.vehicle_aianimcheck[ "free_attack" ] = ::guy_free_attack_check;
|
|
|
|
// suburban
|
|
level.vehicle_aianimthread[ "hide_attack_right" ] = ::guy_hide_attack_right;
|
|
level.vehicle_aianimcheck[ "hide_attack_right" ] = ::guy_hide_attack_right_check;
|
|
|
|
|
|
}
|
|
|
|
guy_free_attack( guy, pos )
|
|
{
|
|
// free guy to attack.
|
|
guy endon( "newanim" );
|
|
guy endon( "death" );
|
|
guy notify( "animontag_thread" );
|
|
guy StopAnimScripted();
|
|
|
|
guy.noragdoll = true;
|
|
|
|
self thread guy_free_attack_cleanup( guy );
|
|
guy.deathanim = %boneyard_crouch_exposed_death_twist; // %covercrouch_death_3;
|
|
|
|
animtime = getanimlength( guy.deathanim );
|
|
guy thread guy_free_attack_death( animtime );
|
|
|
|
old_stance = "";
|
|
|
|
fov = level.cosine[ "20" ]; // only do standing when player is clearly infront of the pickup.
|
|
while( true )
|
|
{
|
|
if ( within_fov( self.origin, self.angles, level.player.origin, fov ) )
|
|
stance = "stand";
|
|
else
|
|
stance = "crouch";
|
|
|
|
if ( stance != old_stance )
|
|
{
|
|
old_stance = stance;
|
|
guy allowedstances( stance );
|
|
|
|
/#
|
|
guy thread debug_stance( stance );
|
|
#/
|
|
}
|
|
|
|
wait 1;
|
|
}
|
|
}
|
|
|
|
guy_free_attack_death( animtime )
|
|
{
|
|
self endon( "newanim" );
|
|
|
|
self waittill( "death" );
|
|
wait animtime * 0.6;
|
|
self delete();
|
|
}
|
|
|
|
guy_free_attack_cleanup( guy )
|
|
{
|
|
guy endon( "death" );
|
|
guy waittill( "animontag_thread" );
|
|
guy allowedstances( "stand", "crouch", "prone" );
|
|
}
|
|
|
|
guy_free_attack_check( guy, pos )
|
|
{
|
|
return ( self.vehicletype == "truck_physics" && pos >= 1 );
|
|
}
|
|
|
|
guy_hidetoback_check( guy, pos )
|
|
{
|
|
return isdefined( maps\_vehicle_aianim::anim_pos( self, pos ).hidetoback );
|
|
}
|
|
|
|
guy_hidetoback_startingback( guy, pos )
|
|
{
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
assert( isdefined( animpos.hidetoback ) );
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hidetoback );
|
|
thread guy_back_attack( guy, pos );
|
|
}
|
|
|
|
|
|
guy_back_attack( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
assert( isdefined( animpos.back_attack ) );
|
|
while ( 1 )
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.back_attack );
|
|
}
|
|
|
|
guy_backtohide_check( guy, pos )
|
|
{
|
|
return isdefined( maps\_vehicle_aianim::anim_pos( self, pos ).backtohide );
|
|
}
|
|
|
|
|
|
guy_hide_starting_back( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
|
|
assert( isdefined( animpos.backtohide ) );
|
|
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.backtohide );
|
|
thread guy_hide_attack_back( guy, pos );
|
|
}
|
|
|
|
guy_hide_startingleft( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
if ( !isdefined( animpos.backtohide ) )
|
|
return maps\_vehicle_aianim::guy_idle( guy, pos );
|
|
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.backtohide );
|
|
thread guy_hide_attack_left( guy, pos );
|
|
|
|
}
|
|
|
|
guy_backtohide( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
assert( isdefined( animpos.backtohide ) );
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.backtohide );
|
|
thread maps\_vehicle_aianim::guy_idle( guy, pos );
|
|
}
|
|
|
|
|
|
guy_react( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
assert( isdefined( animpos.react ) );
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.react );
|
|
thread maps\_vehicle_aianim::guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_react_check( guy, pos )
|
|
{
|
|
return isdefined( maps\_vehicle_aianim::anim_pos( self, pos ).react );
|
|
}
|
|
|
|
|
|
guy_hide_attack_back_check( guy, pos )
|
|
{
|
|
return isdefined( maps\_vehicle_aianim::anim_pos( self, pos ).hide_attack_back );
|
|
}
|
|
|
|
guy_hide_attack_back( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
assert( isdefined( animpos.hide_attack_back ) );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( isdefined( animpos.hide_attack_back_occurrence ) )
|
|
{
|
|
theanim = maps\_vehicle_aianim::randomoccurrance( guy, animpos.hide_attack_back_occurrence );
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_back[ theanim ] );
|
|
}
|
|
else
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_back );
|
|
}
|
|
}
|
|
|
|
guy_hide_attack_forward_check( guy, pos )
|
|
{
|
|
return isdefined( maps\_vehicle_aianim::anim_pos( self, pos ).hide_attack_forward );
|
|
}
|
|
|
|
guy_hide_attack_forward( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
|
|
assert( isdefined( animpos.hide_attack_forward ) );
|
|
|
|
while ( 1 )
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_forward );
|
|
}
|
|
|
|
guy_hide_attack_left_check( guy, pos )
|
|
{
|
|
return isdefined( maps\_vehicle_aianim::anim_pos( self, pos ).hide_attack_left );
|
|
}
|
|
|
|
guy_hide_attack_left( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
|
|
assert( isdefined( animpos.hide_attack_left ) );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( isdefined( animpos.hide_attack_left_occurrence ) )
|
|
{
|
|
theanim = maps\_vehicle_aianim::randomoccurrance( guy, animpos.hide_attack_left_occurrence );
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_left[ theanim ] );
|
|
}
|
|
else
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_left );
|
|
}
|
|
}
|
|
|
|
guy_hide_attack_left_standing( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
|
|
assert( isdefined( animpos.hide_attack_left_standing ) );
|
|
while ( 1 )
|
|
{
|
|
if ( isdefined( animpos.hide_attack_left_standing_occurrence ) )
|
|
{
|
|
theanim = maps\_vehicle_aianim::randomoccurrance( guy, animpos.hide_attack_left_standing_occurrence );
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_left_standing[ theanim ] );
|
|
}
|
|
else
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_left_standing );
|
|
}
|
|
}
|
|
|
|
guy_hide_attack_right_check( guy, pos )
|
|
{
|
|
return isdefined( maps\_vehicle_aianim::anim_pos( self, pos ).hide_attack_left );
|
|
}
|
|
|
|
guy_hide_attack_right( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, pos );
|
|
|
|
assert( isdefined( animpos.hide_attack_right ) );
|
|
|
|
while ( 1 )
|
|
{
|
|
maps\_vehicle_aianim::animontag( guy, animpos.sittag, animpos.hide_attack_left );
|
|
}
|
|
}
|
|
|
|
killer_bird_setup()
|
|
{
|
|
array_thread( getentarray( "player_stray_trigger" ,"targetname" ), ::killer_bird );
|
|
}
|
|
|
|
killer_bird()
|
|
{
|
|
level.player endon( "death" );
|
|
|
|
ent_arr = getentarray( self.target, "targetname" );
|
|
|
|
timer = 0;
|
|
while ( true )
|
|
{
|
|
self waittill( "trigger" );
|
|
if ( timer > 8 )
|
|
break;
|
|
timer++;
|
|
wait 1;
|
|
}
|
|
|
|
start_ent = ent_arr[0];
|
|
end_ent = ent_arr[1];
|
|
|
|
fov = level.cosine[ "60" ];
|
|
if ( within_fov( level.player.origin, level.player.angles, start_ent.origin, fov ) )
|
|
{
|
|
start_ent = ent_arr[1];
|
|
end_ent = ent_arr[0];
|
|
}
|
|
|
|
heli_spawner = getent( "killer_bird", "targetname" );
|
|
|
|
heli_spawner.origin = start_ent.origin;
|
|
heli_spawner.angles = start_ent.angles;
|
|
|
|
killer_bird = heli_spawner spawn_vehicle();
|
|
|
|
killer_bird SetVehGoalPos( end_ent.origin, true );
|
|
killer_bird Vehicle_SetSpeedImmediate( 100, 40, 30 );
|
|
killer_bird SetNearGoalNotifyDist( 4000 );
|
|
|
|
killer_bird thread killer_bird_goal( end_ent.origin );
|
|
|
|
killer_bird waittill_either( "goal", "near_goal" );
|
|
killer_bird SetLookAtEnt( level.player );
|
|
|
|
killer_bird waittill( "goal" );
|
|
|
|
fov = level.cosine[ "20" ];
|
|
while ( !within_fov( killer_bird.origin, killer_bird.angles, level.player.origin, fov ) )
|
|
wait 0.05;
|
|
|
|
killer_bird notify( "killing_player" );
|
|
|
|
target_arr = [];
|
|
target_arr[0] = level.player;
|
|
killer_bird thread fire_missile( target_arr, 5 );
|
|
|
|
wait 2;
|
|
level.player kill();
|
|
}
|
|
|
|
killer_bird_goal( goal_origin )
|
|
{
|
|
self endon( "killing_player" );
|
|
|
|
dist = distance2d( level.player.origin, goal_origin );
|
|
height = goal_origin[2];
|
|
|
|
while( true )
|
|
{
|
|
vector = AnglesToForward( level.player.angles );
|
|
new_origin = level.player.origin + (dist * vector) + ( 0, 0, height );
|
|
vector = VectorNormalize( new_origin - level.player.origin );
|
|
|
|
if ( SightTracePassed( level.player geteye(), new_origin, false, self ) )
|
|
self SetVehGoalPos( new_origin, true );
|
|
|
|
wait 2;
|
|
}
|
|
}
|
|
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
/***** MISC STUFF *****/
|
|
|
|
ramp_sunsample_over_time( samplesize, time )
|
|
{
|
|
current_samplesize = getdvarfloat( "sm_sunSampleSizeNear" );
|
|
range = current_samplesize - samplesize; // min sample size is 0.25
|
|
|
|
frames = time * 20;
|
|
for ( i = 0; i <= frames; i++ )
|
|
{
|
|
dif = i / frames;
|
|
dif = 1 - dif;
|
|
current_range = dif * range;
|
|
current_sample_size = samplesize + current_range;
|
|
setsaveddvar( "sm_sunSampleSizeNear", current_sample_size );
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
makesentient( team )
|
|
{
|
|
self MakeEntitySentient( team );
|
|
self waittill( "death" );
|
|
|
|
if ( isdefined( self ) )
|
|
self FreeEntitySentient();
|
|
}
|
|
|
|
waittill_damage( damage_types, attacker_ent, endon_death, timeout )
|
|
{
|
|
if ( isdefined( timeout ) )
|
|
mytimeOutEnt( timeout ) endon ( "timeout" );
|
|
|
|
if ( isstring( damage_types ) )
|
|
{
|
|
tmp = [];
|
|
tmp[ tolower( damage_types ) ] = true;
|
|
damage_types = tmp;
|
|
}
|
|
|
|
if ( isdefined( endon_death ) && endon_death )
|
|
self endon( "death" );
|
|
|
|
state = false;
|
|
while ( !state )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, dflags );
|
|
|
|
if ( isdefined( damage_types ) )
|
|
state = isdefined( damage_types[ tolower( type ) ] );
|
|
if ( state && isdefined( attacker_ent ) )
|
|
state = ( attacker_ent == attacker );
|
|
}
|
|
}
|
|
|
|
|
|
fade_out_level( fadeOutTime )
|
|
{
|
|
setSavedDvar( "compass", 0 );
|
|
setSavedDvar( "hud_showStance", 0 );
|
|
|
|
overlay = newHudElem();
|
|
overlay.x = 0;
|
|
overlay.y = 0;
|
|
overlay setshader( "black", 640, 480 );
|
|
overlay.alignX = "left";
|
|
overlay.alignY = "top";
|
|
overlay.horzAlign = "fullscreen";
|
|
overlay.vertAlign = "fullscreen";
|
|
|
|
overlay.alpha = 0;
|
|
overlay fadeOverTime( fadeOutTime );
|
|
overlay.alpha = 1;
|
|
|
|
wait fadeOutTime;
|
|
|
|
level.player freezeControls( true );
|
|
enablePlayerWeapons( false );
|
|
}
|
|
|
|
sp_objective_onentity( id, entity )
|
|
{
|
|
level endon( "release_objective" );
|
|
while ( true )
|
|
{
|
|
objective_position( id, entity.origin );
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
set_flag_on_notify( flag_str, msg_str )
|
|
{
|
|
self waittill( msg_str );
|
|
flag_set( flag_str );
|
|
}
|
|
|
|
rotate_with_offset( rotation, center_origin, goal_offset )
|
|
{
|
|
test_origin = TransformMove( level.btr80.origin, level.btr80.angles, center_origin, (0,0,0), self.origin, (0,0,0) );
|
|
|
|
offset = self.origin - center_origin;
|
|
right = anglestoright( rotation ) * -1;
|
|
forward = anglestoforward( rotation );
|
|
up = anglestoup( rotation );
|
|
new_offset = forward * offset[ 0 ] + right * offset[ 1 ] + up * offset[ 2 ];
|
|
self.origin = center_origin + new_offset + goal_offset;
|
|
}
|
|
|
|
rotate_vector( vector, rotation )
|
|
{
|
|
right = anglestoright( rotation ) * -1;
|
|
forward = anglestoforward( rotation );
|
|
up = anglestoup( rotation );
|
|
new_vector = forward * vector[ 0 ] + right * vector[ 1 ] + up * vector[ 2 ];
|
|
return new_vector;
|
|
}
|
|
|
|
flag_set_on_notify( flag_str, msg )
|
|
{
|
|
level endon( flag_str );
|
|
self waittill( msg );
|
|
flag_set( flag_str );
|
|
}
|
|
|
|
main_turret_init()
|
|
{
|
|
self ent_flag_init( "hold_fire" );
|
|
self ent_flag_init( "have_target" );
|
|
self ent_flag_init( "allow_stop" );
|
|
self ent_flag_set( "allow_stop" );
|
|
self.main_turret_enemies = [];
|
|
self.fire_on_ai = false;
|
|
}
|
|
|
|
main_turret_think( target_ai )
|
|
{
|
|
self endon( "death" );
|
|
|
|
old_target_ent = undefined;
|
|
|
|
while( true )
|
|
{
|
|
enemies = self.main_turret_enemies;
|
|
enemies = array_combine( enemies, self get_ai_enemies() );
|
|
|
|
target_ent = undefined;
|
|
foreach( ent in enemies )
|
|
{
|
|
if ( issentient( ent ) && !isalive( ent ) )
|
|
continue;
|
|
|
|
if ( isdefined( ent.ridingvehicle ) )
|
|
continue;
|
|
if ( self Vehicle_CanTurretTargetPoint( ent.origin + (0,0,20), 1, ent ) )
|
|
{
|
|
if ( isplayer( ent ) || ( IsAI( ent ) && !self.fire_on_ai ) )
|
|
{
|
|
ent_flag_set( "hold_fire" );
|
|
self maps\_vehicle::mgon();
|
|
}
|
|
else
|
|
{
|
|
ent_flag_clear( "hold_fire" );
|
|
self maps\_vehicle::mgoff();
|
|
}
|
|
target_ent = ent;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !isdefined( target_ent ) ) // never mind the old target stuff ->> || ( isdefined( old_target_ent ) && target_ent == old_target_ent ) )
|
|
{
|
|
wait 0.5;
|
|
continue;
|
|
}
|
|
|
|
old_target_ent = target_ent;
|
|
|
|
/#
|
|
println( "btr80 aquired a new target" );
|
|
#/
|
|
|
|
self ent_flag_set( "have_target" );
|
|
self main_turret_attack( target_ent, (0,0,20), false, 5 );
|
|
self ent_flag_clear( "have_target" );
|
|
}
|
|
}
|
|
|
|
main_turret_attack( target_ent, offset, no_vis, timeout )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self notify( "clear_turret_target" );
|
|
self endon( "clear_turret_target" );
|
|
|
|
if ( isalive( target_ent ) )
|
|
self thread main_turret_attack_target_death( target_ent );
|
|
|
|
if ( !isdefined( target_ent.fake_target ) )
|
|
self SetTurretTargetEnt( target_ent, offset );
|
|
else
|
|
self SetTurretTargetEnt( target_ent.fake_target, offset );
|
|
|
|
self waittill( "turret_on_target" );
|
|
|
|
if ( isdefined( timeout ) )
|
|
mytimeOutEnt( timeout ) endon ( "timeout" );
|
|
|
|
if ( !isdefined( no_vis ) )
|
|
self thread main_turret_target_lost( target_ent, offset );
|
|
|
|
starttime = gettime();
|
|
while( true )
|
|
{
|
|
self ent_flag_waitopen( "hold_fire" ); // lets me pause firing while still aiming at the target.
|
|
self FireWeapon();
|
|
wait 0.2;
|
|
}
|
|
}
|
|
|
|
main_turret_attack_target_death( target_ent )
|
|
{
|
|
self endon( "clear_turret_target" );
|
|
|
|
if ( isdefined( target_ent.fake_target ) )
|
|
target_ent.fake_target add_wait( ::waittill_msg, "death" );
|
|
target_ent add_wait( ::waittill_either, "death", "pain_death" );
|
|
do_wait_any();
|
|
|
|
wait randomfloat( 1 );
|
|
|
|
// remove the target if it's in the special enemy list unless it's alive.
|
|
if ( is_in_array( self.main_turret_enemies, target_ent ) && !isalive( target_ent ) )
|
|
self.main_turret_enemies = array_remove( self.main_turret_enemies, target_ent );
|
|
self notify( "clear_turret_target" );
|
|
|
|
}
|
|
|
|
main_turret_target_lost( target_ent, offset )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "clear_turret_target" );
|
|
|
|
timelimit = randomfloatrange( 1000, 2000 );
|
|
start_time = undefined;
|
|
|
|
while( true )
|
|
{
|
|
msg = self waittill_any_return( "turret_no_vis", "turret_on_vistarget" );
|
|
if ( msg == "turret_no_vis" )
|
|
{
|
|
if ( self Vehicle_CanTurretTargetPoint( target_ent.origin + offset, 1, target_ent ) )
|
|
continue;
|
|
if( !isdefined( start_time ) )
|
|
start_time = gettime();
|
|
if ( start_time + timelimit < gettime() )
|
|
break;
|
|
}
|
|
else
|
|
start_time = undefined;
|
|
}
|
|
|
|
self main_turret_aim_straight();
|
|
self notify( "clear_turret_target" );
|
|
}
|
|
|
|
main_turret_aim_straight()
|
|
{
|
|
self endon( "death" );
|
|
self ent_flag_set( "hold_fire" );
|
|
|
|
ent = spawn( "script_origin", (0,0,0) );
|
|
ent linkto( self, "tag_origin", (500,0,100), (0,0,0) );
|
|
self setTurretTargetEnt( ent );
|
|
|
|
self waittill_either( "turret_on_target", "death" );
|
|
if ( isalive( self ) )
|
|
self ClearTurretTarget();
|
|
ent delete();
|
|
self ent_flag_clear( "hold_fire" );
|
|
}
|
|
|
|
slowdown_on_target( speedmin, speedmax )
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( true )
|
|
{
|
|
self ent_flag_wait( "have_target" );
|
|
self ent_flag_wait( "allow_stop" );
|
|
|
|
speed = randomintrange( speedmin, speedmax );
|
|
|
|
|
|
self vehicle_setspeed( speed, 10 );
|
|
|
|
self ent_flag_waitopen_either( "have_target", "allow_stop" );
|
|
self resumespeed( 10 );
|
|
}
|
|
}
|
|
|
|
get_ai_enemies()
|
|
{
|
|
enemies = [];
|
|
team = "";
|
|
if ( isdefined( self.team ) )
|
|
team = self.team;
|
|
else if ( isdefined( self.script_team ) )
|
|
team = self.script_team;
|
|
else
|
|
assertmsg( "get_ai_enemies used on entity with no team" );
|
|
|
|
switch( team )
|
|
{
|
|
case "axis":
|
|
enemies = sortbydistance( getaiarray( "allies", "team3" ), self.origin );
|
|
case "team3":
|
|
enemies = sortbydistance( getaiarray( "allies", "axis" ), self.origin );
|
|
break;
|
|
case "allies":
|
|
enemies = sortbydistance( getaiarray( "team3", "axis" ), self.origin );
|
|
break;
|
|
}
|
|
|
|
if ( team != "allies" )
|
|
{
|
|
if ( enemies.size > 0 )
|
|
enemies = array_insert( enemies, level.player, 0 );
|
|
else
|
|
enemies[0] = level.player;
|
|
}
|
|
|
|
return enemies;
|
|
}
|
|
|
|
vehicle_set_health( health )
|
|
{
|
|
self.health = health + self.healthbuffer;
|
|
self.currenthealth = self.health;
|
|
}
|
|
|
|
flyby_magic_rocket( delete_time )
|
|
{
|
|
flag_wait( "flyby_rockets" );
|
|
while( !flag( "flyby_c130" ) )
|
|
{
|
|
wait 2;
|
|
self magic_rocket( delete_time );
|
|
wait randomfloatrange( 3, 4 );
|
|
}
|
|
}
|
|
|
|
magic_rocket( delete_time )
|
|
{
|
|
wait self.script_delay;
|
|
target = getent( self.target, "targetname" );
|
|
rocket = MagicBullet( "rpg_distant", self.origin, target.origin );
|
|
|
|
if ( isdefined( delete_time ) )
|
|
{
|
|
wait delete_time;
|
|
if( isdefined( rocket ) )
|
|
rocket delete();
|
|
}
|
|
}
|
|
|
|
set_flag_on_player_damage( flag_str )
|
|
{
|
|
level endon( flag_str );
|
|
self endon( "death" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "damage", amount, damager );
|
|
assert( isdefined(damager) );
|
|
if ( isplayer( damager ) )
|
|
break;
|
|
}
|
|
|
|
flag_set( flag_str );
|
|
}
|
|
|
|
random_delayed_kill( min_delay, max_delay )
|
|
{
|
|
enemy_team = [];
|
|
enemy_team[ "team3" ] = "axis";
|
|
enemy_team[ "axis" ] = "team3";
|
|
|
|
self endon( "death" );
|
|
wait randomfloatrange( min_delay, max_delay );
|
|
|
|
enemies = getaiarray( enemy_team[ self.team ] );
|
|
enemies = SortByDistance( enemies, self.origin );
|
|
guy = enemies[0];
|
|
|
|
if ( isdefined( guy ) )
|
|
self Kill( guy geteye(), guy );
|
|
else
|
|
self Kill( self geteye() );
|
|
}
|
|
|
|
move_and_delete( add_delay )
|
|
{
|
|
self endon( "death" );
|
|
self notify( "tobedeleted" );
|
|
|
|
self disable_ai_color();
|
|
|
|
// just kill them after a random time isntead of crazy move stuff.
|
|
random_delayed_kill( 3, 7 );
|
|
}
|
|
|
|
origin_is_behind( node_origin )
|
|
{
|
|
forward = anglestoforward( (0,0,0) ); // we are heading north in this level.
|
|
vector = VectorNormalize( node_origin - self.origin );
|
|
dot = VectorDot( forward, vector );
|
|
return ( dot < -0.2 );
|
|
}
|
|
|
|
switch_colors()
|
|
{
|
|
while( true )
|
|
{
|
|
flag_wait( "switch_color" );
|
|
|
|
guys = getaiarray( "axis", "team3" );
|
|
foreach ( guy in guys )
|
|
{
|
|
color = guy.script_forcecolor;
|
|
colorCodes = strtok( level.base_colors[ guy.team ], " " );
|
|
|
|
if ( isdefined( color ) )
|
|
{
|
|
// if not a base color switch to one that is.
|
|
if ( isdefined( color ) && !is_in_array( colorCodes, color ) || isdefined( guy.old_forcecolor ) )
|
|
{
|
|
guy set_force_color( colorCodes[0] );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if guy used to be a color turn old color to the base color
|
|
if ( isdefined( guy.old_forcecolor ) )
|
|
guy.old_forcecolor = colorCodes[0];
|
|
}
|
|
}
|
|
flag_clear( "switch_color" );
|
|
}
|
|
}
|
|
|
|
waittill_stack_add( msg, ent )
|
|
{
|
|
/*
|
|
Used to wait on multiple messages and/or entities and get the message and triggering ent and variables passed through the notify
|
|
|
|
msg: message to wait for.
|
|
ent: optional ent to waittill on instead of self.
|
|
|
|
level waittill_stack_add( "damage", ai1 ); // waits for damage on ai1
|
|
level waittill_stack_add( "damage", ai2 ); // waits for damage on ai2
|
|
level waittill( "waittill_stack", msg, ent ); // returns messge and ai1 or ai2 on damage to either
|
|
damager = level.waittill_stack.b; // gets the second variable passes by the notify.
|
|
level waittill_stack_clear(); // cleans up any variables.
|
|
*/
|
|
|
|
if ( !isdefined( self.waittill_stack ) )
|
|
self.waittill_stack = spawnstruct();
|
|
|
|
self thread waittill_stack_thread( msg, ent);
|
|
}
|
|
|
|
waittill_stack_clear()
|
|
{
|
|
self notify( "waittill_stack" );
|
|
self.waittill_stack = undefined;
|
|
}
|
|
|
|
waittill_stack_thread( msg, ent )
|
|
{
|
|
self endon( "waittill_stack" );
|
|
self endon( "death" );
|
|
|
|
if ( !isdefined( ent ) )
|
|
ent = self;
|
|
|
|
ent waittill( msg, a, b, c, d, e, f );
|
|
|
|
if ( isdefined( a ) )
|
|
self.waittill_stack.a = a;
|
|
if ( isdefined( b ) )
|
|
self.waittill_stack.b = b;
|
|
if ( isdefined( c ) )
|
|
self.waittill_stack.c = c;
|
|
if ( isdefined( d ) )
|
|
self.waittill_stack.d = d;
|
|
if ( isdefined( e ) )
|
|
self.waittill_stack.e = e;
|
|
if ( isdefined( f ) )
|
|
self.waittill_stack.f = f;
|
|
|
|
self notify( "waittill_stack", msg, ent );
|
|
}
|
|
|
|
fire_missile_setup()
|
|
{
|
|
run_thread_on_noteworthy( "fire_missile", maps\_attack_heli::boneyard_style_heli_missile_attack );
|
|
}
|
|
|
|
fire_missile( target_arr, number )
|
|
{
|
|
tags = [];
|
|
tags[0] = "tag_missile_right";
|
|
tags[1] = "tag_missile_left";
|
|
|
|
self SetVehWeapon( "littlebird_FFAR" );
|
|
|
|
for ( i=0; i<number; i++ )
|
|
{
|
|
assertex( isdefined( target_arr[ i % target_arr.size ] ), "fire_missile requires indexes to start at 0 and not have any gaps." );
|
|
|
|
target_ent = target_arr[ i % target_arr.size ];
|
|
self SetTurretTargetEnt( target_ent );
|
|
missile = self FireWeapon( tags[ i % tags.size ], target_ent );
|
|
missile delaycall( 1, ::Missile_ClearTarget );
|
|
|
|
if ( isdefined( target_ent.script_delay ) )
|
|
wait target_ent.script_delay;
|
|
else
|
|
wait randomfloatrange( 0.2, 0.3 );
|
|
}
|
|
}
|
|
|
|
move_spawn_and_go( path_ent )
|
|
{
|
|
self.origin = path_ent.origin;
|
|
if ( isdefined( path_ent.angles ) )
|
|
self.angles = path_ent.angles;
|
|
|
|
// changes targetname of ai so that they to can spawn
|
|
other_ents = getentarray( self.target, "targetname" );
|
|
foreach( ent in other_ents )
|
|
{
|
|
if ( isspawner( ent ) )
|
|
ent.targetname = path_ent.targetname;
|
|
}
|
|
|
|
self.target = path_ent.targetname;
|
|
|
|
vehicle = self thread maps\_vehicle::spawn_vehicle_and_gopath();
|
|
|
|
/#
|
|
// debug shit
|
|
if ( vehicle maps\_vehicle::ishelicopter() )
|
|
level thread mark_heli_path( path_ent );
|
|
#/
|
|
|
|
return vehicle;
|
|
}
|
|
|
|
new_vehicle_path( path_start )
|
|
{
|
|
if ( !self maps\_vehicle::ishelicopter() )
|
|
self StartPath( path_start );
|
|
self thread maps\_vehicle::vehicle_paths( path_start );
|
|
}
|
|
|
|
turret_fire_control( mg_guy )
|
|
{
|
|
mg_guy endon( "death" );
|
|
|
|
while( true )
|
|
{
|
|
mg_guy.ignoreall = !flag( "minigun_fire" );
|
|
|
|
if ( flag( "minigun_fire" ) )
|
|
flag_waitopen( "minigun_fire" );
|
|
else
|
|
flag_wait( "minigun_fire" );
|
|
}
|
|
}
|
|
|
|
start_at( start_point )
|
|
{
|
|
if ( self == level.player )
|
|
{
|
|
self setOrigin( start_point.origin );
|
|
self setPlayerAngles( start_point.angles );
|
|
}
|
|
else
|
|
{
|
|
self.origin = start_point.origin;
|
|
self.angles = start_point.angles;
|
|
}
|
|
}
|
|
|
|
delete_not_touching_setup()
|
|
{
|
|
array_thread( getentarray( "delete_not_touching", "targetname" ), ::delete_not_touching );
|
|
}
|
|
|
|
delete_not_touching()
|
|
{
|
|
self waittill( "trigger" );
|
|
while ( level.player istouching( self ) )
|
|
wait 0.5;
|
|
self trigger_off();
|
|
}
|
|
|
|
|
|
/* temp */
|
|
|
|
drawpath()
|
|
{
|
|
self endon( "death" );
|
|
|
|
/#
|
|
if ( getdvar( "debug_heli" ) == "" )
|
|
return;
|
|
#/
|
|
|
|
while( true )
|
|
{
|
|
if ( self Vehicle_GetSpeed() < 5 )
|
|
{
|
|
wait 0.1;
|
|
continue;
|
|
}
|
|
old_origin = self.origin;
|
|
wait 0.1;
|
|
|
|
Line( old_origin, self.origin, ( 1, 1, 1), 1, false, 500 );
|
|
|
|
vector = AnglesToForward( self.angles );
|
|
origin1 = self.origin + ( vector * 100 );
|
|
vector = AnglesToRight( self.angles );
|
|
origin2 = self.origin - ( vector * 30 );
|
|
origin3 = self.origin + ( vector * 30 );
|
|
|
|
draw_tri( origin1, origin2, origin3 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
mark_heli_path( ent )
|
|
{
|
|
/#
|
|
if ( getdvar( "debug_heli" ) == "" )
|
|
return;
|
|
|
|
old_ent = ent;
|
|
while( true )
|
|
{
|
|
draw_cross( ent.origin );
|
|
if ( !isdefined( ent.target ) )
|
|
break;
|
|
ent = getent( ent.target, "targetname" );
|
|
Line( ent.origin, old_ent.origin, ( 0.7,0,0 ), 1, false, 500 );
|
|
old_ent = ent;
|
|
}
|
|
#/
|
|
}
|
|
|
|
draw_tri( origin1, origin2, origin3 )
|
|
{
|
|
Line( origin1, origin2, ( 0,0,0 ), 1, false, 500 );
|
|
Line( origin2, origin3, ( 0,0,0 ), 1, false, 500 );
|
|
Line( origin3, origin1, ( 0,0,0 ), 1, false, 500 );
|
|
}
|
|
|
|
draw_cross( origin, col )
|
|
{
|
|
if ( !isdefined( col ) )
|
|
{
|
|
Line( origin + ( 10,0,0 ), origin + ( -10,0,0 ), ( .5, .5, .5 ), 1, false, 500 );
|
|
Line( origin + ( 0,10,10 ), origin + ( 0,-10,0 ), ( .5, .5, .5 ), 1, false, 500 );
|
|
Line( origin, origin + ( 0,0,100 ), ( 1, 1, 1 ), 1, false, 50 );
|
|
}
|
|
else
|
|
{
|
|
Line( origin + ( 10,0,0 ), origin + ( -10,0,0 ), col, 1, false, 500 );
|
|
Line( origin + ( 0,10,10 ), origin + ( 0,-10,0 ), col, 1, false, 500 );
|
|
Line( origin, origin + ( 0,0,100 ), col, 1, false, 500 );
|
|
}
|
|
}
|
|
|
|
cc( text )
|
|
{
|
|
if ( !isdefined( level.cc ) )
|
|
{
|
|
level.cc = spawnstruct();
|
|
level.cc.line = 0;
|
|
}
|
|
|
|
subtitle = newHudElem();
|
|
subtitle.x = 0;
|
|
subtitle.y = -80 + ( level.cc.line * 14 );
|
|
subtitle settext( text );
|
|
subtitle.fontScale = 1.46;
|
|
subtitle.alignX = "center";
|
|
subtitle.alignY = "middle";
|
|
subtitle.horzAlign = "center";
|
|
subtitle.vertAlign = "bottom";
|
|
subtitle.sort = 1;
|
|
subtitle.dbtext = text;
|
|
|
|
my_line = level.cc.line;
|
|
subtitle thread cc_move( my_line );
|
|
|
|
level.cc.line++;
|
|
waittime = ( ( text.size + 1 )/ 50) + 1;
|
|
wait waittime;
|
|
|
|
subtitle FadeOverTime( .5 );
|
|
subtitle.alpha = 0;
|
|
level.cc.line--;
|
|
wait .5;
|
|
|
|
level.cc notify( "move", my_line );
|
|
subtitle notify( "destoyed" );
|
|
subtitle destroy();
|
|
|
|
}
|
|
|
|
cc_move( my_line )
|
|
{
|
|
self endon( "destoyed" );
|
|
while( true )
|
|
{
|
|
level.cc waittill( "move", line );
|
|
if ( line > my_line || my_line == 0 )
|
|
continue;
|
|
self MoveOverTime(.5);
|
|
self.y = self.y - 14;
|
|
my_line--;
|
|
}
|
|
}
|
|
|
|
draw_vehicle_paths()
|
|
{
|
|
vnode_arr = GetAllVehicleNodes();
|
|
start_vnodes = [];
|
|
foreach( vnode in vnode_arr )
|
|
{
|
|
if ( isdefined( vnode.spawnflags ) && vnode.spawnflags & 1 )
|
|
start_vnodes[ start_vnodes.size ] = vnode;
|
|
}
|
|
|
|
foreach( vnode in start_vnodes )
|
|
{
|
|
level draw_vehicle_paths_traverse( vnode );
|
|
}
|
|
}
|
|
|
|
draw_vehicle_paths_traverse( vnode )
|
|
{
|
|
random_color = ( randomfloatrange( 0, 1 ), randomfloatrange( 0, 1 ), randomfloatrange( 0, 1 ) );
|
|
|
|
while ( isdefined( vnode.target ) )
|
|
{
|
|
start = vnode.origin;
|
|
vnode = getvehiclenode( vnode.target, "targetname" );
|
|
end = vnode.origin;
|
|
Line( start, end, random_color, 1, false, 1000 );
|
|
}
|
|
}
|
|
|
|
debug_stance( stance )
|
|
{
|
|
self notify( "new_debug" );
|
|
self endon( "new_debug" );
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
wait 0.05;
|
|
if ( getdvarint( "debug_stance", 0 ) )
|
|
{
|
|
if ( stance == "stand" )
|
|
Print3d( self.origin + (0,0,80), "S" );
|
|
else
|
|
Print3d( self.origin + (0,0,80), "C" );
|
|
}
|
|
}
|
|
}
|
|
|
|
boneyard_gameskill_ride_settings()
|
|
{
|
|
|
|
// RIGHT NOW ONLY .25 AND .75 ARE USED for easy and normal
|
|
|
|
level.difficultySettings[ "threatbias" ][ "easy" ] = 0;
|
|
level.difficultySettings[ "threatbias" ][ "normal" ] = 0;
|
|
level.difficultySettings[ "threatbias" ][ "hardened" ] = 0;
|
|
level.difficultySettings[ "threatbias" ][ "veteran" ] = 0;
|
|
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "easy" ] = 1.0;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "normal" ] = 1.0;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "hardened" ] = 1.0;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "veteran" ] = 1.0;
|
|
|
|
// lower numbers = higher accuracy for AI at a distance
|
|
level.difficultySettings[ "accuracyDistScale" ][ "easy" ] = 1.0;
|
|
level.difficultySettings[ "accuracyDistScale" ][ "normal" ] = 1.0;
|
|
level.difficultySettings[ "accuracyDistScale" ][ "hardened" ] = 1.0;
|
|
level.difficultySettings[ "accuracyDistScale" ][ "veteran" ] = 1.0; // too many other things make it more difficult
|
|
|
|
|
|
level.difficultySettings[ "pain_test" ][ "easy" ] = maps\_gameskill::always_pain;
|
|
level.difficultySettings[ "pain_test" ][ "normal" ] = maps\_gameskill::always_pain;
|
|
level.difficultySettings[ "pain_test" ][ "hardened" ] = maps\_gameskill::always_pain;
|
|
level.difficultySettings[ "pain_test" ][ "veteran" ] = maps\_gameskill::always_pain;
|
|
|
|
|
|
// Death Invulnerable Time controls how long the player is death-proof after going into red flashing
|
|
// This protection resets after the player recovers full health.
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "easy" ] = 800;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "normal" ] = 600;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "hardened" ] = 400;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "veteran" ] = 200;
|
|
|
|
|
|
// level.invulTime_preShield: time player is invulnerable when hit before their health is low enough for a red overlay( should be very short )
|
|
level.difficultySettings[ "invulTime_preShield" ][ "easy" ] = 0.0;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "normal" ] = 0.0;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "hardened" ] = 0.0;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "veteran" ] = 0.0;
|
|
|
|
// level.invulTime_onShield: time player is invulnerable when hit the first time they get a red health overlay( should be reasonably long )
|
|
// should not be more than or too much lower than player_deathInvulnerableTime
|
|
level.difficultySettings[ "invulTime_onShield" ][ "easy" ] = 0.5;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "normal" ] = 0.5;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "hardened" ] = 0.5;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "veteran" ] = 0.5;
|
|
|
|
// level.invulTime_postShield: time player is invulnerable when hit after the red health overlay is already up( should be short )
|
|
level.difficultySettings[ "invulTime_postShield" ][ "easy" ] = 0.3;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "normal" ] = 0.3;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "hardened" ] = 0.3;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "veteran" ] = 0.3;
|
|
|
|
// level.playerHealth_RegularRegenDelay
|
|
// The delay before you regen health after getting hurt
|
|
level.difficultySettings[ "playerHealth_RegularRegenDelay" ][ "easy" ] = 500;
|
|
level.difficultySettings[ "playerHealth_RegularRegenDelay" ][ "normal" ] = 500;
|
|
level.difficultySettings[ "playerHealth_RegularRegenDelay" ][ "hardened" ] = 500;
|
|
level.difficultySettings[ "playerHealth_RegularRegenDelay" ][ "veteran" ] = 500;
|
|
|
|
|
|
// level.worthyDamageRatio( player must recieve this much damage as a fraction of maxhealth to get invulTime_PREshield. )
|
|
level.difficultySettings[ "worthyDamageRatio" ][ "easy" ] = 0.2;
|
|
level.difficultySettings[ "worthyDamageRatio" ][ "normal" ] = 0.2;
|
|
level.difficultySettings[ "worthyDamageRatio" ][ "hardened" ] = 0.2;
|
|
level.difficultySettings[ "worthyDamageRatio" ][ "veteran" ] = 0.2;
|
|
|
|
|
|
// self.gs.regenRate
|
|
// the rate you regen health once it starts to regen
|
|
level.difficultySettings[ "health_regenRate" ][ "easy" ] = 0.01;
|
|
level.difficultySettings[ "health_regenRate" ][ "normal" ] = 0.008;
|
|
level.difficultySettings[ "health_regenRate" ][ "hardened" ] = 0.008;
|
|
level.difficultySettings[ "health_regenRate" ][ "veteran" ] = 0.008;
|
|
|
|
|
|
|
|
// level.playerDifficultyHealth
|
|
// the amount of health you have in this difficulty
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "easy" ] = 1000;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "normal" ] = 800;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "hardened" ] = 600;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "veteran" ] = 500;
|
|
|
|
// If you go to red flashing, the amount of time before your health regens
|
|
level.difficultySettings[ "longRegenTime" ][ "easy" ] = 1000;
|
|
level.difficultySettings[ "longRegenTime" ][ "normal" ] = 1000;
|
|
level.difficultySettings[ "longRegenTime" ][ "hardened" ] = 1000;
|
|
level.difficultySettings[ "longRegenTime" ][ "veteran" ] = 1000;
|
|
|
|
// level.healthOverlayCutoff
|
|
level.difficultySettings[ "healthOverlayCutoff" ][ "easy" ] = 0.02;
|
|
level.difficultySettings[ "healthOverlayCutoff" ][ "normal" ] = 0.02;
|
|
level.difficultySettings[ "healthOverlayCutoff" ][ "hardened" ] = 0.02;
|
|
level.difficultySettings[ "healthOverlayCutoff" ][ "veteran" ] = 0.02;
|
|
} |