2665 lines
75 KiB
Plaintext
2665 lines
75 KiB
Plaintext
#using scripts\shared\ai_shared;
|
|
#using scripts\shared\array_shared;
|
|
#using scripts\shared\clientfield_shared;
|
|
#using scripts\shared\flag_shared;
|
|
#using scripts\shared\flagsys_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\vehicleriders_shared;
|
|
|
|
|
|
|
|
|
|
#namespace turret;
|
|
|
|
function autoexec __init__sytem__() { system::register("turret",&__init__,undefined,undefined); }
|
|
|
|
/*-----------------------------------------------------------------------------------------------------
|
|
|
|
******************************************************************
|
|
Turret Overview
|
|
******************************************************************
|
|
|
|
This script handles all turret functionality.
|
|
|
|
Below are a list of script command available.
|
|
|
|
See the module:- t6\game\share\devraw\scripts\module_turret.gsc for a group simple examples that shows the
|
|
scripter in-game MG Turrent and Helicopter Turret examples.
|
|
|
|
Note: An MG Turret typicaly only has 1 turret.
|
|
Helicopter can have upto 5 turrets, the main turret and 4 gunner turrets.
|
|
|
|
Setting up an MG Turret in Radient:-
|
|
- See the file module_turret.gsc for a description of how to setup a manned MG Turret in Radiant.
|
|
|
|
|
|
******************************************************************
|
|
Using, Activating and Pausing a Turret
|
|
******************************************************************
|
|
|
|
is_turret_enabled( n_index )
|
|
- Checks to see if the turret is enabled.
|
|
|
|
enable( n_index )
|
|
- Enables the given turret.
|
|
|
|
disable( [n_index] )
|
|
- Disables the given turret, but it keeps its targetted information.
|
|
|
|
pause( [n_index], <time, 0=infinite> )
|
|
- Pause turret for X time.
|
|
|
|
unpause( [n_index] )
|
|
- remove the pause restriction from the turret.
|
|
|
|
stop( <n_index> )
|
|
- Stops the turrets current action and puts it back in search for enemy mode.
|
|
|
|
|
|
******************************************************************
|
|
Turret "User" Functios - Turrets that need Users to Operate
|
|
******************************************************************
|
|
|
|
does_need_user( <n_index> )
|
|
- Does the turret need a used (node) to oprerate.
|
|
|
|
does_have_user( <n_index> )
|
|
- Does the turret currently have a user?
|
|
|
|
get_user( <index> )
|
|
- Gets the user of this turret.
|
|
|
|
is_current_user( <ai_user>, [n_index] )
|
|
- Checks to see if an AI is the current user.
|
|
|
|
SetOnTargetAngle( <angle> )
|
|
- The turret has to be aiming within this number of degrees at the target to get the
|
|
"turret_on_target" notify.
|
|
|
|
|
|
******************************************************************
|
|
Turret Targetting Functions
|
|
******************************************************************
|
|
|
|
set_target( <e_target>, [v_offset], [n_index] )
|
|
- Sets the target of this turret.
|
|
|
|
get_target( [n_index] )
|
|
- Returns turret target data for this turret.
|
|
|
|
clear_target( [n_index] )
|
|
- Clears turret target data for this turret.
|
|
|
|
set_target_flags( <n_flags>, [n_index] )
|
|
- Sets a sub-set of targets for the turret.
|
|
|
|
set_target_ent_array( [a_ents] )
|
|
- Passes an array of targets to the turret to kill.
|
|
|
|
clear_target_ent_array()
|
|
- Scripters ability to clear the turrets ent target array.
|
|
|
|
set_ignore_ent_array( [a_ents] )
|
|
- Passes an array of ents to the turret to ignore.
|
|
|
|
clear_ignore_ent_array()
|
|
- Scripters ability to clear the turrets ent ignore array.
|
|
|
|
set_best_target_func( <::function>, [n_index] )
|
|
- Custom override function, scripter can use to custermise their choice from potential targets;
|
|
|
|
is_target_in_view( e_target, n_index )
|
|
- Lets script know if the turret can aim for a target within its GDT movement constraints.
|
|
|
|
can_turret_shoot_target( e_target, n_index )
|
|
- Basically is there anythinhg blocking the turrets shot (geo etc..)
|
|
|
|
can_hit_target( e_target, turret_index )
|
|
- Basically combines the two functions above ( is_target_in_view() and can_turret_shoot_target() )
|
|
|
|
set_max_target_distance( n_dist )
|
|
- Can be used to limit the maximum distance of a turret's target
|
|
|
|
set_min_target_distance( n_dist )
|
|
- Can be used to limit the minimum distance of a turret's target (from pivot point)
|
|
|
|
set_min_target_distance_squared( n_dist_squared )
|
|
- Can be used to limit the minimum distance of a turret's target (from pivot point); squared distance version of above
|
|
|
|
|
|
******************************************************************
|
|
Turret Firing Functions
|
|
******************************************************************
|
|
|
|
fire( [n_index] )
|
|
- Fires a turret.
|
|
|
|
fire_for_time( <n_time> , [n_index] )
|
|
- Fires a turret for a time, blocks for n_time.
|
|
|
|
shoot_at_target( <ent>, <time> );
|
|
- Gets a turret to go into manual mode and fire at a specific target, ignoring all other threats.
|
|
|
|
set_burst_parameters( n_fire_min, n_fire_max, n_wait_min, n_wait_max, n_index )
|
|
- Sets the burst parameters for a turret.
|
|
|
|
|
|
******************************************************************
|
|
Useful Misc Turret Functions
|
|
******************************************************************
|
|
|
|
set_turret_occupy_no_target_time( n_index )
|
|
- How long an AI will stay inside a turret if there is no target, default is 2.0 seconds.
|
|
|
|
set_turret_ignore_line_of_sight( b_ignore, n_index )
|
|
- set this turret to ignore line of sight
|
|
|
|
get_weapon( n_index )
|
|
- Gets the turrets weapon.
|
|
|
|
get_parent( n_index )
|
|
- Gets the turrets parent entity.
|
|
|
|
enable_laser( n_index, b_enable )
|
|
- Turns on the laser.
|
|
|
|
enable_emp( n_index, b_enable )
|
|
- Allows this turret to be emped which shuts it down and turns of the laser if enabled.
|
|
|
|
NOTIFY: "terminate_all_turrets_firing" Use this notify on a vehicle to immediately stop any burst firing loops it may have running.
|
|
|
|
|
|
******************************************************************
|
|
Script NOTIFIES
|
|
******************************************************************
|
|
|
|
TURRET
|
|
"turret_enabled" - When the turret has been enabled.
|
|
"turret_disabled" - When the durret has been disabled
|
|
"user_using_turret" - Sent out when the ai_user first gets on the turret.
|
|
"_stop_turret" - Sent out when a turret is stops firint at its latest target.
|
|
"turret_target_out_of_range" - Sent when the target of a turret moves out of range.
|
|
"target_array_destroyed" - Send out when a target array has bee cleared
|
|
"shooting" - Sent out bu turret when it starts shooting
|
|
"turret_on_target" - Sent out when the turret has a target in its sights
|
|
"gunner_turret_on_target" - Sent out when a turrets gunner has a target in its sights
|
|
"idle" - Sent out when the turret has no targets in sight
|
|
|
|
----------------------------------------------------------------------------------------------------- */
|
|
|
|
/*-----------------------------------------------------------------------------------------------------
|
|
Turret Struct Data
|
|
|
|
is_enabled
|
|
w_weapon
|
|
str_team
|
|
e_parent
|
|
e_target
|
|
ai_user
|
|
e_next_target
|
|
n_target_flags
|
|
n_burst_fire_min
|
|
n_burst_fire_max
|
|
n_burst_wait_min
|
|
n_burst_wait_max
|
|
|
|
-----------------------------------------------------------------------------------------------------*/
|
|
|
|
// The amount of time a turret will wait with no target before calling _drop_turret()
|
|
// dropping the turret causes the user to exit the turret
|
|
|
|
|
|
function __init__()
|
|
{
|
|
clientfield::register( "vehicle", "toggle_lensflare", 1, 1, "int" );
|
|
|
|
level._turrets = SpawnStruct();
|
|
}
|
|
|
|
/@
|
|
"Name: get_weapon( n_index )"
|
|
"Summary: Gets a turret's weapon"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: turret get_weapon();"
|
|
@/
|
|
function get_weapon( n_index = 0 )
|
|
{
|
|
w_weapon = self SeatGetWeapon( n_index );
|
|
return w_weapon;
|
|
}
|
|
|
|
/@
|
|
"Name: get_parent( n_index )"
|
|
"Summary: Gets a turret's parent"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: turret get_parent( n_index );"
|
|
@/
|
|
function get_parent( n_index )
|
|
{
|
|
return _get_turret_data( n_index ).e_parent;
|
|
}
|
|
|
|
function laser_death_watcher()
|
|
{
|
|
self notify( "laser_death_thread_stop" );
|
|
self endon( "laser_death_thread_stop" );
|
|
self waittill( "death" );
|
|
if(isDefined(self))
|
|
{
|
|
self LaserOff();
|
|
}
|
|
}
|
|
|
|
function enable_laser( b_enable, n_index )
|
|
{
|
|
if ( b_enable )
|
|
{
|
|
_get_turret_data( n_index ).has_laser = true;
|
|
self LaserOn();
|
|
self thread laser_death_watcher();
|
|
}
|
|
else
|
|
{
|
|
_get_turret_data( n_index ).has_laser = undefined;
|
|
self LaserOff();
|
|
self notify( "laser_death_thread_stop" );
|
|
}
|
|
}
|
|
|
|
function watch_for_flash()
|
|
{
|
|
self endon( "watch_for_flash_and_stun" );
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "flashbang", pct_dist, pct_angle, attacker, team );
|
|
self notify( "damage", 1, attacker, undefined, undefined, undefined, undefined, undefined, undefined, "flash_grenade" );
|
|
}
|
|
}
|
|
|
|
function watch_for_flash_and_stun( n_index )
|
|
{
|
|
self notify( "watch_for_flash_and_stun_end" );
|
|
self endon( "watch_for_flash_and_stun" );
|
|
self endon( "death" );
|
|
|
|
self thread watch_for_flash();
|
|
|
|
while ( true )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction, point, type, tagName, modelName, partname, weapon );
|
|
|
|
if ( weapon.doStun )
|
|
{
|
|
if ( isdefined( self.stunned ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
self.stunned = true;
|
|
|
|
stop( n_index, true );
|
|
|
|
wait RandomFloatRange( 5, 7 );
|
|
|
|
self.stunned = undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
function emp_watcher( n_index )
|
|
{
|
|
self notify( "emp_thread_stop" );
|
|
self endon( "emp_thread_stop" );
|
|
self endon( "death" );
|
|
|
|
while ( true )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction, point, type, tagName, modelName, partname, weapon );
|
|
|
|
if ( weapon.isEmp )
|
|
{
|
|
if ( isdefined( self.emped ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
self.emped = true;
|
|
|
|
if ( isdefined( _get_turret_data( n_index ).has_laser ) )
|
|
{
|
|
self LaserOff();
|
|
}
|
|
|
|
stop( n_index, true );
|
|
|
|
wait RandomFloatRange( 5, 7 );
|
|
|
|
self.emped = undefined;
|
|
|
|
if ( isdefined( _get_turret_data( n_index ).has_laser ) )
|
|
{
|
|
self LaserOn();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function enable_emp( b_enable, n_index )
|
|
{
|
|
if ( b_enable )
|
|
{
|
|
_get_turret_data( n_index ).can_emp = true;
|
|
self thread emp_watcher( n_index );
|
|
self.takedamage = true;
|
|
}
|
|
else
|
|
{
|
|
_get_turret_data( n_index ).can_emp = undefined;
|
|
self notify( "emp_thread_stop" );
|
|
}
|
|
}
|
|
|
|
function set_team( str_team, n_index )
|
|
{
|
|
_get_turret_data( n_index ).str_team = str_team;
|
|
|
|
// set the internal code team
|
|
self.team = str_team;
|
|
}
|
|
|
|
function get_team( n_index )
|
|
{
|
|
str_team = undefined;
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
str_team = self.team;
|
|
|
|
if ( !isdefined( s_turret.str_team ) )
|
|
{
|
|
s_turret.str_team = str_team;
|
|
}
|
|
|
|
return str_team;
|
|
}
|
|
|
|
/@
|
|
"Name: is_turret_enabled( n_index )"
|
|
"Summary: Checks to see if a turret is enabled"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: if ( is_turret_enabled( n_index ) );"
|
|
@/
|
|
function is_turret_enabled( n_index )
|
|
{
|
|
return _get_turret_data( n_index ).is_enabled;
|
|
}
|
|
|
|
/@
|
|
"Name: does_need_user( [n_index] )"
|
|
"Summary: Checks to see if a turret needs a user"
|
|
"Module: Turret"
|
|
"OptionalArg: [n_index]: Index of the turret for a vehicle turret"
|
|
"Example: if ( does_need_user( n_index ) );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function does_need_user( n_index )
|
|
{
|
|
return ( isdefined( _get_turret_data( n_index ).b_needs_user ) && _get_turret_data( n_index ).b_needs_user );
|
|
}
|
|
|
|
/@
|
|
"Name: does_have_user( n_index )"
|
|
"Summary: Checks to see if a turret has a user"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: if ( turret turret::does_have_user( n_index ) );"
|
|
@/
|
|
function does_have_user( n_index )
|
|
{
|
|
return IsAlive( get_user( n_index ) );
|
|
}
|
|
|
|
/@
|
|
"Name: get_user( n_index )"
|
|
"Summary: Gets the user of this turret"
|
|
|
|
"OptionalArg: [n_index]: Index of the turret for a vehicle turret"
|
|
|
|
"Example: ai_user = turret::get_user( n_index );"
|
|
@/
|
|
|
|
function get_user( n_index )
|
|
{
|
|
return ( self GetSeatOccupant( n_index ) );
|
|
}
|
|
|
|
function _set_turret_needs_user( n_index, b_needs_user )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
if ( b_needs_user )
|
|
{
|
|
s_turret.b_needs_user = true;
|
|
self thread watch_for_flash_and_stun( n_index );
|
|
}
|
|
else
|
|
{
|
|
self notify( "watch_for_flash_and_stun_end" );
|
|
s_turret.b_needs_user = false;
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: set_target_ent_array( a_entities, n_index )"
|
|
"Summary: Sends a list of targets for the turret. NOTE - Sends notify when targets are all dead"
|
|
|
|
"MandatoryArg: <a_targets> The Array of Targets you want the turret to destroy"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: set_target_ent_array( a_ents );"
|
|
@/
|
|
|
|
// self = turret or vehicle
|
|
function set_target_ent_array( a_ents, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.priority_target_array = a_ents;
|
|
}
|
|
|
|
/@
|
|
"Name: add_priority_target( ent_or_ent_array, n_index )"
|
|
"Summary: Adds a single ent or array of ents to be a priority target of a turret"
|
|
|
|
"MandatoryArg: <ent_or_ent_array> The ent or array of entity targets the turret should attack"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: claw_turret add_priority_target( a_balcony_guys )"
|
|
@/
|
|
function add_priority_target( ent_or_ent_array, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
// priority_target_array should always take an array
|
|
if ( !IsArray( ent_or_ent_array ) ) // single ent
|
|
{
|
|
a_new_targets = [];
|
|
a_new_targets[ 0 ] = ent_or_ent_array;
|
|
}
|
|
else // array
|
|
{
|
|
a_new_targets = ent_or_ent_array;
|
|
}
|
|
|
|
// add new targets to existing priority_target_array
|
|
if ( isdefined( s_turret.priority_target_array ) )
|
|
{
|
|
a_new_targets = ArrayCombine( s_turret.priority_target_array, a_new_targets, true, false );
|
|
}
|
|
|
|
s_turret.priority_target_array = a_new_targets;
|
|
}
|
|
|
|
/@
|
|
"Name: clear_target_ent_array( n_index )"
|
|
"Summary: Clears the turrets target array"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: clear_target_ent_array();"
|
|
@/
|
|
|
|
function clear_target_ent_array( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.priority_target_array = undefined;
|
|
}
|
|
|
|
/@
|
|
"Name: set_ignore_ent_array( a_ents, n_index )"
|
|
"Summary: Sends a list of targets for the turret to ignore."
|
|
|
|
"MandatoryArg: <a_ents> The Array of Targets you want the turret to IGNORE"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: set_ignore_ent_array( a_ents );"
|
|
@/
|
|
|
|
// self = turret or vehicle
|
|
function set_ignore_ent_array( a_ents, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.a_ignore_target_array = a_ents;
|
|
}
|
|
|
|
/@
|
|
"Name: clear_ignore_ent_array( n_index )"
|
|
"Summary: Clears the turrets target array"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: clear_ignore_ent_array();"
|
|
@/
|
|
|
|
function clear_ignore_ent_array( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.a_ignore_target_array = undefined;
|
|
}
|
|
|
|
function _wait_for_current_user_to_finish( n_index )
|
|
{
|
|
self endon( "death" );
|
|
|
|
while ( IsAlive( get_user( n_index ) ) )
|
|
{
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: is_current_user( ai_user, n_index )"
|
|
"Summary: Checks to see if an AI is the current user"
|
|
|
|
"MandatoryArg: <ai_user> The user of this turret"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: if ( is_current_user( ai_user, n_index ) );"
|
|
@/
|
|
function is_current_user( ai_user, n_index )
|
|
{
|
|
ai_current_user = get_user( n_index );
|
|
return ( IsAlive( ai_current_user ) && ( ai_user == ai_current_user ) );
|
|
}
|
|
|
|
/@
|
|
"Name: set_burst_parameters( n_fire_min, n_fire_max, n_wait_min, n_wait_max, n_index )"
|
|
"Summary: Sets the burst parameters for a turret"
|
|
|
|
"MandatoryArg: <n_fire_min> The minimum time to fire the turret for"
|
|
"MandatoryArg: <n_fire_max> The maximum time to fire the turret for"
|
|
"MandatoryArg: <n_wait_min> The minimum time to wait between firing"
|
|
"MandatoryArg: <n_wait_max> The maximum time to wait between firing"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: turret set_burst_parameters( 1, 2, 3, 4 ); // slow burst firing"
|
|
@/
|
|
function set_burst_parameters( n_fire_min, n_fire_max, n_wait_min, n_wait_max, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.n_burst_fire_min = n_fire_min;
|
|
s_turret.n_burst_fire_max = n_fire_max;
|
|
s_turret.n_burst_wait_min = n_wait_min;
|
|
s_turret.n_burst_wait_max = n_wait_max;
|
|
}
|
|
|
|
// For use against human targets
|
|
function set_torso_targetting( n_index, n_torso_targetting_offset = ( -12 ) )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.n_torso_targetting_offset = n_torso_targetting_offset;
|
|
}
|
|
|
|
// For use against human targets
|
|
function set_target_leading( n_index, n_target_leading_factor = 0.1 )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.n_target_leading_factor = n_target_leading_factor;
|
|
}
|
|
|
|
|
|
/@
|
|
"Name: set_on_target_angle( n_angle, n_index )"
|
|
"Summary: Sets the angle at which the turret will be on target"
|
|
|
|
"MandatoryArg: <n_angle> angle at which the turret will be on target"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: turret set_on_target_angle( 1, 5 ); // on target at 5 degrees from target position"
|
|
@/
|
|
function set_on_target_angle( n_angle, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
if ( !isdefined( n_angle ) )
|
|
{
|
|
if ( s_turret.str_guidance_type != "none" )
|
|
{
|
|
n_angle = 10;
|
|
}
|
|
else
|
|
{
|
|
n_angle = 2;
|
|
}
|
|
}
|
|
|
|
if ( n_index > 0 )
|
|
{
|
|
self SetOnTargetAngle( n_angle, n_index - 1 );
|
|
}
|
|
else
|
|
{
|
|
self SetOnTargetAngle( n_angle );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: set_target( e_target, v_offset, n_index )"
|
|
"Summary: Sets the target of this turret"
|
|
|
|
"MandatoryArg: <e_target> The target of this turret"
|
|
"OptionalArg: [v_offset] Offset from the turret target"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret set_target( e_target, v_offset, n_index );"
|
|
@/
|
|
function set_target( e_target, v_offset, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
if ( !isdefined( v_offset ) )
|
|
{
|
|
v_offset = _get_default_target_offset( e_target, n_index );
|
|
}
|
|
|
|
if ( !isdefined( n_index ) || n_index == 0 )
|
|
{
|
|
self SetTargetEntity( e_target, v_offset );
|
|
}
|
|
else
|
|
{
|
|
self SetTargetEntity( e_target, v_offset, n_index - 1 );
|
|
}
|
|
|
|
s_turret.e_target = e_target;
|
|
s_turret.e_last_target = e_target;
|
|
s_turret.v_offset = v_offset;
|
|
}
|
|
|
|
function _get_default_target_offset( e_target, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
if ( s_turret.str_weapon_type == "bullet" )
|
|
{
|
|
if ( isdefined( e_target ) )
|
|
{
|
|
if ( IsSentient( self ) && IsSentient( e_target ) )
|
|
{
|
|
z_offset = ( IsVehicle( e_target ) ? 0 : (isdefined(s_turret.n_torso_targetting_offset)?s_turret.n_torso_targetting_offset:0) ); // if both sentients, the code will handle the offset intelligently
|
|
}
|
|
else if ( IsPlayer( e_target ) )
|
|
{
|
|
z_offset = RandomIntRange( 40, 50 );
|
|
}
|
|
else if ( ( e_target.type === "human" ) )
|
|
{
|
|
z_offset = RandomIntRange( 20, 60 );
|
|
}
|
|
else if ( ( e_target.type === "robot" ) )
|
|
{
|
|
z_offset = RandomIntRange( 40, 60 );
|
|
}
|
|
|
|
if ( isdefined(e_target.z_target_offset_override) )
|
|
{
|
|
if( !isdefined(z_offset) )
|
|
{
|
|
z_offset = 0;
|
|
}
|
|
z_offset += e_target.z_target_offset_override;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!isdefined(z_offset))z_offset=0;
|
|
v_offset = ( 0, 0, z_offset );
|
|
|
|
// Apply target leading against human targets
|
|
if ( (isdefined(s_turret.n_target_leading_factor)?s_turret.n_target_leading_factor:0) != 0 && isdefined( e_target ) && IsSentient( self ) && IsSentient( e_target ) && !IsVehicle( e_target ) )
|
|
{
|
|
velocity = e_target GetVelocity();
|
|
v_offset += velocity * s_turret.n_target_leading_factor;
|
|
}
|
|
|
|
return v_offset;
|
|
}
|
|
|
|
/@
|
|
"Name: get_target( n_index )"
|
|
"Summary: Returns turret target data for this turret"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: has_target = isdefined( get_target( n_index ) );"
|
|
@/
|
|
function get_target( n_index )
|
|
{
|
|
if(isDefined(_get_turret_data( n_index ).e_target) && ( isdefined( _get_turret_data( n_index ).e_target.ignoreme ) && _get_turret_data( n_index ).e_target.ignoreme ))
|
|
{
|
|
clear_target( n_index );
|
|
}
|
|
return _get_turret_data( n_index ).e_target;
|
|
}
|
|
|
|
function is_target( e_target, n_index )
|
|
{
|
|
e_current_target = get_target( n_index );
|
|
|
|
if ( isdefined( e_current_target ) )
|
|
{
|
|
return ( e_current_target == e_target );
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/@
|
|
"Name: clear_target( n_index )"
|
|
"Summary: Clears turret target data for this turret"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret clear_target( n_index );"
|
|
@/
|
|
function clear_target( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret flag::clear( "turret manual" );
|
|
|
|
s_turret.e_next_target = undefined;
|
|
s_turret.e_target = undefined;
|
|
|
|
if ( !isdefined( n_index ) || ( n_index == 0 ) )
|
|
{
|
|
self ClearTurretTarget();
|
|
}
|
|
else
|
|
{
|
|
self ClearGunnerTarget( n_index - 1 );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: set_target_flags( n_flags, n_index )"
|
|
"Summary: Sets the flags for the types of targets the turret should pick"
|
|
|
|
"MandatoryArg: <n_flags> The types of targets - currently supports TURRET_TARGET_AI, TURRET_TARGET_PLAYERS, TURRET_TARGET_DRONES, TURRET_TARGET_VEHICLES"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_bunker_turret set_target_flags( TURRET_TARGET_AI | TURRET_TARGET_PLAYERS, n_index );"
|
|
@/
|
|
function set_target_flags( n_flags, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.n_target_flags = n_flags;
|
|
}
|
|
|
|
function _has_target_flags( n_flags, n_index )
|
|
{
|
|
n_current_flags = _get_turret_data( n_index ).n_target_flags;
|
|
return ( ( n_current_flags & n_flags ) == n_flags);
|
|
}
|
|
|
|
|
|
/@
|
|
"Name: set_max_target_distance( n_distance )"
|
|
"Summary: Sets the maximum distance a target can be, ( NOTE: 0 or undefined == unlimited distance)"
|
|
|
|
"MandatoryArg: <n_distance> The maximum distance a target can be, ( NOTE: 0 or undefined == unlimited distance )"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_vehicle set_max_target_distance( 2048, 1 );"
|
|
@/
|
|
function set_max_target_distance( n_distance, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.n_max_target_distance_squared = n_distance * n_distance;
|
|
}
|
|
|
|
/@
|
|
"Name: set_min_target_distance( n_distance )"
|
|
"Summary: Sets the minimum distance a target can be"
|
|
|
|
"MandatoryArg: <n_distance> The minimum distance a target can be, below which is not considered a target ( NOTE: undefined == zero distance )"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_vehicle set_min_target_distance( 18, 1 );"
|
|
@/
|
|
function set_min_target_distance( n_distance, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.n_min_target_distance_squared = n_distance * n_distance;
|
|
}
|
|
|
|
/@
|
|
"Name: set_min_target_distance_squared( n_distance )"
|
|
"Summary: Sets the minimum distance squared a target can be"
|
|
|
|
"MandatoryArg: <n_distance> The minimum distance squared a target can be, below which is not considered a target ( NOTE: undefined == zero distance )"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_vehicle set_min_target_distance_squared( DistanceSquared( e_vehicle GetTagOrigin( "tag_flash" ), e_vehicle GetTagOrigin( "tag_barrel" ) ), 0 );"
|
|
@/
|
|
function set_min_target_distance_squared( n_distance_squared, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.n_min_target_distance_squared = n_distance_squared;
|
|
}
|
|
|
|
/@
|
|
"Name: fire( n_index )"
|
|
"Summary: Fires a turret"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret fire();"
|
|
@/
|
|
function fire( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
assert( isdefined( n_index ) && ( n_index >= 0 ), "Invalid index specified to fire vehicle turret." );
|
|
|
|
if ( n_index == 0 )
|
|
{
|
|
self FireWeapon( 0, s_turret.e_target );
|
|
}
|
|
else
|
|
{
|
|
ai_current_user = get_user( n_index );
|
|
if(isdefined(ai_current_user) && ( isdefined( ai_current_user.is_disabled ) && ai_current_user.is_disabled ))
|
|
return;
|
|
|
|
if ( isdefined( s_turret.e_target ) )
|
|
{
|
|
self SetGunnerTargetEnt( s_turret.e_target, s_turret.v_offset, n_index - 1 );
|
|
}
|
|
|
|
self FireWeapon( n_index, s_turret.e_target, s_turret.v_offset, s_turret.e_parent );
|
|
}
|
|
|
|
s_turret.n_last_fire_time = GetTime();
|
|
}
|
|
|
|
/@
|
|
"Name: stop( n_index, b_clear_target )"
|
|
"Summary: Stops a turret"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
"OptionalArg: [b_clear_target] Clears the turret target. default true."
|
|
|
|
"Example: e_turret stop();"
|
|
@/
|
|
function stop( n_index, b_clear_target = false )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
s_turret.e_next_target = undefined;
|
|
s_turret.e_target = undefined;
|
|
|
|
s_turret flag::clear( "turret manual" );
|
|
|
|
if ( b_clear_target )
|
|
{
|
|
clear_target( n_index );
|
|
}
|
|
|
|
self notify( "_stop_turret" + _index( n_index ) );
|
|
}
|
|
|
|
/@
|
|
"Name: fire_for_time( n_time , n_index )"
|
|
"Summary: Fires a turret for a time, blocks for n_time"
|
|
|
|
"MandatoryArg: <n_time> The time to fire the turret for"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret fire_for_time( 3 );"
|
|
@/
|
|
function fire_for_time( n_time, n_index = 0 )
|
|
{
|
|
Assert( isdefined( n_time ), "n_time is a required parameter for _turet::fire_for_time." );
|
|
|
|
self endon( "death" );
|
|
self endon( "drone_death" );
|
|
|
|
self endon( "_stop_turret" + _index( n_index ) );
|
|
// MikeA: 4/18/11
|
|
self endon( "turret_disabled" + _index( n_index ) );
|
|
|
|
/* only one of these threads at a time */
|
|
|
|
self notify( "_fire_turret_for_time" + _index( n_index ) );
|
|
self endon( "_fire_turret_for_time" + _index( n_index ) );
|
|
|
|
b_fire_forever = false;
|
|
if ( n_time < 0)
|
|
{
|
|
b_fire_forever = true;
|
|
}
|
|
else
|
|
{
|
|
/#
|
|
w_weapon = get_weapon( n_index );
|
|
assert( n_time >= w_weapon.fireTime, "Fire time (" + n_time + ") must be greater than the weapon's fire time. weapon fire time = " + w_weapon.fireTime );
|
|
#/
|
|
}
|
|
|
|
while ( ( n_time > 0 ) || b_fire_forever )
|
|
{
|
|
//IPrintLnBold( "BURST FIRE START LOOP" );
|
|
|
|
n_burst_time = _burst_fire( n_time, n_index );
|
|
|
|
if ( !b_fire_forever )
|
|
{
|
|
n_time -= n_burst_time;
|
|
}
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: shoot_at_target( e_target, n_time, v_offset, n_index )"
|
|
"Summary: Fires turret at given target"
|
|
|
|
"MandatoryArg: <e_target> The target of this turret"
|
|
"MandatoryArg: <n_time> The time to fire the turret for"
|
|
"OptionalArg: [v_offset] Offset from the turret target"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
"OptionalArg: [b_just_once] Ignore the time parameter and just fire once"
|
|
|
|
"Example: self thread shoot_at_target( e_best_target, -1, undefined, n_index );"
|
|
@/
|
|
|
|
// self = turret or vehicle
|
|
function shoot_at_target( e_target, n_time, v_offset, n_index, b_just_once )
|
|
{
|
|
Assert( isdefined( e_target ), "Undefined target passed to shoot_at_target()." );
|
|
|
|
self endon( "drone_death" );
|
|
self endon( "death" );
|
|
|
|
// Manual mode will stop the turret searching for new targets
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret flag::set( "turret manual" );
|
|
|
|
_shoot_turret_at_target( e_target, n_time, v_offset, n_index, b_just_once );
|
|
|
|
s_turret flag::clear( "turret manual" );
|
|
}
|
|
|
|
function _shoot_turret_at_target( e_target, n_time, v_offset, n_index, b_just_once )
|
|
{
|
|
self endon( "drone_death" );
|
|
self endon( "death" );
|
|
self endon( "_stop_turret" + _index( n_index ) );
|
|
self endon( "turret_disabled" + _index( n_index ) );
|
|
|
|
// only one of these threads at a time
|
|
self notify( "_shoot_turret_at_target" + _index( n_index ) );
|
|
self endon( "_shoot_turret_at_target" + _index( n_index ) );
|
|
|
|
if ( n_time == -1 )
|
|
{
|
|
e_target endon( "death" );
|
|
}
|
|
|
|
if ( !isdefined( b_just_once ) )
|
|
{
|
|
b_just_once = false;
|
|
}
|
|
|
|
set_target( e_target, v_offset, n_index );
|
|
|
|
if ( !isdefined( self.aim_only_no_shooting ) )
|
|
{
|
|
_waittill_turret_on_target( e_target, n_index );
|
|
|
|
if ( b_just_once )
|
|
{
|
|
turret::fire( n_index );
|
|
}
|
|
else
|
|
{
|
|
fire_for_time( n_time, n_index );
|
|
}
|
|
}
|
|
}
|
|
|
|
function _waittill_turret_on_target( e_target, n_index )
|
|
{
|
|
do
|
|
{
|
|
wait (isdefined(self.waittill_turret_on_target_delay)?self.waittill_turret_on_target_delay:0.5);
|
|
if ( !isdefined( n_index ) || ( n_index == 0 ) )
|
|
{
|
|
self waittill( "turret_on_target" );
|
|
}
|
|
else
|
|
{
|
|
self waittill( "gunner_turret_on_target" );
|
|
}
|
|
}
|
|
while ( isdefined( e_target ) && !can_hit_target( e_target, n_index ) );
|
|
}
|
|
|
|
/@
|
|
"Name: shoot_at_target_once( e_target, v_offset, n_index )"
|
|
"Summary: Fires turret at given target once"
|
|
|
|
"MandatoryArg: <e_target> The target of this turret"
|
|
"OptionalArg: [v_offset] Offset from the turret target"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: self thread shoot_at_target_once( e_best_target, undefined, n_index );"
|
|
@/
|
|
function shoot_at_target_once( e_target, v_offset, n_index )
|
|
{
|
|
shoot_at_target( e_target, 0, v_offset, n_index, true );
|
|
}
|
|
|
|
/@
|
|
"Name: enable( n_index, b_user_required, v_offset )"
|
|
"Summary: Enables the given turret"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
"OptionalArg: [b_user_required] Determines if the turret require a user to fire"
|
|
"OptionalArg: [v_offset] Offset from the turret target"
|
|
|
|
"Example: e_turret turret::enable( 1 ); // enable gunner1"
|
|
@/
|
|
function enable( n_index, b_user_required, v_offset )
|
|
{
|
|
if ( isAlive(self) && !is_turret_enabled( n_index ) )
|
|
{
|
|
_get_turret_data( n_index ).is_enabled = true;
|
|
self thread _turret_think( n_index, v_offset );
|
|
self notify( "turret_enabled" + _index( n_index ) );
|
|
|
|
if ( isdefined( b_user_required ) && ( !b_user_required ) )
|
|
{
|
|
_set_turret_needs_user( n_index, false );
|
|
}
|
|
else
|
|
{
|
|
_set_turret_needs_user( n_index, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: enable_auto_use( b_enable = true )"
|
|
"Summary: Enables a vehicle to automatically get AI turret users"
|
|
|
|
"CallOn: vehicle/turret"
|
|
"OptionalArg: [b_enable] Set to 'false' to turn off, 'true' to turn on (default)."
|
|
|
|
"Example: vh_truck turret::enable_auto_use( true )"
|
|
@/
|
|
function enable_auto_use( b_enable = true )
|
|
{
|
|
self.script_auto_use = b_enable;
|
|
}
|
|
|
|
/@
|
|
"Name: disable_ai_getoff( n_index, b_disable = true )"
|
|
"Summary: Disable AIrom getting off the turret of lost sight of the player"
|
|
|
|
"CallOn: turret"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
"OptionalArg: [b_disable] Set to 'true' to turn on (default), 'false' to turn off"
|
|
|
|
"Example: e_turret turret::disable_ai_getoff( 1, true )"
|
|
@/
|
|
function disable_ai_getoff( n_index, b_disable = true )
|
|
{
|
|
_get_turret_data( n_index ).disable_ai_getoff = b_disable;
|
|
}
|
|
|
|
/@
|
|
"Name: disable( n_index )"
|
|
"Summary: Disables the given turret, But it keeps its targetted information"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret turret::disable();"
|
|
@/
|
|
function disable( n_index )
|
|
{
|
|
if ( is_turret_enabled( n_index ) )
|
|
{
|
|
_drop_turret( n_index );
|
|
|
|
// MikeA: 4/18/11
|
|
clear_target( n_index );
|
|
|
|
_get_turret_data( n_index ).is_enabled = false;
|
|
|
|
self notify( "turret_disabled" + _index( n_index ) );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: pause( time, n_index )"
|
|
"Summary: Disables the given turret, But it keeps its targetted information"
|
|
|
|
"MandatoryArg: <time> Time to pause turret for, 0 for infinite"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret turret::pause( 0 );"
|
|
@/
|
|
|
|
function pause( time, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
// Convert time to Milli Seconds
|
|
if ( time > 0 )
|
|
{
|
|
time = time * 1000;
|
|
}
|
|
|
|
// If already paused, just add on the extra time
|
|
if ( isdefined( s_turret.pause ) )
|
|
{
|
|
s_turret.pause_time = s_turret.pause_time + time;
|
|
}
|
|
else
|
|
{
|
|
s_turret.pause = 1;
|
|
s_turret.pause_time = time;
|
|
stop( n_index );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: unpause( n_index )"
|
|
"Summary: Disables the given turret, But it keeps its targetted information"
|
|
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
"Example: e_turret turret::unpause();"
|
|
@/
|
|
|
|
function unpause( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.pause = undefined;
|
|
}
|
|
|
|
|
|
|
|
// self = vehicle or turret
|
|
function _turret_think( n_index, v_offset )
|
|
{
|
|
TURRET_THINK_TIME = Max( 1.5, get_weapon( n_index ).fireTime );
|
|
|
|
no_target_start_time = 0; // Time when turret last had a target
|
|
|
|
self endon( "death" );
|
|
self endon( "turret_disabled" + _index( n_index ) );
|
|
|
|
/* only one think thread at a time */
|
|
|
|
self notify( "_turret_think" + _index( n_index ) );
|
|
self endon( "_turret_think" + _index( n_index ) );
|
|
|
|
/# self thread _debug_turret_think( n_index ); #/
|
|
|
|
self thread _turret_user_think( n_index );
|
|
|
|
self thread _turret_new_user_think( n_index );
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
if ( isdefined( s_turret.has_laser ) )
|
|
{
|
|
self LaserOn();
|
|
}
|
|
|
|
while ( true )
|
|
{
|
|
s_turret flag::wait_till_clear( "turret manual" );
|
|
|
|
n_time_now = GetTime();
|
|
|
|
// Do we want to pause the turret?
|
|
if ( self _check_for_paused( n_index ) || isdefined( self.emped ) || isdefined( self.stunned ) )
|
|
{
|
|
wait TURRET_THINK_TIME;
|
|
continue;
|
|
}
|
|
|
|
a_potential_targets = _get_potential_targets( n_index );
|
|
|
|
if ( !isdefined( s_turret.e_target )
|
|
|| ( s_turret.e_target.health < 0 )
|
|
|| !IsInArray( a_potential_targets, s_turret.e_target )
|
|
|| s_turret _did_turret_lose_target( n_time_now ) )
|
|
{
|
|
stop( n_index );
|
|
}
|
|
|
|
e_original_next_target = s_turret.e_next_target;
|
|
|
|
// The turret picks the best candidate from the target array
|
|
s_turret.e_next_target = _get_best_target_from_potential( a_potential_targets, n_index );
|
|
|
|
/* If we have a new target, shoot at it */
|
|
if ( isdefined( s_turret.e_next_target ) )
|
|
{
|
|
s_turret.b_target_out_of_range = undefined;
|
|
s_turret.n_time_lose_sight = undefined;
|
|
no_target_start_time = 0;
|
|
|
|
if ( _user_check( n_index ) )
|
|
{
|
|
self thread _shoot_turret_at_target( s_turret.e_next_target, TURRET_THINK_TIME, v_offset, n_index );
|
|
|
|
if ( s_turret.e_next_target !== e_original_next_target )
|
|
{
|
|
self notify( "has_new_target", s_turret.e_next_target );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !isdefined( self.do_not_clear_targets_during_think ) || !self.do_not_clear_targets_during_think )
|
|
clear_target( n_index ); // forget the old target if we don't have a next target
|
|
|
|
// If we've been waiting for as target for too long, drop the turret (user AI exits)
|
|
|
|
if ( no_target_start_time == 0 )
|
|
{
|
|
no_target_start_time = n_time_now;
|
|
}
|
|
|
|
// How long have we been waiting
|
|
target_wait_time = ( n_time_now - no_target_start_time );
|
|
|
|
// If we've been waitng for a target for too long, drop the turret
|
|
if ( isdefined(s_turret.occupy_no_target_time) )
|
|
{
|
|
occupy_time = s_turret.occupy_no_target_time;
|
|
}
|
|
else
|
|
{
|
|
occupy_time = 3600;
|
|
}
|
|
|
|
//Only leave the turret if it is automated or if the target is a player and we lost sight
|
|
if(!( isdefined( s_turret.disable_ai_getoff ) && s_turret.disable_ai_getoff ))
|
|
{
|
|
bWasPlayerTarget = isdefined( s_turret.e_last_target ) && ( s_turret.e_last_target.health > 0 ) && IsPlayer(s_turret.e_last_target);
|
|
|
|
if(bWasPlayerTarget)
|
|
occupy_time = occupy_time / 4;
|
|
}
|
|
else
|
|
{
|
|
bWasPlayerTarget = false;
|
|
}
|
|
|
|
if ( target_wait_time >= occupy_time )
|
|
{
|
|
_drop_turret( n_index, !bWasPlayerTarget );
|
|
}
|
|
}
|
|
|
|
if ( !( isdefined( s_turret.disable_ai_getoff ) && s_turret.disable_ai_getoff ) &&
|
|
_has_nearby_player_enemy( n_index, self ) )
|
|
{
|
|
_drop_turret( n_index, false );
|
|
}
|
|
|
|
wait TURRET_THINK_TIME;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
function _has_nearby_player_enemy( index, turret )
|
|
{
|
|
has_nearby_enemy = false;
|
|
time = GetTime();
|
|
|
|
ai_user = turret get_user( index );
|
|
|
|
if ( !IsDefined( ai_user ) )
|
|
{
|
|
return has_nearby_enemy;
|
|
}
|
|
|
|
if ( !IsDefined( turret.next_nearby_enemy_time ) )
|
|
{
|
|
turret.next_nearby_enemy_time = time;
|
|
}
|
|
|
|
if ( time >= turret.next_nearby_enemy_time )
|
|
{
|
|
players = GetPlayers();
|
|
|
|
foreach ( player in players )
|
|
{
|
|
if ( turret.team == player.team )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( Abs( ai_user.origin[2] - player.origin[2] ) <= 60 &&
|
|
Distance2DSquared( ai_user.origin, player.origin ) <= ( (300) * (300) ) )
|
|
{
|
|
has_nearby_enemy = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
turret.next_nearby_enemy_time = time + 1000;
|
|
}
|
|
|
|
return has_nearby_enemy;
|
|
}
|
|
|
|
// self = turret struct
|
|
function _did_turret_lose_target( n_time_now )
|
|
{
|
|
if ( ( isdefined( self.b_target_out_of_range ) && self.b_target_out_of_range ) )
|
|
{
|
|
return true;
|
|
}
|
|
else if ( isdefined( self.n_time_lose_sight ) )
|
|
{
|
|
return ( ( n_time_now - self.n_time_lose_sight ) > 3000 );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function _turret_user_think( n_index )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "turret_disabled" + _index( n_index ) );
|
|
self endon( "_turret_think" + _index( n_index ) );
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
ai_user = self GetSeatOccupant( n_index );
|
|
|
|
if ( IsActor( ai_user ) )
|
|
{
|
|
self thread _listen_for_damage_on_actor(ai_user, n_index);
|
|
}
|
|
|
|
while ( true )
|
|
{
|
|
_waittill_user_change( n_index );
|
|
if ( !_user_check( n_index ) )
|
|
{
|
|
stop( n_index, true );
|
|
}
|
|
else
|
|
{
|
|
ai_user = self GetSeatOccupant( n_index );
|
|
|
|
if ( IsActor( ai_user ) )
|
|
{
|
|
self thread _listen_for_damage_on_actor(ai_user, n_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function _listen_for_damage_on_actor(ai_user, n_index)
|
|
{
|
|
self endon( "death" );
|
|
ai_user endon( "death" );
|
|
self endon( "turret_disabled" + _index( n_index ) );
|
|
self endon( "_turret_think" + _index( n_index ) );
|
|
self endon( "exit_vehicle" );
|
|
|
|
while(true)
|
|
{
|
|
ai_user waittill( "damage", n_amount, e_attacker, v_org, v_dir, str_mod );
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
if(isdefined(s_turret))
|
|
{
|
|
if(!isdefined(s_turret.e_next_target) && !isdefined(s_turret.e_target))
|
|
{
|
|
s_turret.e_last_target = e_attacker;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function _waittill_user_change( n_index )
|
|
{
|
|
ai_user = self GetSeatOccupant( n_index );
|
|
|
|
if ( IsAlive( ai_user ) )
|
|
{
|
|
if ( IsActor( ai_user ) )
|
|
{
|
|
ai_user endon( "death" );
|
|
}
|
|
else if ( IsPlayer( ai_user ) )
|
|
{
|
|
self notify( "turret_disabled" + _index( n_index ) ); //turret was continuing to fire on its own
|
|
}
|
|
}
|
|
|
|
self util::waittill_either( "exit_vehicle", "enter_vehicle" );
|
|
}
|
|
|
|
// self = turret or vehicle
|
|
function _check_for_paused( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
s_turret.pause_start_time = GetTime();
|
|
|
|
while ( isdefined( s_turret.pause ) )
|
|
{
|
|
if ( s_turret.pause_time > 0 )
|
|
{
|
|
time = GetTime();
|
|
paused_time = time - s_turret.pause_start_time;
|
|
if( paused_time > s_turret.pause_time )
|
|
{
|
|
s_turret.pause = undefined;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
{wait(.05);};
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// self = vehicle or turret
|
|
function _drop_turret( n_index, bExitIfAutomatedOnly )
|
|
{
|
|
ai_user = get_user( n_index );
|
|
|
|
if ( IsAlive( ai_user ) && (( isdefined( ai_user.turret_auto_use ) && ai_user.turret_auto_use ) || (isdefined(bExitIfAutomatedOnly) && !bExitIfAutomatedOnly)) )
|
|
{ // only drop turret if auto use, not when scripter puts the AI in the vehicle
|
|
ai_user vehicle::get_out();
|
|
}
|
|
}
|
|
|
|
function _turret_new_user_think( n_index )
|
|
{
|
|
const NEW_USER_THINK_TIME = 3;
|
|
|
|
self endon( "death" );
|
|
self endon( "_turret_think" + _index( n_index ) );
|
|
self endon( "turret_disabled" + _index( n_index ) );
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
if( n_index == 0 )
|
|
{
|
|
str_gunner_pos = "driver";
|
|
}
|
|
else
|
|
{
|
|
str_gunner_pos = "gunner" + n_index;
|
|
}
|
|
|
|
|
|
while ( true )
|
|
{
|
|
wait NEW_USER_THINK_TIME;
|
|
|
|
if ( does_have_target( n_index ) && !_user_check( n_index ) && ( isdefined( self.script_auto_use ) && self.script_auto_use ) )
|
|
{
|
|
str_team = get_team( n_index );
|
|
a_users = GetAIArchetypeArray( "human" , str_team );
|
|
a_ai_by_vehicle = ArraySortClosest( GetAIArray(), self.origin, 99999, 0, 300 );
|
|
|
|
if ( a_users.size > 0 )
|
|
{
|
|
a_potential_users = [];
|
|
if ( isdefined( self.script_auto_use_radius ) )
|
|
{
|
|
a_potential_users = ArraySort( a_users, self.origin, true, a_potential_users.size, self.script_auto_use_radius );
|
|
}
|
|
else
|
|
{
|
|
a_potential_users = ArraySort( a_users, self.origin, true );
|
|
}
|
|
|
|
ai_user = undefined;
|
|
foreach ( ai in a_potential_users )
|
|
{
|
|
b_enemy_close = false;
|
|
foreach ( ai_enemy in a_ai_by_vehicle )
|
|
{
|
|
if ( ai_enemy.team != ai.team )
|
|
{
|
|
b_enemy_close = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( b_enemy_close )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( ai flagsys::get( "vehiclerider" ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( !ai vehicle::can_get_in( self, str_gunner_pos ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ai_user = ai;
|
|
break;
|
|
}
|
|
|
|
if ( IsAlive( ai_user ) )
|
|
{
|
|
ai_user.turret_auto_use = true;
|
|
ai_user vehicle::get_in( self, str_gunner_pos );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function does_have_target( n_index )
|
|
{
|
|
return isdefined( _get_turret_data( n_index ).e_next_target );
|
|
}
|
|
|
|
function _user_check( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
if ( does_need_user( n_index ) )
|
|
{
|
|
b_has_user = does_have_user( n_index );
|
|
return b_has_user;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/#
|
|
|
|
function _debug_turret_think( n_index )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "_turret_think" + _index( n_index ) );
|
|
self endon( "turret_disabled" + _index( n_index ) );
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
v_color = ( 0, 0, 1 );
|
|
|
|
while ( true )
|
|
{
|
|
if ( !GetDvarint( "g_debugTurrets") )
|
|
{
|
|
wait(0.2);
|
|
continue;
|
|
}
|
|
|
|
has_target = isdefined( get_target( n_index ) );
|
|
|
|
if ( ( does_need_user( n_index ) && !does_have_user( n_index ) )
|
|
|| !has_target )
|
|
{
|
|
v_color = ( 1, 1, 0 );
|
|
}
|
|
else
|
|
{
|
|
v_color = ( 0, 1, 0 );
|
|
}
|
|
|
|
str_team = get_team( n_index );
|
|
if ( !isdefined( str_team ) )
|
|
{
|
|
str_team = "no team";
|
|
}
|
|
|
|
str_target = "target > ";
|
|
|
|
e_target = s_turret.e_next_target;
|
|
if ( isdefined( e_target ) )
|
|
{
|
|
if ( IsActor( e_target ) )
|
|
{
|
|
str_target += "ai";
|
|
}
|
|
else if ( IsPlayer( e_target ) )
|
|
{
|
|
str_target += "player";
|
|
}
|
|
else if ( IsVehicle( e_target ) )
|
|
{
|
|
str_target += "vehicle";
|
|
}
|
|
else if ( isdefined( e_target.targetname ) && ( e_target.targetname == "drone" ) )
|
|
{
|
|
str_target += "drone";
|
|
}
|
|
else if ( isdefined( e_target.classname ) )
|
|
{
|
|
str_target += e_target.classname;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
str_target += "none";
|
|
}
|
|
|
|
str_debug = self GetEntNum() + ":" + str_team + ":" + str_target;
|
|
Record3DText( str_debug, self.origin, v_color, "Script", self );
|
|
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
#/
|
|
|
|
/*===================================================================================================
|
|
SYSTEM FUNCTIONS
|
|
===================================================================================================*/
|
|
|
|
/*===================================================================
|
|
SELF: turret or vehicle
|
|
PURPOSE: Get the data structure holding the turret info
|
|
RETURNS: The turret data struct
|
|
[n_index]: optional index of vehicle turret if self is a vehicle
|
|
===================================================================*/
|
|
function _get_turret_data( n_index )
|
|
{
|
|
s_turret = undefined;
|
|
|
|
if ( IsVehicle( self ) )
|
|
{
|
|
if ( isdefined( self.a_turrets ) && isdefined( self.a_turrets[ n_index ] ) )
|
|
{
|
|
s_turret = self.a_turrets[ n_index ];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s_turret = self._turret;
|
|
}
|
|
|
|
if ( !isdefined( s_turret ) )
|
|
{
|
|
s_turret = _init_turret( n_index );
|
|
}
|
|
|
|
return s_turret;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
SELF: turret or vehicle
|
|
PURPOSE: Get the data structure holding the turret info
|
|
RETURNS: The turret data struct
|
|
[n_index]: optional index of vehicle turret if self is a vehicle
|
|
===================================================================*/
|
|
function has_turret( n_index )
|
|
{
|
|
if ( isdefined( self.a_turrets ) && isdefined( self.a_turrets[ n_index ] ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*===================================================================
|
|
SELF: turret or vehicle
|
|
PURPOSE: Initialize a turret for use in this system
|
|
RETURNS: The turret data struct
|
|
[n_index]: optional index of vehicle turret if self is a vehicle
|
|
===================================================================*/
|
|
|
|
function _init_turret( n_index = 0 )
|
|
{
|
|
self endon( "death" );
|
|
|
|
w_weapon = get_weapon( n_index );
|
|
|
|
if ( w_weapon == level.weaponNone )
|
|
{
|
|
AssertMsg( "Cannot initialize turret. No weapon info." );
|
|
return;
|
|
}
|
|
|
|
util::waittill_asset_loaded( "xmodel", self.model );
|
|
|
|
if ( IsVehicle( self ) )
|
|
{
|
|
s_turret = _init_vehicle_turret( n_index );
|
|
}
|
|
else
|
|
{
|
|
AssertMsg( "Misc turrets are no longer supported, please use a supported script_vehicle turret" );
|
|
}
|
|
|
|
s_turret.w_weapon = w_weapon;
|
|
_update_turret_arcs( n_index );
|
|
|
|
s_turret.is_enabled = false;
|
|
s_turret.e_parent = self;
|
|
s_turret.e_target = undefined;
|
|
s_turret.b_ignore_line_of_sight = false;
|
|
s_turret.v_offset = (0, 0, 0);
|
|
s_turret.n_burst_fire_time = 0;
|
|
s_turret.n_max_target_distance_squared = undefined;
|
|
s_turret.n_min_target_distance_squared = undefined;
|
|
|
|
// Defaults
|
|
s_turret.str_weapon_type = "bullet";
|
|
s_turret.str_guidance_type = "none";
|
|
|
|
s_turret.str_weapon_type = w_weapon.type;
|
|
s_turret.str_guidance_type = w_weapon.guidedMissileType;
|
|
|
|
set_on_target_angle( undefined, n_index );
|
|
|
|
s_turret.n_target_flags = 1 | 2;
|
|
|
|
set_best_target_func_from_weapon_type( n_index );
|
|
|
|
s_turret flag::init( "turret manual" );
|
|
|
|
return s_turret;
|
|
}
|
|
|
|
function _update_turret_arcs( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.rightarc = s_turret.w_weapon.rightArc;
|
|
s_turret.leftarc = s_turret.w_weapon.leftArc;
|
|
s_turret.toparc = s_turret.w_weapon.topArc;
|
|
s_turret.bottomarc = s_turret.w_weapon.bottomArc;
|
|
}
|
|
|
|
function set_best_target_func_from_weapon_type( n_index )
|
|
{
|
|
switch ( _get_turret_data( n_index ).str_weapon_type )
|
|
{
|
|
case "bullet":
|
|
set_best_target_func(&_get_best_target_bullet, n_index );
|
|
break;
|
|
|
|
case "gas":
|
|
set_best_target_func(&_get_best_target_gas, n_index );
|
|
break;
|
|
|
|
case "grenade":
|
|
set_best_target_func(&_get_best_target_grenade, n_index );
|
|
break;
|
|
|
|
case "projectile":
|
|
set_best_target_func(&_get_best_target_projectile, n_index );
|
|
break;
|
|
|
|
default:
|
|
AssertMsg( "unsupported turret weapon type." );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: set_best_target_func( function, n_index )"
|
|
"Summary: Custom override function, scripter can use to custermise their choice from potential targets"
|
|
|
|
"MandatoryArg: <function> Override target selection function"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: set_best_target_func(&_get_best_target_bullet, n_index );"
|
|
@/
|
|
|
|
function set_best_target_func( func_get_best_target, n_index )
|
|
{
|
|
_get_turret_data( n_index ).func_get_best_target = func_get_best_target;
|
|
}
|
|
|
|
/*===================================================================
|
|
SELF: vehicle
|
|
PURPOSE: Initialize a vehicle turret for use in this system
|
|
RETURNS: The turret data struct
|
|
<n_index>: index of vehicle turret
|
|
===================================================================*/
|
|
function _init_vehicle_turret( n_index )
|
|
{
|
|
Assert( isdefined( n_index ) && ( n_index >= 0 ), "Invalid index specified to initialize vehicle turret." );
|
|
|
|
s_turret = SpawnStruct();
|
|
|
|
v_angles = self GetSeatFiringAngles( n_index );
|
|
if ( isdefined( v_angles ) )
|
|
{
|
|
s_turret.n_rest_angle_pitch = 0;
|
|
s_turret.n_rest_angle_yaw = 0;
|
|
}
|
|
|
|
switch ( n_index )
|
|
{
|
|
case 0:
|
|
s_turret.str_tag_flash = "tag_flash";
|
|
s_turret.str_tag_pivot = "tag_barrel";
|
|
break;
|
|
|
|
case 1:
|
|
s_turret.str_tag_flash = "tag_gunner_flash1";
|
|
s_turret.str_tag_pivot = "tag_gunner_barrel1";
|
|
break;
|
|
|
|
case 2:
|
|
s_turret.str_tag_flash = "tag_gunner_flash2";
|
|
s_turret.str_tag_pivot = "tag_gunner_barrel2";
|
|
break;
|
|
|
|
case 3:
|
|
s_turret.str_tag_flash = "tag_gunner_flash3";
|
|
s_turret.str_tag_pivot = "tag_gunner_barrel3";
|
|
break;
|
|
|
|
case 4:
|
|
s_turret.str_tag_flash = "tag_gunner_flash4";
|
|
s_turret.str_tag_pivot = "tag_gunner_barrel4";
|
|
break;
|
|
}
|
|
|
|
if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
|
|
{
|
|
// helicopters are not likely to shoot themselves, but can also cause problems tracing
|
|
// don't ignore ground vehicle like trucks because they are likely to shoot themselves
|
|
s_turret.e_trace_ignore = self;
|
|
}
|
|
|
|
if ( !isdefined( self.a_turrets ) )
|
|
{
|
|
self.a_turrets = [];
|
|
}
|
|
|
|
self.a_turrets[n_index] = s_turret;
|
|
|
|
if ( n_index > 0 )
|
|
{
|
|
// If vehicle has a gunner tag, assume it needs a user
|
|
tag_origin = self GetTagOrigin( _get_gunner_tag_for_turret_index( n_index ) );
|
|
if ( isdefined( tag_origin ) )
|
|
{
|
|
_set_turret_needs_user( n_index, true );
|
|
}
|
|
}
|
|
|
|
return s_turret;
|
|
}
|
|
|
|
/*===================================================================
|
|
SELF: turret or vehicle
|
|
PURPOSE: fires a turret for one burst based on min and max burst
|
|
values
|
|
RETURNS: the total time of the burst plus wait time
|
|
[n_max_time]: the max time that the burst will take
|
|
[n_index]: optional index of vehicle turret if self is a vehicle
|
|
===================================================================*/
|
|
function _burst_fire( n_max_time, n_index )
|
|
{
|
|
self endon( "terminate_all_turrets_firing" );
|
|
|
|
if ( n_max_time < 0 )
|
|
{
|
|
n_max_time = 9999;
|
|
}
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
n_burst_time = _get_burst_fire_time( n_index );
|
|
n_burst_wait = _get_burst_wait_time( n_index );
|
|
|
|
if ( !isdefined( n_burst_time ) || ( n_burst_time > n_max_time ) )
|
|
{
|
|
n_burst_time = n_max_time;
|
|
}
|
|
|
|
if ( s_turret.n_burst_fire_time >= n_burst_time )
|
|
{
|
|
s_turret.n_burst_fire_time = 0;
|
|
|
|
n_time_since_last_shot = ( GetTime() - s_turret.n_last_fire_time ) / 1000;
|
|
if ( n_time_since_last_shot < n_burst_wait )
|
|
{
|
|
wait ( n_burst_wait - n_time_since_last_shot );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
n_burst_time = n_burst_time - s_turret.n_burst_fire_time;
|
|
}
|
|
|
|
w_weapon = get_weapon( n_index );
|
|
n_fire_time = w_weapon.fireTime;
|
|
n_total_time = 0;
|
|
|
|
while ( n_total_time < n_burst_time )
|
|
{
|
|
turret::fire( n_index );
|
|
|
|
n_total_time += n_fire_time; // keep track of time for this instance of burst fire
|
|
s_turret.n_burst_fire_time += n_fire_time; // keep track of totall burst time between multiple calls to burst fire
|
|
|
|
wait n_fire_time;
|
|
}
|
|
|
|
if ( n_burst_wait > 0 )
|
|
{
|
|
wait n_burst_wait;
|
|
}
|
|
|
|
return n_burst_time + n_burst_wait;
|
|
}
|
|
|
|
/*===================================================================
|
|
SELF: turret or vehicle
|
|
PURPOSE: get the random burst time for a turret based on min
|
|
and max values
|
|
RETURNS: number between burst_min and burst_max, or 0
|
|
[n_index]: optional index of vehicle turret if self is a vehicle
|
|
===================================================================*/
|
|
function _get_burst_fire_time( n_index)
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
n_time = undefined;
|
|
|
|
if ( isdefined( s_turret.n_burst_fire_min ) && isdefined( s_turret.n_burst_fire_max ) )
|
|
{
|
|
if ( s_turret.n_burst_fire_min == s_turret.n_burst_fire_max )
|
|
{
|
|
n_time = s_turret.n_burst_fire_min;
|
|
}
|
|
else
|
|
{
|
|
n_time = RandomFloatRange( s_turret.n_burst_fire_min, s_turret.n_burst_fire_max );
|
|
}
|
|
}
|
|
else if ( isdefined( s_turret.n_burst_fire_max ) )
|
|
{
|
|
n_time = RandomFloatRange( 0, s_turret.n_burst_fire_max );
|
|
}
|
|
|
|
return n_time;
|
|
}
|
|
|
|
/*===================================================================
|
|
SELF: turret or vehicle
|
|
PURPOSE: get the random burst wait time for a turret struct based
|
|
on min and max values
|
|
RETURNS: number between wait_min and wait_max, or 0 by default
|
|
[n_index]: optional index of vehicle turret if self is a vehicle
|
|
===================================================================*/
|
|
function _get_burst_wait_time( n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
n_time = 0;
|
|
|
|
if ( isdefined( s_turret.n_burst_wait_min ) && isdefined( s_turret.n_burst_wait_max ) )
|
|
{
|
|
if ( s_turret.n_burst_wait_min == s_turret.n_burst_wait_max )
|
|
{
|
|
n_time = s_turret.n_burst_wait_min;
|
|
}
|
|
else
|
|
{
|
|
n_time = RandomFloatRange( s_turret.n_burst_wait_min, s_turret.n_burst_wait_max );
|
|
}
|
|
}
|
|
else if ( isdefined( s_turret.n_burst_wait_max ) )
|
|
{
|
|
n_time = RandomFloatRange( 0, s_turret.n_burst_wait_max );
|
|
}
|
|
|
|
return n_time;
|
|
}
|
|
|
|
/*===================================================================
|
|
PURPOSE: convert a turret index to a string value. used for
|
|
unique endons, etc.
|
|
RETURNS: string value of index if defined. empty string if not
|
|
defined.
|
|
[n_index]: optional index of vehicle turret if self is a vehicle
|
|
===================================================================*/
|
|
function _index( n_index )
|
|
{
|
|
return (isdefined(n_index)?""+n_index:"");
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------------------------------
|
|
Targeting Functions
|
|
------------------------------------------------------------------------------------------------------*/
|
|
|
|
// self = turret or vehicle
|
|
function _get_potential_targets( n_index )
|
|
{
|
|
s_turret = self _get_turret_data( n_index );
|
|
|
|
//************************************************************************************************
|
|
// MikeA: For now if we have a target array the only targets we are interested is the target array
|
|
//************************************************************************************************
|
|
|
|
a_priority_targets = self _get_any_priority_targets( n_index );
|
|
|
|
if ( isdefined( a_priority_targets ) && ( a_priority_targets.size > 0 ) )
|
|
{
|
|
//return arrayCopy( a_priority_targets );
|
|
return( a_priority_targets );
|
|
}
|
|
|
|
//****************************
|
|
// Search for a regular target
|
|
//****************************
|
|
|
|
a_potential_targets = [];
|
|
|
|
str_team = get_team( n_index );
|
|
if ( self.use_non_teambased_enemy_selection === true && !level.teambased )
|
|
{
|
|
// get all ai, players, and vehicles
|
|
|
|
a_all_targets = [];
|
|
|
|
if ( _has_target_flags( 1, n_index ) )
|
|
{
|
|
a_ai_targets = GetAIArray();
|
|
a_all_targets = ArrayCombine( a_all_targets, a_ai_targets, true, false );
|
|
}
|
|
|
|
if ( _has_target_flags( 2, n_index ) )
|
|
{
|
|
a_all_targets = ArrayCombine( a_all_targets, level.players, true, false );
|
|
}
|
|
|
|
if ( _has_target_flags( 8, n_index ) )
|
|
{
|
|
a_all_targets = ArrayCombine( a_all_targets, level.vehicles_list, true, false );
|
|
}
|
|
|
|
// remove all targets not on the same team
|
|
for ( i = 0; i < a_all_targets.size; i++ )
|
|
{
|
|
e_target = a_all_targets[i];
|
|
|
|
if ( !isdefined( e_target ) )
|
|
continue;
|
|
|
|
if ( !isdefined( e_target.team ) )
|
|
continue;
|
|
|
|
if ( e_target.team == str_team )
|
|
continue;
|
|
|
|
if( !( isdefined( level.team_free_targeting ) && level.team_free_targeting ) && ( e_target.team == "free" ) )
|
|
continue;
|
|
|
|
a_potential_targets[ a_potential_targets.size ] = e_target;
|
|
}
|
|
}
|
|
else if ( isdefined( str_team ) )
|
|
{
|
|
str_opposite_team = "allies";
|
|
if ( str_team == "allies" )
|
|
{
|
|
str_opposite_team = "axis";
|
|
}
|
|
|
|
if ( _has_target_flags( 1, n_index ) )
|
|
{
|
|
a_ai_targets = GetAITeamArray( str_opposite_team );
|
|
if( ( isdefined( level.team_free_targeting ) && level.team_free_targeting ) )
|
|
{
|
|
a_ai_targets = ArrayCombine( GetAITeamArray( "free" ), a_ai_targets, true, false );
|
|
}
|
|
a_potential_targets = ArrayCombine( a_potential_targets, a_ai_targets, true, false );
|
|
}
|
|
|
|
if ( _has_target_flags( 2, n_index ) )
|
|
{
|
|
a_potential_targets = ArrayCombine( a_potential_targets, level.aliveplayers[ str_opposite_team ], true, false );
|
|
}
|
|
|
|
if ( _has_target_flags( 4, n_index ) )
|
|
{
|
|
// a_drone_targets = _drones::drones_get_array( str_opposite_team ); TODO: _drones has been removed
|
|
// a_potential_targets = ArrayCombine( a_potential_targets, a_drone_targets, true, false );
|
|
}
|
|
|
|
if ( _has_target_flags( 8, n_index ) )
|
|
{
|
|
a_vehicle_targets = GetVehicleTeamArray( str_opposite_team );
|
|
a_potential_targets = ArrayCombine( a_potential_targets, a_vehicle_targets, true, false );
|
|
}
|
|
}
|
|
|
|
if ( isdefined( s_turret.e_target ) && !IsInArray( a_potential_targets, s_turret.e_target ) )
|
|
{
|
|
a_potential_targets[ a_potential_targets.size ] = s_turret.e_target;
|
|
}
|
|
|
|
//**************************************
|
|
// Remove targets that should be ignored
|
|
//**************************************
|
|
|
|
if ( isdefined( str_team ) )
|
|
{
|
|
a_valid_targets = [];
|
|
|
|
for ( i = 0; i < a_potential_targets.size; i++ )
|
|
{
|
|
e_target = a_potential_targets[i];
|
|
ignore_target = false;
|
|
|
|
assert( isdefined( e_target ), "Undefined potential turret target." );
|
|
|
|
// Should we remove the target?
|
|
if ( ( isdefined( e_target.ignoreme ) && e_target.ignoreme ) || !isdefined( e_target.health ) || ( e_target.health <= 0 ) )
|
|
{
|
|
ignore_target = true;
|
|
}
|
|
else if ( IsSentient( e_target ) && ( ( e_target IsNoTarget() ) || ( e_target ai::is_dead_sentient() ) ) ) // Removal checks for Sentients Only
|
|
{
|
|
ignore_target = true;
|
|
}
|
|
else if ( _is_target_within_range( e_target, s_turret ) == false ) // Remove target if we have a target limit distance set
|
|
{
|
|
ignore_target = true;
|
|
}
|
|
else if ( isplayer( e_target ) && e_target hasPerk( "specialty_nottargetedbysentry" ) )
|
|
{
|
|
ignore_target = true;
|
|
}
|
|
|
|
if ( !ignore_target )
|
|
{
|
|
a_valid_targets[ a_valid_targets.size ] = e_target;
|
|
}
|
|
}
|
|
|
|
// Save the new valid targets
|
|
a_potential_targets = a_valid_targets;
|
|
}
|
|
|
|
a_targets = a_potential_targets;
|
|
|
|
if ( isdefined( s_turret ) && isdefined( s_turret.a_ignore_target_array ) )
|
|
{
|
|
while ( true )
|
|
{
|
|
found_bad_target = 0;
|
|
a_targets = a_potential_targets;
|
|
|
|
for ( i = 0; i < a_targets.size; i++ )
|
|
{
|
|
e_target = a_targets[i];
|
|
found_bad_target = 0;
|
|
|
|
for ( j = 0; j < s_turret.a_ignore_target_array.size; j++ )
|
|
{
|
|
if ( e_target == s_turret.a_ignore_target_array[ j ] )
|
|
{
|
|
ArrayRemoveValue( a_potential_targets, e_target );
|
|
found_bad_target = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !found_bad_target )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return a_potential_targets;
|
|
}
|
|
|
|
// self = Vehicle/MG
|
|
function _is_target_within_range( e_target, s_turret )
|
|
{
|
|
if ( isdefined( s_turret.n_max_target_distance_squared ) || isdefined( s_turret.n_min_target_distance_squared ) )
|
|
{
|
|
if ( !isdefined( e_target.origin ) )
|
|
return false;
|
|
|
|
n_dist_squared = DistanceSquared( e_target.origin, self.origin );
|
|
|
|
if ( n_dist_squared > (isdefined(s_turret.n_max_target_distance_squared)?s_turret.n_max_target_distance_squared:811711611) )
|
|
return false;
|
|
|
|
if ( n_dist_squared < (isdefined(s_turret.n_min_target_distance_squared)?s_turret.n_min_target_distance_squared:0) )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function _get_any_priority_targets( n_index )
|
|
{
|
|
a_targets = undefined;
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
// Do we have a set of priority targets?
|
|
if ( isdefined( s_turret.priority_target_array ) )
|
|
{
|
|
while ( true )
|
|
{
|
|
found_bad_target = 0;
|
|
a_targets = s_turret.priority_target_array;
|
|
|
|
// Make sure all the priority tagets are still alive
|
|
for ( i = 0; i < a_targets.size; i++ )
|
|
{
|
|
e_target = a_targets[ i ];
|
|
bad_index = undefined;
|
|
|
|
// Should we remove the target?
|
|
if ( !isdefined( e_target ) )
|
|
{
|
|
bad_index = i;
|
|
}
|
|
else if ( !IsAlive( e_target ) )
|
|
{
|
|
bad_index = i;
|
|
}
|
|
else if ( e_target.health <= 0 )
|
|
{
|
|
bad_index = i;
|
|
}
|
|
else if ( IsSentient( e_target ) && ( e_target ai::is_dead_sentient() ) )
|
|
{
|
|
bad_index = i;
|
|
}
|
|
|
|
if ( isdefined( bad_index ) )
|
|
{
|
|
s_turret.priority_target_array = a_targets;
|
|
ArrayRemoveValue( s_turret.priority_target_array, e_target );
|
|
found_bad_target = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Did we removee any bad targets?
|
|
if ( !found_bad_target )
|
|
{
|
|
return ( s_turret.priority_target_array );
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ( s_turret.priority_target_array.size <= 0 )
|
|
{
|
|
s_turret.priority_target_array = undefined;
|
|
self notify( "target_array_destroyed" );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( a_targets );
|
|
}
|
|
|
|
function _get_best_target_from_potential( a_potential_targets, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
return [[ s_turret.func_get_best_target ]]( a_potential_targets, n_index );
|
|
}
|
|
|
|
function _get_best_target_bullet( a_potential_targets, n_index )
|
|
{
|
|
e_best_target = undefined;
|
|
|
|
while ( !isdefined( e_best_target ) && ( a_potential_targets.size > 0 ) )
|
|
{
|
|
e_closest_target = ArrayGetClosest( self.origin, a_potential_targets );
|
|
|
|
if ( !isdefined( e_closest_target ) )
|
|
{
|
|
break;
|
|
}
|
|
else if( self can_hit_target( e_closest_target, n_index ) )
|
|
{
|
|
e_best_target = e_closest_target;
|
|
}
|
|
else
|
|
{
|
|
ArrayRemoveValue( a_potential_targets, e_closest_target );
|
|
}
|
|
}
|
|
|
|
return e_best_target;
|
|
}
|
|
|
|
function _get_best_target_gas( a_potential_targets, n_index )
|
|
{
|
|
// TODO: TEMP: use bullet function
|
|
return _get_best_target_bullet( a_potential_targets, n_index );
|
|
}
|
|
|
|
function _get_best_target_grenade( a_potential_targets, n_index )
|
|
{
|
|
// TODO: TEMP: use bullet function
|
|
return _get_best_target_bullet( a_potential_targets, n_index );
|
|
}
|
|
|
|
function _get_best_target_projectile( a_potential_targets, n_index )
|
|
{
|
|
// TODO: TEMP: use bullet function
|
|
return _get_best_target_bullet( a_potential_targets, n_index );
|
|
}
|
|
|
|
/@
|
|
"Name: can_hit_target( e_target, n_index )"
|
|
"Summary: Check bothif the target is within the turrets constaraints and theres is nothing blocking a LOS"
|
|
|
|
"MandatoryArg: <e_target> Target the Turret wants to fire at"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret can_hit_target( e_guy, 1 );"
|
|
@/
|
|
// self = vehicle or turret
|
|
function can_hit_target( e_target, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
v_offset = _get_default_target_offset( e_target, n_index );
|
|
|
|
b_current_target = is_target( e_target, n_index );
|
|
|
|
if(isDefined(e_target) && ( isdefined( e_target.ignoreme ) && e_target.ignoreme ))
|
|
return false;
|
|
|
|
b_target_in_view = is_target_in_view( ( IsPlayer( e_target ) ? e_target GetTagOrigin( "tag_eye" ) : e_target.origin + v_offset ), n_index );
|
|
b_trace_passed = true;
|
|
|
|
if ( b_target_in_view )
|
|
{
|
|
if ( !s_turret.b_ignore_line_of_sight )
|
|
{
|
|
b_trace_passed = trace_test( e_target, v_offset - ( 0, 0, ( IsVehicle( e_target ) ? 0 : (isdefined(s_turret.n_torso_targetting_offset)?s_turret.n_torso_targetting_offset:0) ) ), n_index );
|
|
}
|
|
|
|
if ( b_current_target && !b_trace_passed && !isdefined( s_turret.n_time_lose_sight ) )
|
|
{
|
|
s_turret.n_time_lose_sight = GetTime();
|
|
}
|
|
}
|
|
else if ( b_current_target )
|
|
{
|
|
s_turret.b_target_out_of_range = true;
|
|
}
|
|
|
|
return ( b_target_in_view && b_trace_passed );
|
|
}
|
|
|
|
/@
|
|
"Name: is_target_in_view( v_target, n_index )"
|
|
"Summary: Gets whether a turret's target is in view"
|
|
|
|
"MandatoryArg: <e_target> Target the Turret wants to fire at"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret is_target_in_view( v_position, 1 );"
|
|
@/
|
|
//self = turret/vehicle
|
|
function is_target_in_view( v_target, n_index )
|
|
{
|
|
/#
|
|
_update_turret_arcs( n_index );
|
|
#/
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
v_pivot_pos = self GetTagOrigin( s_turret.str_tag_pivot );
|
|
v_angles_to_target = VectorToAngles( v_target - v_pivot_pos );
|
|
|
|
n_rest_angle_pitch = s_turret.n_rest_angle_pitch + self.angles[0];
|
|
n_rest_angle_yaw = s_turret.n_rest_angle_yaw + self.angles[1];
|
|
|
|
n_ang_pitch = AngleClamp180( v_angles_to_target[0] - n_rest_angle_pitch );
|
|
n_ang_yaw = AngleClamp180( v_angles_to_target[1] - n_rest_angle_yaw );
|
|
|
|
b_out_of_range = false;
|
|
|
|
if ( n_ang_pitch > 0 )
|
|
{
|
|
if ( n_ang_pitch > s_turret.bottomarc )
|
|
{
|
|
b_out_of_range = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( Abs( n_ang_pitch ) > s_turret.toparc )
|
|
{
|
|
b_out_of_range = true;
|
|
}
|
|
}
|
|
|
|
if ( n_ang_yaw > 0 )
|
|
{
|
|
if ( n_ang_yaw > s_turret.leftarc )
|
|
{
|
|
b_out_of_range = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( Abs( n_ang_yaw ) > s_turret.rightarc )
|
|
{
|
|
b_out_of_range = true;
|
|
}
|
|
}
|
|
|
|
return !b_out_of_range;
|
|
}
|
|
|
|
/@
|
|
"Name: can_turret_shoot_target( e_target, n_index )"
|
|
"Summary: Basically is there anythinhg blocking the turrets shot (geo etc..)"
|
|
|
|
"MandatoryArg: <e_target> Target the Turret wats to fire at"
|
|
"OptionalArg: [v_offset] Optional offset"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: vh_heli can_turret_shoot_target( e_guy, 1 );"
|
|
@/
|
|
|
|
|
|
//self = turret/vehicle
|
|
function trace_test( e_target, v_offset = (0,0,0), n_index )
|
|
{
|
|
//********************************************************************************
|
|
// Old style tracing, reviving to restore functionality in pak3 at this late stage
|
|
//********************************************************************************
|
|
|
|
if ( isdefined( self.good_old_style_turret_tracing ) )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
v_start_org = self GetTagOrigin( s_turret.str_tag_pivot );
|
|
if ( e_target SightConeTrace( v_start_org, self ) > .2 )
|
|
{
|
|
// If we can see the target, make sure we can bullet trace over half way to hit the target
|
|
v_target = e_target.origin + v_offset;
|
|
v_start_org += VectorNormalize( v_target - v_start_org ) * 50;
|
|
a_trace = BulletTrace( v_start_org, v_target, true, s_turret.e_trace_ignore, true, true );
|
|
if ( a_trace["fraction"] > .6 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//*************************************************************************************************
|
|
// MikeA: I think there is a bug where when we have two people in a vehicle
|
|
// If the driver is in the LOS of the gunner and the target, the trace will hit the driver and fail
|
|
//*************************************************************************************************
|
|
|
|
s_turret = _get_turret_data( n_index );
|
|
|
|
v_start_org = self GetTagOrigin( s_turret.str_tag_pivot );
|
|
v_target = e_target.origin + v_offset;
|
|
|
|
if ( SessionModeIsMultiplayerGame() && IsPlayer( e_target ) )
|
|
v_target = e_target GetShootAtPos();
|
|
|
|
if ( DistanceSquared( v_start_org, v_target ) < 100*100 )
|
|
return true;
|
|
|
|
v_dir_to_target = VectorNormalize( v_target - v_start_org );
|
|
|
|
if ( !SessionModeIsMultiplayerGame() )
|
|
{
|
|
v_start_org += v_dir_to_target * 50;
|
|
v_target -= v_dir_to_target * 75; // don't have to see all the way there
|
|
}
|
|
|
|
if ( SightTracePassed( v_start_org, v_target, false, self ) )
|
|
{
|
|
/* too expensive
|
|
v_start_org = self GetTagOrigin( s_turret.str_tag_flash );
|
|
v_start_org += v_dir_to_target * TURRET_TRACE_OFFSET;
|
|
|
|
// If we can see the target, make sure we can bullet trace over half way to hit the target
|
|
a_trace = BulletTrace( v_start_org, v_target, true, s_turret.e_trace_ignore, true, true );
|
|
if ( a_trace["fraction"] > .6 )
|
|
{
|
|
return true;
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/@
|
|
"Name: set_ignore_line_of_sight( b_ignore, n_index )"
|
|
"Summary: Set whether a turret will ignore line of sight or not"
|
|
|
|
"MandatoryArg: <b_ignore> bool to ignore line of sight or not"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret set_ignore_line_of_sight( true, 0 );"
|
|
@/
|
|
|
|
function set_ignore_line_of_sight( b_ignore, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.b_ignore_line_of_sight = b_ignore;
|
|
}
|
|
|
|
/@
|
|
"Name: set_occupy_no_target_time( time, n_index )"
|
|
"Summary: How long an AI will stay inside a turret if there is no target, default = 2.0"
|
|
|
|
"MandatoryArg: <time> time to occupy turret without a target"
|
|
"OptionalArg: [n_index] Index of the turret for a vehicle turret"
|
|
|
|
"Example: e_turret turret::set_occupy_no_target_time( 4 );"
|
|
@/
|
|
|
|
function set_occupy_no_target_time( time, n_index )
|
|
{
|
|
s_turret = _get_turret_data( n_index );
|
|
s_turret.occupy_no_target_time = time;
|
|
}
|
|
|
|
//self = turret
|
|
function toggle_lensflare( bool )
|
|
{
|
|
self clientfield::set( "toggle_lensflare", bool );
|
|
}
|
|
|
|
function track_lens_flare()
|
|
{
|
|
self endon( "death" );
|
|
self notify( "disable_lens_flare" );
|
|
self endon( "disable_lens_flare" );
|
|
|
|
while ( true )
|
|
{
|
|
e_target = self GetTargetEntity();
|
|
if ( self.turretontarget && ( isdefined( e_target ) && IsPlayer( e_target ) ) )
|
|
{
|
|
if ( IsDefined( self GetTagOrigin( "TAG_LASER" ) ) )
|
|
{
|
|
e_target util::waittill_player_looking_at( self GetTagOrigin( "TAG_LASER" ), 90 );
|
|
if(isDefined(e_target))
|
|
{
|
|
self turret::toggle_lensflare( true );
|
|
e_target util::waittill_player_not_looking_at( self GetTagOrigin( "TAG_LASER" ) );
|
|
}
|
|
self turret::toggle_lensflare( false );
|
|
}
|
|
else
|
|
{
|
|
///# iPrintLn( "TAG_LASER not found on " + self.targetname ); #/
|
|
}
|
|
}
|
|
|
|
wait 0.5;//We don't need to check too often, helps keep resources down if we have a lot of turrets
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
// If the AI or Drone is manning a Turret, init the vehicles turret
|
|
// self = AI User or Drone User
|
|
//*****************************************************************************
|
|
|
|
function _get_gunner_tag_for_turret_index( n_index )
|
|
{
|
|
switch ( n_index )
|
|
{
|
|
case 1: return "tag_gunner1";
|
|
case 2: return "tag_gunner2";
|
|
case 3: return "tag_gunner3";
|
|
case 4: return "tag_gunner4";
|
|
default: AssertMsg( "unsupported turret index for getting gunner tag." );
|
|
}
|
|
}
|
|
|
|
function _get_turret_index_for_tag( str_tag )
|
|
{
|
|
switch ( str_tag )
|
|
{
|
|
case "tag_gunner1": return 1;
|
|
case "tag_gunner2": return 2;
|
|
case "tag_gunner3": return 3;
|
|
case "tag_gunner4": return 4;
|
|
}
|
|
}
|