1677 lines
35 KiB
Plaintext
1677 lines
35 KiB
Plaintext
|
#include maps\_utility;
|
|||
|
#include common_scripts\utility;
|
|||
|
#include maps\_hud_util;
|
|||
|
#include maps\_specialops;
|
|||
|
#include maps\_specialops_code;
|
|||
|
#include maps\so_chopper_invasion;
|
|||
|
|
|||
|
init_dialogue()
|
|||
|
{
|
|||
|
// "Gunslinger One <20>to ground, primary LZ is too hot! We've got ten-plus hostiles in our immediate AO and cannot remain on the ground, over!"
|
|||
|
level.scr_radio[ "lift_off" ] = "so_chop_inv_hp1_lz2hot";
|
|||
|
|
|||
|
// "Ground forces, we're going to link up at the secondary extraction point. Head for the roof of Nate's Sports Bar, to the East."
|
|||
|
level.scr_radio[ "objective" ] = "so_chop_inv_hp1_enghost";
|
|||
|
|
|||
|
// "Gunner, you are cleared to engage hostiles. Watch out for friendlies on the ground, over."
|
|||
|
level.scr_radio[ "objective2" ] = "so_chop_inv_hp1_gunner";
|
|||
|
|
|||
|
// "Gunslinger-One to ground forces, find some cover! We are tracking a platoon-sized group of hostiles behind that barricade at the end of the street, over!"
|
|||
|
level.scr_radio[ "drive_by" ] = "so_chop_inv_hp1_findcover";
|
|||
|
|
|||
|
// "Danger close ground forces, we're comin' in hot! Gunner, you are cleared to engage hostiles by the barricade."
|
|||
|
level.scr_radio[ "start_drive_by" ] = "so_chop_inv_hp1_by_barricade";
|
|||
|
|
|||
|
// "Good effect on target, Gunner - whoa, hang on!"
|
|||
|
level.scr_radio[ "evade_rpgs" ] = "so_chop_inv_hp1_goodeffect";
|
|||
|
|
|||
|
// "Ground forces be advised, we've got RPGs in the area, over.<2E> We<57>re coming back around for a strafing run here in a second."
|
|||
|
level.scr_radio[ "evade_extra" ] = "so_chop_inv_hp1_rpgs";
|
|||
|
|
|||
|
// "Gunslinger-One to ground, we are starting our strafing run, over. Gunner, light 'em up."
|
|||
|
level.scr_radio[ "drive_by_payback" ] = "so_chop_inv_hp1_lightemup";
|
|||
|
|
|||
|
// "Guns guns guns!"
|
|||
|
level.scr_radio[ "drive_by_guns_guns_guns" ] = "so_chop_inv_hp1_guns";
|
|||
|
|
|||
|
// "Gunslinger-One to ground, repositioning to your location, over."
|
|||
|
level.scr_radio[ "back_to_squad" ] = "so_chop_inv_hp1_reposition";
|
|||
|
|
|||
|
// "Gunslinger-One to ground, be advised, we are tracking a convoy of enemy trucks movin' in from the southeast, recommend you let us handle those, over."
|
|||
|
level.scr_radio[ "convoy" ] = "so_chop_inv_hp1_ba_trucks";
|
|||
|
|
|||
|
// "Gunslinger-One to ground, we see you! Maintain your location on the roof, we're en route!"
|
|||
|
level.scr_radio[ "on_the_roof" ] = "so_chop_inv_hp1_weseeyou";
|
|||
|
|
|||
|
// "Ground forces, let's go let's go! We are at the roof and ready to link up for extraction!"
|
|||
|
level.scr_radio[ "end_reminder_1" ] = "so_chop_inv_hp1_letsgo";
|
|||
|
|
|||
|
// "Ground forces, pick up the pace, we're sitting ducks up here!"
|
|||
|
level.scr_radio[ "end_reminder_2" ] = "so_chop_inv_hp1_pace";
|
|||
|
|
|||
|
// "Jump! You'll make it!"
|
|||
|
level.scr_radio[ "jump" ] = "so_chop_inv_hp1_jump";
|
|||
|
|
|||
|
// "Ground forces, link up on the roof of Nate's Sports Bar!"
|
|||
|
level.scr_radio[ "objective_reminder_1" ] = "so_chop_inv_hp1_linkup";
|
|||
|
|
|||
|
// "Gunslinger One to ground, meet us up on the roof of Nate's Sports Bar!"
|
|||
|
level.scr_radio[ "objective_reminder_2" ] = "so_chop_inv_hp1_meetus";
|
|||
|
|
|||
|
// "Ground forces, get to the roof of Nate's Sports Bar!"
|
|||
|
level.scr_radio[ "objective_reminder_3" ] = "so_chop_inv_hp1_gettoroof";
|
|||
|
|
|||
|
// "Gunner, watch your fire - that's a friendly!"
|
|||
|
level.scr_radio[ "friendlyfire_1" ] = "so_chop_inv_hp1_friendly_01";
|
|||
|
|
|||
|
// "Gunner, that is friendly fire! Check your aim!"
|
|||
|
level.scr_radio[ "friendlyfire_2" ] = "so_chop_inv_hp1_friendly_02";
|
|||
|
|
|||
|
// "That's a friendly down there, Gunner! Focus up!"
|
|||
|
level.scr_radio[ "friendlyfire_3" ] = "so_chop_inv_hp1_friendly_03";
|
|||
|
}
|
|||
|
|
|||
|
so_chopper_invasion_fx()
|
|||
|
{
|
|||
|
level._effect[ "chopper_minigun_shells" ] = LoadFX( "shellejects/20mm_cargoship" );
|
|||
|
}
|
|||
|
|
|||
|
chopper_minigun_shells()
|
|||
|
{
|
|||
|
fx = getfx( "chopper_minigun_shells" );
|
|||
|
tag = "tag_turret";
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
if ( self AttackButtonPressed() )
|
|||
|
{
|
|||
|
PlayFXOnTag( fx, level.chopper, tag );
|
|||
|
}
|
|||
|
|
|||
|
wait( 0.05 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
so_chopper_invasion_enemy_setup()
|
|||
|
{
|
|||
|
level.enemies = [];
|
|||
|
|
|||
|
allspawners = GetSpawnerTeamArray( "axis" );
|
|||
|
array_thread( allspawners, ::add_spawn_function, ::so_chopper_invasion_enemy_spawnfunc );
|
|||
|
}
|
|||
|
|
|||
|
so_chopper_invasion_enemy_spawnfunc()
|
|||
|
{
|
|||
|
self pathrandompercent_set( 800 );
|
|||
|
|
|||
|
if ( IsSubStr( self.code_classname, "juggernaut" ) )
|
|||
|
{
|
|||
|
self thread so_chopper_invasion_juggernaut_init();
|
|||
|
}
|
|||
|
|
|||
|
level.activeEnemies[ level.enemies.size ] = self;
|
|||
|
self thread so_chopper_invasion_enemy_deathcleanup();
|
|||
|
}
|
|||
|
|
|||
|
so_chopper_invasion_juggernaut_init()
|
|||
|
{
|
|||
|
self SetThreatBiasGroup( "juggernauts" );
|
|||
|
//self thread juggernaut_hud_box();
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
juggernaut_hud_box()
|
|||
|
{
|
|||
|
offset = ( 0, 0, 32 );
|
|||
|
Target_Set( self, offset );
|
|||
|
|
|||
|
self waittill( "death" );
|
|||
|
Target_Remove( self );
|
|||
|
}
|
|||
|
*/
|
|||
|
|
|||
|
so_chopper_invasion_enemy_deathcleanup()
|
|||
|
{
|
|||
|
self waittill( "death" );
|
|||
|
level.activeEnemies = array_remove( level.enemies, self );
|
|||
|
}
|
|||
|
|
|||
|
// TODO maybe genericize this
|
|||
|
get_targeted_line_array( start )
|
|||
|
{
|
|||
|
arr = [];
|
|||
|
arr[ 0 ] = start;
|
|||
|
point = start;
|
|||
|
|
|||
|
while ( IsDefined( point.target ) )
|
|||
|
{
|
|||
|
nextpoint = getstruct( point.target, "targetname" );
|
|||
|
|
|||
|
if ( !IsDefined( nextpoint ) )
|
|||
|
{
|
|||
|
nextpoint = GetEnt( point.target, "targetname" );
|
|||
|
}
|
|||
|
|
|||
|
if ( !IsDefined( nextpoint ) )
|
|||
|
{
|
|||
|
nextpoint = GetNode( point.target, "targetname" );
|
|||
|
}
|
|||
|
|
|||
|
if ( !IsDefined( nextpoint ) )
|
|||
|
{
|
|||
|
nextpoint = GetVehicleNode( point.target, "targetname" );
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( nextpoint ) )
|
|||
|
{
|
|||
|
arr[ arr.size ] = nextpoint;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
point = nextpoint;
|
|||
|
}
|
|||
|
|
|||
|
return arr;
|
|||
|
}
|
|||
|
|
|||
|
milliseconds( seconds )
|
|||
|
{
|
|||
|
return seconds * 1000;
|
|||
|
}
|
|||
|
|
|||
|
seconds( milliseconds )
|
|||
|
{
|
|||
|
return milliseconds / 1000;
|
|||
|
}
|
|||
|
|
|||
|
// AI Section ---------------------------------------------
|
|||
|
ai_post_spawn()
|
|||
|
{
|
|||
|
self endon( "death" );
|
|||
|
|
|||
|
if ( !IsDefined( self.target ) )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
points = getstructarray( self.target, "targetname" );
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
if ( points.size == 0 )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
point = points[ 0 ];
|
|||
|
if ( points.size > 1 )
|
|||
|
{
|
|||
|
point = points[ RandomInt( points.size ) ];
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( point.radius ) )
|
|||
|
{
|
|||
|
self.goalradius = point.radius;
|
|||
|
}
|
|||
|
|
|||
|
self SetGoalPos( point.origin );
|
|||
|
self waittill( "goal" );
|
|||
|
|
|||
|
if ( IsDefined( point.script_noteworthy ) )
|
|||
|
{
|
|||
|
if ( point.script_noteworthy == "so_shoot_rpg" )
|
|||
|
{
|
|||
|
self.a.rockets = 1;
|
|||
|
// target = GetEnt( "so_rpg_target", "targetname" );
|
|||
|
self SetEntityTarget( level.chopper );
|
|||
|
|
|||
|
self.ignoreall = false;
|
|||
|
level notify( "so_rpgs_shot" );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( point.script_flag_wait ) )
|
|||
|
{
|
|||
|
flag_wait( point.script_flag_wait );
|
|||
|
|
|||
|
if ( IsDefined( point.script_noteworthy ) )
|
|||
|
{
|
|||
|
if ( point.script_noteworthy == "so_shoot_rpg" )
|
|||
|
{
|
|||
|
self SetCanDamage( true );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
point script_delay();
|
|||
|
|
|||
|
if ( IsDefined( point.script_noteworthy ) )
|
|||
|
{
|
|||
|
if ( point.script_noteworthy == "so_shoot_rpg" )
|
|||
|
{
|
|||
|
self ClearEntityTarget();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( !IsDefined( point.target ) )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
points = getstructarray( point.target, "targetname" );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
self.goalradius = level.default_goalradius;
|
|||
|
}
|
|||
|
|
|||
|
so_rpg_post_spawn()
|
|||
|
{
|
|||
|
self SetCanDamage( false );
|
|||
|
}
|
|||
|
|
|||
|
friendlyfire()
|
|||
|
{
|
|||
|
next_time = 0;
|
|||
|
duration = 3000;
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
level.groundplayer waittill( "damage", dmg, attacker );
|
|||
|
|
|||
|
if ( attacker == level.chopper )
|
|||
|
{
|
|||
|
if ( GetTime() > next_time )
|
|||
|
{
|
|||
|
next_time = GetTime() + duration;
|
|||
|
chopper_dialog( "friendlyfire" );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// ---------------------
|
|||
|
// --- CHOPPER STUFF ---
|
|||
|
// ---------------------
|
|||
|
#using_animtree( "vehicles" );
|
|||
|
build_chopper()
|
|||
|
{
|
|||
|
maps\_blackhawk_minigun::main( "vehicle_blackhawk_minigun_hero", "blackhawk_minigun_so" );
|
|||
|
}
|
|||
|
|
|||
|
chopper_defaults()
|
|||
|
{
|
|||
|
self ClearGoalYaw();
|
|||
|
self.speed_setting = "none";
|
|||
|
self chopper_default_speed();
|
|||
|
self SetHoverParams( 50, 10, 3 );
|
|||
|
self chopper_default_pitch_roll();
|
|||
|
self SetNearGoalNotifyDist( 200 );
|
|||
|
}
|
|||
|
|
|||
|
chopper_default_pitch_roll()
|
|||
|
{
|
|||
|
self SetMaxPitchRoll( 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
chopper_default_speed()
|
|||
|
{
|
|||
|
if ( self.speed_setting == "default" )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
self.speed_setting = "default";
|
|||
|
// self Vehicle_SetSpeed( 15, 7.5 );
|
|||
|
// self Vehicle_SetSpeed( 20, 10, 10 );
|
|||
|
self Vehicle_SetSpeed( 20, 20, 20 );
|
|||
|
}
|
|||
|
|
|||
|
chopper_slow_speed()
|
|||
|
{
|
|||
|
if ( self.speed_setting == "slow" && self.speed_setting != "force_default" )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
self.speed_setting = "slow";
|
|||
|
self Vehicle_SetSpeed( 10, 20, 20 );
|
|||
|
}
|
|||
|
|
|||
|
chopper_high_speed()
|
|||
|
{
|
|||
|
if ( self.speed_setting == "high" )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
self.speed_setting = "high";
|
|||
|
self Vehicle_SetSpeed( 30, 20, 20 );
|
|||
|
}
|
|||
|
|
|||
|
// Mouns the player to the chopper
|
|||
|
chopper_playermount( player )
|
|||
|
{
|
|||
|
player AllowCrouch( false );
|
|||
|
player AllowProne( false );
|
|||
|
player AllowSprint( false );
|
|||
|
player AllowJump( false );
|
|||
|
|
|||
|
self maps\_blackhawk_minigun::player_mount_blackhawk_gun( true, player, false );
|
|||
|
self chopper_defaults();
|
|||
|
}
|
|||
|
|
|||
|
chopper_dialog( alias )
|
|||
|
{
|
|||
|
if ( flag( "special_op_terminated" ) )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
aliases = [];
|
|||
|
switch( alias )
|
|||
|
{
|
|||
|
case "end_reminder":
|
|||
|
aliases[ 0 ] = "end_reminder_1";
|
|||
|
aliases[ 1 ] = "end_reminder_2";
|
|||
|
break;
|
|||
|
|
|||
|
case "objective_reminder":
|
|||
|
aliases[ 0 ] = "objective_reminder_1";
|
|||
|
aliases[ 1 ] = "objective_reminder_2";
|
|||
|
aliases[ 2 ] = "objective_reminder_3";
|
|||
|
break;
|
|||
|
|
|||
|
case "friendlyfire":
|
|||
|
aliases[ 0 ] = "friendlyfire_1";
|
|||
|
aliases[ 1 ] = "friendlyfire_2";
|
|||
|
aliases[ 2 ] = "friendlyfire_3";
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ( aliases.size > 0 )
|
|||
|
{
|
|||
|
alias = aliases[ RandomInt( aliases.size ) ];
|
|||
|
}
|
|||
|
|
|||
|
thread radio_dialogue( alias );
|
|||
|
}
|
|||
|
|
|||
|
// Kicks off the chopper threads
|
|||
|
chopper_think()
|
|||
|
{
|
|||
|
// draw_high_obstacles();
|
|||
|
|
|||
|
self SetMaxPitchRoll( 30, 30 );
|
|||
|
|
|||
|
level.chopper_segment_points = 15;
|
|||
|
level.chopper_range_from_point = 1300;
|
|||
|
|
|||
|
level.chopper_base_elevation = 3100;
|
|||
|
level.chopper_lookat_point = level.groundplayer.origin;
|
|||
|
|
|||
|
chopper_dialog( "lift_off" );
|
|||
|
|
|||
|
// initial getting into the air
|
|||
|
liftoffPath = get_targeted_line_array( self.start );
|
|||
|
|
|||
|
for ( i = 1; i < liftoffPath.size; i++ )
|
|||
|
{
|
|||
|
if ( i == 1 )
|
|||
|
{
|
|||
|
self Vehicle_SetSpeed( 20, 6, 6 );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
self chopper_default_speed();
|
|||
|
}
|
|||
|
|
|||
|
node = liftoffPath[ i ];
|
|||
|
|
|||
|
self SetGoalYaw( node.angles[ 1 ] );
|
|||
|
self SetVehGoalPos( node.origin, 0 );
|
|||
|
self waittill_either( "near_goal", "goal" );
|
|||
|
}
|
|||
|
|
|||
|
chopper_dialog( "objective" );
|
|||
|
chopper_dialog( "objective2" );
|
|||
|
|
|||
|
self chopper_defaults();
|
|||
|
|
|||
|
level.chopperhint_time = GetTime();
|
|||
|
level.chopperplayer thread display_hint_timeout( "ads_slowdown", 5 );
|
|||
|
|
|||
|
self thread chopper_gun_face_entity( level.groundplayer );
|
|||
|
self thread chopper_move_with_player();
|
|||
|
// self thread chopper_target();
|
|||
|
|
|||
|
self thread debug_chopper_base_path();
|
|||
|
}
|
|||
|
|
|||
|
so_ads_slowdown_hint()
|
|||
|
{
|
|||
|
if ( GetTime() > level.chopperhint_time + 2000 )
|
|||
|
{
|
|||
|
if ( chopperplayer_pressing_slowdown() )
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
so_fake_choppergunner()
|
|||
|
{
|
|||
|
spawner = GetEnt( "so_choppergunner_spawner", "targetname" );
|
|||
|
drone = maps\_spawner::spawner_dronespawn( spawner );
|
|||
|
|
|||
|
drone LinkTo( level.chopper, "tag_player" );
|
|||
|
}
|
|||
|
|
|||
|
test_chopper_paths()
|
|||
|
{
|
|||
|
level.chopper chopper_follow_path( "so_chopper_driveby", false );
|
|||
|
|
|||
|
level.chopper notify( "stop_chopper_gun_face_entity" );
|
|||
|
level.chopper chopper_defaults();
|
|||
|
level.chopper SetHoverParams( 10, 2, 1 );
|
|||
|
level.chopper ClearLookAtEnt();
|
|||
|
level.chopper thread chopper_gun_face_entity( getstruct( "so_chopper_gasstation_lookat", "targetname" ) );
|
|||
|
|
|||
|
wait( 1 );
|
|||
|
|
|||
|
struct = getstruct( "so_last_driveby_point", "script_noteworthy" );
|
|||
|
level.chopper SetVehGoalPos( struct.origin, 1 );
|
|||
|
level.chopper SetHoverParams( 10, 2, 1 );
|
|||
|
level.chopper Vehicle_SetSpeed( 5, 1, 1 );
|
|||
|
level.chopper thread chopper_fake_hover( struct.origin );
|
|||
|
|
|||
|
level waittill( "never" );
|
|||
|
}
|
|||
|
|
|||
|
chopper_fake_hover( origin, dist, use_goal )
|
|||
|
{
|
|||
|
self endon( "stop_chopper_fake_hover" );
|
|||
|
|
|||
|
if ( !IsDefined( dist ) )
|
|||
|
{
|
|||
|
dist = 100;
|
|||
|
}
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
x = RandomFloatRange( dist * -1, dist );
|
|||
|
y = RandomFloatRange( dist * -1, dist );
|
|||
|
z = RandomFloatRange( dist * -1, dist );
|
|||
|
|
|||
|
self SetVehGoalPos( origin + ( x, y, z ), 1 );
|
|||
|
|
|||
|
if ( IsDefined( use_goal ) && use_goal )
|
|||
|
{
|
|||
|
self Vehicle_SetSpeed( 5 + RandomInt( 10 ), 3, 3 );
|
|||
|
self waittill( "goal" );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
wait( RandomFloatRange( 3, 5 ) );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//chopper_target()
|
|||
|
//{
|
|||
|
// while ( 1 )
|
|||
|
// {
|
|||
|
// wait( 0.5 );
|
|||
|
// foreach ( ai in GetAIArray( "axis" ) )
|
|||
|
// {
|
|||
|
// if ( !IsDefined( ai.showing_as_target ) && IsAlive( ai ) && !( IsDefined( ai.a.special ) && ai.a.special == "none" ) )
|
|||
|
// {
|
|||
|
// ai.showing_as_target = true;
|
|||
|
// Target_Set( ai, ( 0, 0, 32 ) );
|
|||
|
// Target_SetShader( ai, "remotemissile_infantry_target" );
|
|||
|
// Target_ShowToPlayer( ai, level.chopperplayer );
|
|||
|
// }
|
|||
|
// }
|
|||
|
// }
|
|||
|
//}
|
|||
|
|
|||
|
// Handles the chopper orientation with the ground player
|
|||
|
chopper_gun_face_entity( ent, wait_for_goal, delay )
|
|||
|
{
|
|||
|
self notify( "stop_chopper_gun_face_entity" );
|
|||
|
self endon( "stop_chopper_gun_face_entity" );
|
|||
|
|
|||
|
if ( !IsDefined( level.chopper_gun_ground_entity ) )
|
|||
|
{
|
|||
|
level.chopper_gun_ground_entity = Spawn( "script_origin", self.origin );
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( wait_for_goal ) && wait_for_goal )
|
|||
|
{
|
|||
|
self SetMaxPitchRoll( 20, 20 );
|
|||
|
// self waittill_either( "near_goal", "goal" );
|
|||
|
self waittill( "chopper_near_goal" );
|
|||
|
|
|||
|
self chopper_default_pitch_roll();
|
|||
|
}
|
|||
|
|
|||
|
if ( !IsDefined( delay ) )
|
|||
|
{
|
|||
|
delay = 1;
|
|||
|
}
|
|||
|
|
|||
|
self delayCall( delay, ::SetLookAtEnt, level.chopper_gun_ground_entity );
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
if ( ent == level.groundplayer )
|
|||
|
{
|
|||
|
lookat_origin = level.chopper_lookat_point;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
lookat_origin = ent.origin;
|
|||
|
}
|
|||
|
|
|||
|
// "forward" = vector from the chopper to the groundplayer
|
|||
|
forwardvec = VectorNormalize( lookat_origin - self.origin );
|
|||
|
forwardangles = VectorToAngles( forwardvec );
|
|||
|
rightvec = AnglesToRight( forwardangles );
|
|||
|
backvec = rightvec * -1;
|
|||
|
neworigin = self.origin + ( backvec * 100 );
|
|||
|
|
|||
|
level.chopper_gun_ground_entity.origin = neworigin;
|
|||
|
wait( 0.05 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Overall chopper movement thread
|
|||
|
chopper_move_with_player( player )
|
|||
|
{
|
|||
|
thread debug_player_pos();
|
|||
|
self.chopper_pathpoint = chopper_get_closest_pathpoint( 3 );
|
|||
|
self.no_bline_to_goal = true;
|
|||
|
just_started = true;
|
|||
|
|
|||
|
// update_direction_duration = 5000;
|
|||
|
// next_update_direction = GetTime() + update_direction_duration;
|
|||
|
|
|||
|
self chopper_reset_range_points();
|
|||
|
self.slowdown_points = [];
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
if ( self ent_flag( "manual_control" ) )
|
|||
|
{
|
|||
|
wait( 0.1 );
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
self SetNearGoalNotifyDist( 200 );
|
|||
|
|
|||
|
chopper_move_till_goal();
|
|||
|
|
|||
|
self.chopper_pathpoint = chopper_get_next_pathpoint( self.chopper_pathpoint[ "index" ] );
|
|||
|
|
|||
|
if ( just_started )
|
|||
|
{
|
|||
|
just_started = false;
|
|||
|
self.no_bline_to_goal = false;
|
|||
|
}
|
|||
|
|
|||
|
// if ( GetTime() > next_update_direction )
|
|||
|
// {
|
|||
|
// next_update_direction = GetTime() + update_direction_duration;
|
|||
|
// chopper_update_enemy_direction();
|
|||
|
// }
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//chopper_update_enemy_direction()
|
|||
|
//{
|
|||
|
// enemies = GetAiArray( "axis" );
|
|||
|
//
|
|||
|
// self.slowdown_points = [];
|
|||
|
//
|
|||
|
// if ( GetDvarInt( "test_chopper_path" ) < 1 )
|
|||
|
// {
|
|||
|
// return;
|
|||
|
// }
|
|||
|
//
|
|||
|
// if ( enemies.size == 0 )
|
|||
|
// {
|
|||
|
// return;
|
|||
|
// }
|
|||
|
//
|
|||
|
// origins = ( 0, 0, 0 );
|
|||
|
// foreach ( enemy in enemies )
|
|||
|
// {
|
|||
|
// origins += enemy.origin;
|
|||
|
// }
|
|||
|
//
|
|||
|
// avg_origin = ( origins[ 0 ] / enemies.size, origins[ 1 ] / enemies.size, origins[ 2 ] / enemies.size );
|
|||
|
// angles = VectorToAngles( avg_origin - level.groundplayer.origin );
|
|||
|
// angles = ( AngleClamp( angles[ 0 ] ), AngleClamp( angles[ 1 ] ), AngleClamp( angles[ 2 ] ) );
|
|||
|
//
|
|||
|
// // Figure out what points along the chopper points for the angles
|
|||
|
// y = AngleClamp( angles[ 1 ] + 180 );
|
|||
|
//
|
|||
|
// within = 60;
|
|||
|
//
|
|||
|
//
|
|||
|
// for ( i = 0; i < level.chopper_segment_points; i++ )
|
|||
|
// {
|
|||
|
// temp = AngleClamp( ( ( 360 / level.chopper_segment_points ) * -1 ) * i );//* - 1 to have the reverse effect of movement( forward )
|
|||
|
// if ( temp <= ( y + within ) && temp >= ( y - within ) )
|
|||
|
// {
|
|||
|
// self.slowdown_points[ i ] = true;
|
|||
|
// }
|
|||
|
// else
|
|||
|
// {
|
|||
|
// self.slowdown_points[ i ] = false;
|
|||
|
// }
|
|||
|
// }
|
|||
|
//
|
|||
|
// level thread debug_draw_enemy_direction( angles, "update_enemy_direction" );
|
|||
|
//}
|
|||
|
|
|||
|
// Keeps updating the choppers goal, incase the ground player moves, will end once the chopper
|
|||
|
// reaches it's goal.
|
|||
|
chopper_move_till_goal()
|
|||
|
{
|
|||
|
self endon( "chopper_near_goal" );
|
|||
|
self endon( "manual_control" );
|
|||
|
|
|||
|
self thread chopper_notify_near_goal();
|
|||
|
is_far = false;
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
pos = chopper_get_pathpoint( self.chopper_pathpoint[ "index" ] );
|
|||
|
|
|||
|
level thread debug_draw_chopper_line( pos, "final_destination", ( 0, 1, 0 ) );
|
|||
|
|
|||
|
// If the chopper is too far away from it's goal, figure out a b-line path to the closest
|
|||
|
// Assume that we need to get back on track...
|
|||
|
if ( !self.no_bline_to_goal && distance2d_squared( pos, self.origin ) > 1000 * 1000 )
|
|||
|
{
|
|||
|
// Stop the endon until we are close enough
|
|||
|
is_far = true;
|
|||
|
self notify( "stop_chopper_notify_near_goal" );
|
|||
|
info = chopper_get_closest_pathpoint();
|
|||
|
|
|||
|
pos = info[ "point" ];
|
|||
|
self.chopper_pathpoint = info;
|
|||
|
|
|||
|
level thread debug_draw_chopper_line( pos, "final_destination", ( 0, 1, 0 ) );
|
|||
|
|
|||
|
pos = chopper_get_bline_path_point( pos );
|
|||
|
|
|||
|
level thread debug_draw_chopper_line( pos, "closer_point" );
|
|||
|
|
|||
|
if ( self ent_flag( "manual_control" ) )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
self SetVehGoalPos( pos );
|
|||
|
self waittill_either( "near_goal", "goal" );
|
|||
|
continue;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if ( is_far )
|
|||
|
{
|
|||
|
// Reinitiate the chopper_notify_near_goal since we stopped it before
|
|||
|
self thread chopper_notify_near_goal();
|
|||
|
}
|
|||
|
|
|||
|
is_far = false;
|
|||
|
}
|
|||
|
|
|||
|
if ( self ent_flag( "manual_control" ) )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
chopper_slow_down();
|
|||
|
|
|||
|
// Line( pos, pos + ( 0, 0, 2000 ), ( 1, 1, 0 ), 2 );
|
|||
|
|
|||
|
level thread debug_draw_chopper_line( pos, "closer_point" );
|
|||
|
self SetVehGoalPos( pos );
|
|||
|
wait( 0.1 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
chopperplayer_pressing_slowdown()
|
|||
|
{
|
|||
|
if ( level.chopperplayer AdsButtonPressed() || level.chopperplayer UseButtonPressed() )
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
chopper_slow_down()
|
|||
|
{
|
|||
|
// if ( GetDvarInt( "test_chopper_slowdown" ) == 1 )
|
|||
|
// {
|
|||
|
if ( chopperplayer_pressing_slowdown() )
|
|||
|
{
|
|||
|
chopper_slow_speed();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
chopper_default_speed();
|
|||
|
}
|
|||
|
// }
|
|||
|
}
|
|||
|
|
|||
|
chopper_notify_near_goal()
|
|||
|
{
|
|||
|
self notify( "stop_chopper_notify_near_goal" );
|
|||
|
self endon( "stop_chopper_notify_near_goal" );
|
|||
|
|
|||
|
self waittill_either( "near_goal", "goal" );
|
|||
|
self notify( "chopper_near_goal" );
|
|||
|
}
|
|||
|
|
|||
|
// Returns the 1 chopper flight path, depending on the num passed in
|
|||
|
chopper_get_pathpoint( num )
|
|||
|
{
|
|||
|
add_angles = ( 360 / level.chopper_segment_points ) * -1;//* - 1 to have the reverse effect of movement( forward )
|
|||
|
|
|||
|
angles = ( 0, add_angles * num, 0 );
|
|||
|
forward = AnglesToForward( angles );
|
|||
|
// point = level.groundplayer.origin + vector_multiply( forward, level.chopper_range_from_point );
|
|||
|
|
|||
|
if ( is_player_in_parking_lot() )
|
|||
|
{
|
|||
|
level.chopper_lookat_point = get_parkinglot_point();
|
|||
|
point = level.chopper_lookat_point + vector_multiply( forward, level.chopper_range_from_point );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
level.chopper_lookat_point = get_closest_point_on_base_path();
|
|||
|
point = level.chopper_lookat_point + vector_multiply( forward, level.chopper_range_from_point );
|
|||
|
}
|
|||
|
|
|||
|
point = chopper_get_pointheight( point );
|
|||
|
|
|||
|
return point;
|
|||
|
}
|
|||
|
|
|||
|
chopper_get_bline_path_point( pos )
|
|||
|
{
|
|||
|
angles = VectorToAngles( pos - level.chopper.origin );
|
|||
|
forward = AnglesToForward( angles );
|
|||
|
point = level.chopper.origin + vector_multiply( forward, 500 );
|
|||
|
|
|||
|
point = chopper_get_pointheight( point );
|
|||
|
|
|||
|
return point;
|
|||
|
}
|
|||
|
|
|||
|
// Returns all of the choppers flight path points
|
|||
|
chopper_get_pathpoints()
|
|||
|
{
|
|||
|
points = [];
|
|||
|
|
|||
|
for ( i = 0; i < level.chopper_segment_points; i++ )
|
|||
|
{
|
|||
|
points[ i ] = chopper_get_pathpoint( i );
|
|||
|
}
|
|||
|
|
|||
|
return points;
|
|||
|
}
|
|||
|
|
|||
|
// Takes the given point and adjusts it's Z coordinate depending on the obstacles.
|
|||
|
chopper_get_pointheight( point )
|
|||
|
{
|
|||
|
base = level.chopper_base_elevation;
|
|||
|
height = base;
|
|||
|
|
|||
|
info = chopper_get_closest_obstacle_info( point );
|
|||
|
|
|||
|
if( IsDefined( info[ "struct" ] ) )
|
|||
|
{
|
|||
|
if ( info[ "dist" ] < info[ "min_radius" ] )
|
|||
|
{
|
|||
|
height = info[ "struct" ].origin[ 2 ];
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// height = level.chopper_base_elevation + ( ( info[ "struct" ].origin[ 2 ] - level.chopper_base_elevation ) * ( info[ "dist" ] / ( info[ "max_radius" ] - info[ "min_radius" ] ) ) );
|
|||
|
height_diff = info[ "struct" ].origin[ 2 ] - level.chopper_base_elevation;
|
|||
|
height_perc = info[ "dist" ] / ( info[ "max_radius" ] - info[ "min_radius" ] );
|
|||
|
height = base + ( height_diff * height_perc );
|
|||
|
}
|
|||
|
|
|||
|
if( height < base )
|
|||
|
{
|
|||
|
height = base;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
point = ( point[ 0 ], point[ 1 ], height );
|
|||
|
|
|||
|
return point;
|
|||
|
}
|
|||
|
|
|||
|
// Return the closest and highest struct
|
|||
|
chopper_get_closest_obstacle_info( point )
|
|||
|
{
|
|||
|
structs = getstructarray( "high_obstacle", "targetname" );
|
|||
|
|
|||
|
// First find all of the structs the point within it's radius.
|
|||
|
close_structs = [];
|
|||
|
dist_array = [];
|
|||
|
min_radius_array = [];
|
|||
|
max_radius_array = [];
|
|||
|
foreach ( struct in structs )
|
|||
|
{
|
|||
|
max_radius = 600;
|
|||
|
if ( IsDefined( struct.radius ) )
|
|||
|
{
|
|||
|
max_radius = struct.radius;
|
|||
|
}
|
|||
|
|
|||
|
min_radius = max_radius * 0.5;
|
|||
|
|
|||
|
test_dist = Distance2D( point, struct.origin );
|
|||
|
if ( test_dist < max_radius )
|
|||
|
{
|
|||
|
close_structs[ close_structs.size ] = struct;
|
|||
|
dist_array[ dist_array.size ] = test_dist;
|
|||
|
min_radius_array[ min_radius_array.size ] = min_radius;
|
|||
|
max_radius_array[ max_radius_array.size ] = max_radius;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Now filter out the highest struct and return it
|
|||
|
highest_struct = undefined;
|
|||
|
dist = undefined;
|
|||
|
min_radius = undefined;
|
|||
|
max_radius = undefined;
|
|||
|
if ( close_structs.size > 0 )
|
|||
|
{
|
|||
|
highest_struct = close_structs[ 0 ];
|
|||
|
dist = dist_array[ 0 ];
|
|||
|
min_radius = min_radius_array[ 0 ];
|
|||
|
max_radius = max_radius_array[ 0 ];
|
|||
|
|
|||
|
for ( i = 1; i < close_structs.size; i++ )
|
|||
|
{
|
|||
|
if ( close_structs[ i ].origin[ 2 ] > highest_struct.origin [ 2 ] )
|
|||
|
{
|
|||
|
highest_struct = close_structs[ i ];
|
|||
|
dist = dist_array[ i ];
|
|||
|
min_radius = min_radius_array[ i ];
|
|||
|
max_radius = max_radius_array[ i ];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
info = [];
|
|||
|
info[ "struct" ] = highest_struct;
|
|||
|
info[ "dist" ] = dist;
|
|||
|
info[ "min_radius" ] = min_radius;
|
|||
|
info[ "max_radius" ] = max_radius;
|
|||
|
|
|||
|
return info;
|
|||
|
}
|
|||
|
|
|||
|
// 0 = along the X
|
|||
|
chopper_set_range_points( min_num, max_num )
|
|||
|
{
|
|||
|
self.min_point_on_pathpoints = min_num;
|
|||
|
self.max_point_on_pathpoints = max_num;
|
|||
|
}
|
|||
|
|
|||
|
chopper_reset_range_points()
|
|||
|
{
|
|||
|
self.min_point_on_pathpoints = 0;
|
|||
|
self.max_point_on_pathpoints = 0;
|
|||
|
self.hover_direction = 1;
|
|||
|
}
|
|||
|
|
|||
|
// Returns the next point (in array form for extra info) on the path
|
|||
|
chopper_get_next_pathpoint( num )
|
|||
|
{
|
|||
|
points = chopper_get_pathpoints();
|
|||
|
|
|||
|
min_point = self.min_point_on_pathpoints;
|
|||
|
max_point = self.max_point_on_pathpoints;
|
|||
|
|
|||
|
// if not 0 and 0, then stay within the range
|
|||
|
if ( min_point - max_point != 0 )
|
|||
|
{
|
|||
|
if ( num == min_point )
|
|||
|
{
|
|||
|
self.hover_direction = 1;
|
|||
|
}
|
|||
|
else if ( num == max_point )
|
|||
|
{
|
|||
|
self.hover_direction = -1;
|
|||
|
}
|
|||
|
|
|||
|
num = num + self.hover_direction;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
self.hover_direction = 1;
|
|||
|
num++;
|
|||
|
}
|
|||
|
|
|||
|
if ( num < 0 )
|
|||
|
{
|
|||
|
num = level.chopper_segment_points - 1;
|
|||
|
}
|
|||
|
|
|||
|
if ( num >= points.size )
|
|||
|
{
|
|||
|
num = 0;
|
|||
|
}
|
|||
|
|
|||
|
info = [];
|
|||
|
info[ "point" ] = points[ num ];
|
|||
|
info[ "index" ] = num;
|
|||
|
|
|||
|
// Slow down section
|
|||
|
// if ( GetDvarInt( "test_chopper_path" ) > 0 )
|
|||
|
// {
|
|||
|
// if ( self.slowdown_points.size > 0 )
|
|||
|
// {
|
|||
|
// if ( self.slowdown_points[ num ] )
|
|||
|
// {
|
|||
|
// info[ "enemydir" ] = true;
|
|||
|
// }
|
|||
|
// }
|
|||
|
// }
|
|||
|
|
|||
|
return info;
|
|||
|
}
|
|||
|
|
|||
|
// Returns the closest point (in array form for extra info) on the path
|
|||
|
chopper_get_closest_pathpoint( add_index )
|
|||
|
{
|
|||
|
points = chopper_get_pathpoints();
|
|||
|
|
|||
|
dist = DistanceSquared( points[ 0 ], level.chopper.origin );
|
|||
|
closest = points[ 0 ];
|
|||
|
index = 0;
|
|||
|
|
|||
|
foreach ( i, point in points )
|
|||
|
{
|
|||
|
test = DistanceSquared( point, level.chopper.origin );
|
|||
|
if ( test < dist )
|
|||
|
{
|
|||
|
closest = point;
|
|||
|
index = i;
|
|||
|
dist = test;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
info = [];
|
|||
|
if ( IsDefined( add_index ) )
|
|||
|
{
|
|||
|
index = index + add_index;
|
|||
|
|
|||
|
if ( index > level.chopper_segment_points )
|
|||
|
{
|
|||
|
index = index - level.chopper_segment_points;
|
|||
|
}
|
|||
|
|
|||
|
info[ "point" ] = chopper_get_pathpoint( index );
|
|||
|
info[ "index" ] = index;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
info[ "point" ] = closest;
|
|||
|
info[ "index" ] = index;
|
|||
|
}
|
|||
|
|
|||
|
return info;
|
|||
|
}
|
|||
|
|
|||
|
chopper_follow_path( path_targetname, follow_player_when_done, dialog, safe_flight )
|
|||
|
{
|
|||
|
self notify( "stop_chopper_fake_hover" );
|
|||
|
self notify( "stop_chopper_gun_face_entity" );
|
|||
|
self ClearLookAtEnt();
|
|||
|
self.speed_setting = "none";
|
|||
|
|
|||
|
self ent_flag_set( "manual_control" );
|
|||
|
|
|||
|
if ( !IsDefined( safe_flight ) )
|
|||
|
{
|
|||
|
safe_flight = false;
|
|||
|
}
|
|||
|
|
|||
|
path_start = getstruct( path_targetname, "targetname" );
|
|||
|
path_point = path_start;
|
|||
|
going_to_start = true;
|
|||
|
|
|||
|
while ( IsDefined( path_point ) )
|
|||
|
{
|
|||
|
if ( IsDefined( path_point.speed ) )
|
|||
|
{
|
|||
|
speed = path_point.speed;
|
|||
|
|
|||
|
accel = 20;
|
|||
|
decel = 10;
|
|||
|
|
|||
|
if ( IsDefined( path_point.script_accel ) )
|
|||
|
{
|
|||
|
accel = path_point.script_accel;
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( path_point.script_decel ) )
|
|||
|
{
|
|||
|
decel = path_point.script_decel;
|
|||
|
}
|
|||
|
|
|||
|
self Vehicle_SetSpeed( path_point.speed, accel, decel );
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( path_point.script_speed ) )
|
|||
|
{
|
|||
|
speed = path_point.script_speed;
|
|||
|
|
|||
|
accel = 20;
|
|||
|
decel = 10;
|
|||
|
|
|||
|
if ( IsDefined( path_point.script_accel ) )
|
|||
|
{
|
|||
|
accel = path_point.script_accel;
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( path_point.script_decel ) )
|
|||
|
{
|
|||
|
decel = path_point.script_decel;
|
|||
|
}
|
|||
|
|
|||
|
self Vehicle_SetSpeedImmediate( path_point.script_speed, accel, decel );
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( path_point.radius ) )
|
|||
|
{
|
|||
|
self SetNearGoalNotifyDist( path_point.radius );
|
|||
|
}
|
|||
|
|
|||
|
stop_at_goal = false;
|
|||
|
if ( IsDefined( path_point.script_stopnode ) && path_point.script_stopnode )
|
|||
|
{
|
|||
|
stop_at_goal = true;
|
|||
|
}
|
|||
|
|
|||
|
self SetGoalYaw( path_point.angles[ 1 ] );
|
|||
|
|
|||
|
// If the chopper is too far away from it's goal, figure out a b-line path
|
|||
|
if ( going_to_start && safe_flight )
|
|||
|
{
|
|||
|
while ( distance2d_squared( path_point.origin, self.origin ) > 1000 * 1000 )
|
|||
|
{
|
|||
|
point = chopper_get_bline_path_point( path_point.origin );
|
|||
|
self SetVehGoalPos( point, stop_at_goal );
|
|||
|
self waittill_either( "near_goal", "goal" );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
self SetVehGoalPos( path_point.origin, stop_at_goal );
|
|||
|
self waittill_either( "near_goal", "goal" );
|
|||
|
|
|||
|
if ( IsDefined( path_point.script_flag_set ) )
|
|||
|
{
|
|||
|
flag_set( path_point.script_flag_set );
|
|||
|
}
|
|||
|
|
|||
|
going_to_start = false;
|
|||
|
|
|||
|
if ( IsDefined( path_point.script_noteworthy ) )
|
|||
|
{
|
|||
|
[[ level.chopper_funcs[ path_point.script_noteworthy ]]]();
|
|||
|
}
|
|||
|
|
|||
|
path_point script_delay();
|
|||
|
|
|||
|
if ( !IsDefined( path_point.target ) )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
path_point = getstruct( path_point.target, "targetname" );
|
|||
|
}
|
|||
|
|
|||
|
self notify( "follow_path_done" );
|
|||
|
|
|||
|
if ( IsDefined( follow_player_when_done ) && follow_player_when_done )
|
|||
|
{
|
|||
|
self chopper_defaults();
|
|||
|
self.chopper_pathpoint = chopper_get_closest_pathpoint();
|
|||
|
self ent_flag_clear( "manual_control" );
|
|||
|
self thread chopper_gun_face_entity( level.groundplayer, true );
|
|||
|
}
|
|||
|
|
|||
|
if ( IsDefined( dialog ) )
|
|||
|
{
|
|||
|
chopper_dialog( dialog );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
get_parkinglot_point()
|
|||
|
{
|
|||
|
point = level.groundplayer.origin;
|
|||
|
|
|||
|
point = ( clamp( point[ 0 ], -2400, 3100 ), clamp( point[ 1 ], -5300, -700 ), point[ 2 ] );
|
|||
|
|
|||
|
return point;
|
|||
|
}
|
|||
|
|
|||
|
get_closest_point_on_base_path()
|
|||
|
{
|
|||
|
paths = [];
|
|||
|
struct_array = getstructarray( "base_player_path", "targetname" );
|
|||
|
|
|||
|
foreach ( struct in struct_array )
|
|||
|
{
|
|||
|
paths[ paths.size ] = get_targeted_line_array( struct );
|
|||
|
}
|
|||
|
|
|||
|
points = [];
|
|||
|
foreach ( path in paths )
|
|||
|
{
|
|||
|
for ( i = 0; i < path.size - 1; i++ )
|
|||
|
{
|
|||
|
points[ points.size ] = PointOnSegmentNearestToPoint( path[ i ].origin, path[ i + 1 ].origin, level.groundplayer.origin );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dist = DistanceSquared( points[ 0 ], level.groundplayer.origin );
|
|||
|
closest_point = points[ 0 ];
|
|||
|
|
|||
|
foreach ( point in points )
|
|||
|
{
|
|||
|
test_dist = DistanceSquared( point, level.groundplayer.origin );
|
|||
|
if ( test_dist < dist )
|
|||
|
{
|
|||
|
closest_point = point;
|
|||
|
dist = test_dist;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Let's not go more than 200 units off the base path
|
|||
|
if ( distance2d_squared( closest_point, level.groundplayer.origin ) > 200 * 200 )
|
|||
|
{
|
|||
|
angles = VectorToAngles( level.groundplayer.origin - closest_point );
|
|||
|
forward = AnglesToForward( angles );
|
|||
|
closest_point = closest_point + vector_multiply( forward, 200 );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
closest_point = level.groundplayer.origin;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return closest_point;
|
|||
|
}
|
|||
|
|
|||
|
distance2d_squared( pos1, pos2 )
|
|||
|
{
|
|||
|
pos1 = ( pos1[ 0 ], pos1[ 1 ], 0 );
|
|||
|
pos2 = ( pos2[ 0 ], pos2[ 1 ], 0 );
|
|||
|
|
|||
|
return DistanceSquared( pos1, pos2 );
|
|||
|
}
|
|||
|
|
|||
|
is_player_in_parking_lot()
|
|||
|
{
|
|||
|
return level.groundplayer IsTouching( GetEnt( "so_parkinglot", "targetname" ) );
|
|||
|
}
|
|||
|
|
|||
|
// TRUCK Section ------------------------------------------
|
|||
|
truck_init()
|
|||
|
{
|
|||
|
level.truck_spawner = GetEnt( "gas_station_truck", "targetname" );
|
|||
|
level.truck_ai_spawners = GetEntArray( "so_truck_ai_spawner", "targetname" );
|
|||
|
}
|
|||
|
|
|||
|
spawn_truck( targetname )
|
|||
|
{
|
|||
|
spawner = level.truck_spawner;
|
|||
|
ai_spawners = level.truck_ai_spawners;
|
|||
|
|
|||
|
spawner.script_startinghealth = 5000;
|
|||
|
|
|||
|
spawner.targetname = "so_truck";
|
|||
|
spawner.target = targetname;
|
|||
|
foreach ( ai_spawner in ai_spawners )
|
|||
|
{
|
|||
|
ai_spawner.targetname = targetname;
|
|||
|
}
|
|||
|
|
|||
|
truck = maps\_vehicle::spawn_vehicle_from_targetname_and_drive( "so_truck" );
|
|||
|
truck thread truck_brakes();
|
|||
|
|
|||
|
// So the corpse of the truck cannot be moved
|
|||
|
truck.free_on_death = true;
|
|||
|
}
|
|||
|
|
|||
|
truck_brakes()
|
|||
|
{
|
|||
|
self waittill( "unloading" );
|
|||
|
self set_brakes( 0.5 );
|
|||
|
}
|
|||
|
|
|||
|
// FX -----------------------------------------------------
|
|||
|
smoke_mover()
|
|||
|
{
|
|||
|
ent = Spawn( "script_model", ( 600, -4525, 2610 ) );
|
|||
|
ent.angles = ( 357, 179, 177 );
|
|||
|
ent SetModel( "tag_origin" );
|
|||
|
PlayFxOnTag( level._effect[ "objective_smoke" ], ent, "tag_origin" );
|
|||
|
ent thread smoke_mover_thread();
|
|||
|
}
|
|||
|
|
|||
|
// Will need a new FX to finish this off.
|
|||
|
smoke_mover_thread()
|
|||
|
{
|
|||
|
spawn_angles = self.angles;
|
|||
|
full_pitch = 60;
|
|||
|
range = 1500;
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 1 );
|
|||
|
dist = Distance2D( level.chopper.origin, self.origin );
|
|||
|
|
|||
|
percent = dist / range;
|
|||
|
|
|||
|
if ( percent < 1 )
|
|||
|
{
|
|||
|
percent = 1 - percent;
|
|||
|
|
|||
|
angles = VectorToAngles( vector2d( level.player.origin ) - vector2d( self.origin ) );
|
|||
|
angles = ( angles[ 0 ] + ( full_pitch * percent ), angles[ 1 ], angles[ 2 ] );
|
|||
|
|
|||
|
self RotateTo( spawn_angles + angles, 0.5 );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
self RotateTo( spawn_angles, 0.5 );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
vector2d( vec )
|
|||
|
{
|
|||
|
return ( vec[ 0 ], vec[ 1 ], 0 );
|
|||
|
}
|
|||
|
|
|||
|
// Exploders ----------------------------------------------
|
|||
|
do_exploder_custom( current, option )
|
|||
|
{
|
|||
|
while( 1 )
|
|||
|
{
|
|||
|
exploder_stripped( current.script_prefab_exploder, option );
|
|||
|
if( !isdefined( current.target ) )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
next = GetEnt( current.target, "targetname" );
|
|||
|
|
|||
|
if( !isdefined( next ) )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
current = next;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
exploder_stripped( num, option )
|
|||
|
{
|
|||
|
num += "";
|
|||
|
|
|||
|
//here's a hook so you can know when a certain number of an exploder is going off
|
|||
|
level notify( "exploding_" + num );
|
|||
|
|
|||
|
for ( i = 0;i < level.createFXent.size;i++ )
|
|||
|
{
|
|||
|
ent = level.createFXent[ i ];
|
|||
|
|
|||
|
if ( !isdefined( ent ) )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if ( ent.v[ "type" ] != "exploder" )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// make the exploder actually removed the array instead?
|
|||
|
if ( !isdefined( ent.v[ "exploder" ] ) )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if ( ent.v[ "exploder" ] + "" != num )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
ent.v[ "soundalias" ] = undefined;
|
|||
|
ent.v[ "loopsound" ] = undefined;
|
|||
|
ent.v[ "damage" ] = undefined;
|
|||
|
ent.v[ "delay" ] = 0;
|
|||
|
ent.v[ "delay_min" ] = undefined;
|
|||
|
ent.v[ "delay_max" ] = undefined;
|
|||
|
ent.v[ "earthquake" ] = undefined;
|
|||
|
|
|||
|
if ( IsDefined( option ) && option == "just_swap" )
|
|||
|
{
|
|||
|
ent.v[ "firefx" ] = undefined;
|
|||
|
ent.v[ "fxid" ] = undefined;
|
|||
|
}
|
|||
|
|
|||
|
ent activate_individual_exploder();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// DEBUG Section ------------------------------------------
|
|||
|
debug_chopper_base_path()
|
|||
|
{
|
|||
|
/#
|
|||
|
if ( !debug_chopper_enabled() )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
|
|||
|
if ( is_player_in_parking_lot() )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
closest_point = get_closest_point_on_base_path();
|
|||
|
|
|||
|
// level thread draw_linesegment_point( closest_point );
|
|||
|
Line( closest_point, closest_point + ( 0, 0, 1000 ), ( 1, 0.5, 0 ) );
|
|||
|
Line( closest_point, level.groundplayer.origin, ( 1, 1, 1 ) );
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
// Draw the player's location with chopper flight path
|
|||
|
// Dvar debug_follow + number will have the fake ground player move along a path
|
|||
|
// If not using debug_follow, the player can hit use to update the fake ground player's origin
|
|||
|
// to whatever the chopper player is aiming at.
|
|||
|
debug_player_pos()
|
|||
|
{
|
|||
|
/#
|
|||
|
if ( !debug_chopper_enabled() )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
level.groundplayer.origin = ( 84, 4450, 2260 );
|
|||
|
thread draw_player_pos();
|
|||
|
thread draw_chopper_path();
|
|||
|
|
|||
|
if ( GetDvarInt( "debug_follow" ) != 0 )
|
|||
|
{
|
|||
|
num = GetDvarInt( "debug_follow" );
|
|||
|
start = getstruct( "follow_player_path_start" + num, "targetname" );
|
|||
|
struct = start;
|
|||
|
speed = 200;
|
|||
|
|
|||
|
debug_trigger_everything();
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
dist = Distance( level.groundplayer.origin, struct.origin );
|
|||
|
time = dist / speed;
|
|||
|
|
|||
|
level.groundplayer MoveTo( struct.origin, time, 0, 0 );
|
|||
|
level.groundplayer waittill( "movedone" );
|
|||
|
|
|||
|
struct script_delay();
|
|||
|
|
|||
|
if( IsDefined( struct.target ) )
|
|||
|
{
|
|||
|
struct = getstruct( struct.target, "targetname" );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
struct = start;
|
|||
|
debug_trigger_everything();
|
|||
|
so_chopper_invasion_moments();
|
|||
|
debug_kill_ai();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
|
|||
|
if ( level.player UseButtonPressed() )
|
|||
|
{
|
|||
|
eye = level.player GetEye();
|
|||
|
forward = AnglesToForward( level.player GetPlayerAngles() );
|
|||
|
forward_origin = eye + vector_multiply( forward, 10000 );
|
|||
|
trace = BulletTrace( level.player GetEye(), forward_origin, false, self );
|
|||
|
|
|||
|
level.groundplayer.origin = trace[ "position" ];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
debug_trigger_everything()
|
|||
|
{
|
|||
|
/#
|
|||
|
foreach ( trigger in GetEntArray( "trigger_multiple_spawn", "classname" ) )
|
|||
|
{
|
|||
|
trigger thread debug_trigger_everything_think();
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
debug_trigger_everything_think()
|
|||
|
{
|
|||
|
/#
|
|||
|
self notify( "stop_debug_trigger_everything_think" );
|
|||
|
self endon( "stop_debug_trigger_everything_think" );
|
|||
|
|
|||
|
if ( !IsDefined( self.spawners ) )
|
|||
|
{
|
|||
|
self.spawners = GetEntArray( self.target, "targetname" );
|
|||
|
foreach ( spawner in self.spawners )
|
|||
|
{
|
|||
|
spawner.old_count = spawner.count;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
foreach ( spawner in self.spawners )
|
|||
|
{
|
|||
|
spawner.count = spawner.old_count;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
if ( level.groundplayer IsTouching( self ) )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
self notify( "trigger" );
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
// Draws the chopper flight path
|
|||
|
draw_chopper_path()
|
|||
|
{
|
|||
|
/#
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
|
|||
|
points = chopper_get_pathpoints();
|
|||
|
|
|||
|
for ( i = 0; i < points.size; i++ )
|
|||
|
{
|
|||
|
next = i + 1;
|
|||
|
|
|||
|
if ( next == points.size )
|
|||
|
{
|
|||
|
next = 0;
|
|||
|
}
|
|||
|
|
|||
|
color = ( 1, 1, 0.3 );
|
|||
|
|
|||
|
// if ( GetDvarInt( "test_chopper_path" ) > 0 )
|
|||
|
// {
|
|||
|
// if ( self.slowdown_points.size > 0 )
|
|||
|
// {
|
|||
|
// if ( self.slowdown_points[ i ] )
|
|||
|
// {
|
|||
|
// color = ( 0.3, 1, 0.3 );
|
|||
|
// }
|
|||
|
// }
|
|||
|
// }
|
|||
|
|
|||
|
Line( points[ i ], points[ next ], color );
|
|||
|
}
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
draw_linesegment_point( pos )
|
|||
|
{
|
|||
|
/#
|
|||
|
level notify( "stop_draw_linesegment_point" );
|
|||
|
level endon( "stop_draw_linesegment_point" );
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
Line( pos, pos + ( 0, 0, 1000 ), ( 1, 1, 0.1 ) );
|
|||
|
wait( 0.05 );
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
// Draws the ground player's position
|
|||
|
draw_player_pos( pos )
|
|||
|
{
|
|||
|
/#
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
Line( level.groundplayer.origin, level.groundplayer.origin + ( 0, 0, 1000 ), ( 0.3, 1, 0.3 ) );
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
// Draws all of the high objstacle points
|
|||
|
draw_high_obstacles()
|
|||
|
{
|
|||
|
/#
|
|||
|
structs = getstructarray( "high_obstacle", "targetname" );
|
|||
|
foreach ( struct in structs )
|
|||
|
{
|
|||
|
struct thread draw_high_obstacle();
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
draw_high_obstacle()
|
|||
|
{
|
|||
|
/#
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
Line( self.origin, self.origin + ( 0, 0, -5000 ), ( 1, 1, 1 ) );
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
debug_kill_ai()
|
|||
|
{
|
|||
|
/#
|
|||
|
foreach ( ai in GetAIArray( "axis" ) )
|
|||
|
{
|
|||
|
ai Kill();
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
debug_chopper_enabled()
|
|||
|
{
|
|||
|
/#
|
|||
|
return GetDvarInt( "debug_chopper" ) == 1;
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
debug_draw_chopper_line( pos, note, color )
|
|||
|
{
|
|||
|
/#
|
|||
|
level notify( note );
|
|||
|
level endon( note );
|
|||
|
|
|||
|
if ( !IsDefined( color ) )
|
|||
|
{
|
|||
|
color = ( 1, 1, 1 );
|
|||
|
}
|
|||
|
|
|||
|
if ( !debug_chopper_enabled() )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
Line( pos, level.chopper.origin, color );
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|
|||
|
|
|||
|
debug_draw_enemy_direction( angle, note )
|
|||
|
{
|
|||
|
/#
|
|||
|
level notify( note );
|
|||
|
level endon( note );
|
|||
|
|
|||
|
color = ( 1, 1, 1 );
|
|||
|
|
|||
|
if ( !debug_chopper_enabled() )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
wait( 0.05 );
|
|||
|
pos = level.groundplayer.origin + vector_multiply( AnglesToForward( angle ), 1000 );
|
|||
|
Line( pos, level.groundplayer.origin, color );
|
|||
|
}
|
|||
|
#/
|
|||
|
}
|