924 lines
32 KiB
Plaintext
924 lines
32 KiB
Plaintext
#using scripts\codescripts\struct;
|
|
|
|
#using scripts\shared\array_shared;
|
|
#using scripts\shared\colors_shared;
|
|
#using scripts\shared\flag_shared;
|
|
#using scripts\shared\flagsys_shared;
|
|
#using scripts\shared\scene_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\vehicle_ai_shared;
|
|
|
|
#using scripts\shared\ai\systems\init;
|
|
#using scripts\shared\ai\systems\shared;
|
|
#using scripts\shared\ai\archetype_utility;
|
|
#using scripts\shared\ai\systems\weaponList;
|
|
#using scripts\shared\ai\systems\ai_interface;
|
|
|
|
|
|
|
|
|
|
|
|
#namespace ai;
|
|
|
|
/@
|
|
"Name: set_ignoreme( <val> )"
|
|
"Summary: Sets an actor's .ignoreme value. If 'true', other entities will ignore him."
|
|
"Module: AI"
|
|
"CallOn: an actor"
|
|
"Example: guy set_ignoreme( true );"
|
|
"MandatoryArg: <val> : Boolean"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function set_ignoreme( val )
|
|
{
|
|
assert( IsSentient( self ), "Non ai tried to set ignoreme" );
|
|
self.ignoreme = val;
|
|
}
|
|
|
|
/@
|
|
"Name: set_ignoreall( <val> )"
|
|
"Summary: Sets an actor's .ignoreall value"
|
|
"Module: AI"
|
|
"CallOn: an actor"
|
|
"Example: guy set_ignoreall( true );"
|
|
"MandatoryArg: <val> : Boolean"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function set_ignoreall( val )
|
|
{
|
|
assert( isSentient( self ), "Non ai tried to set ignoraell" );
|
|
self.ignoreall = val;
|
|
}
|
|
|
|
/@
|
|
"Name: set_pacifist( <val> )"
|
|
"Summary: Sets an actor's .pacifist value. If 'true', he'll only fire back if fired upon first."
|
|
"Module: AI"
|
|
"CallOn: an actor"
|
|
"Example: guy set_pacifist( true );"
|
|
"MandatoryArg: <val> : Boolean"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function set_pacifist( val )
|
|
{
|
|
assert( IsSentient( self ), "Non ai tried to set pacifist" );
|
|
self.pacifist = val;
|
|
}
|
|
|
|
/@
|
|
"Name: disable_pain()"
|
|
"Summary: Disables pain on the AI"
|
|
"Module: Utility"
|
|
"CallOn: An ai"
|
|
"Example: level.zakhaev disable_pain();"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function disable_pain()
|
|
{
|
|
assert( isalive( self ), "Tried to disable pain on a non ai" );
|
|
self.allowPain = false;
|
|
}
|
|
|
|
/@
|
|
"Name: enable_pain()"
|
|
"Summary: Enables pain on the AI"
|
|
"Module: Utility"
|
|
"CallOn: An ai"
|
|
"Example: level.zakhaev enable_pain();"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function enable_pain()
|
|
{
|
|
assert( isalive( self ), "Tried to enable pain on a non ai" );
|
|
self.allowPain = true;
|
|
}
|
|
|
|
/@
|
|
"Name: gun_remove()"
|
|
"Summary: Removed the gun from the given AI. Often used for scripted sequences where you dont want the AI to carry a weapon."
|
|
"Module: AI"
|
|
"CallOn: An AI"
|
|
"Example: level.price gun_remove();"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function gun_remove()
|
|
{
|
|
self shared::placeWeaponOn( self.weapon, "none" );
|
|
self.gun_removed = true;
|
|
}
|
|
|
|
/@
|
|
"Name: gun_switchto()"
|
|
"Summary: Switches the given AI's gun to the one specified."
|
|
"Module: AI"
|
|
"CallOn: An AI"
|
|
"MandatoryArg: <weaponName> : The weapontype name you want the AI to switch to."
|
|
"MandatoryArg: <whichHand> : Which hand to put the weapon in."
|
|
"Example: level.zeitzev gun_switchto( GetWeapon( "ppsh" ), "right" );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function gun_switchto( weapon, whichHand )
|
|
{
|
|
self shared::placeWeaponOn( weapon, whichHand );
|
|
}
|
|
|
|
/@
|
|
"Name: gun_recall()"
|
|
"Summary: Give the AI his gun back."
|
|
"Module: AI"
|
|
"CallOn: An AI"
|
|
"Example: level.price gun_recall();"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function gun_recall()
|
|
{
|
|
self shared::placeWeaponOn( self.primaryweapon, "right" );
|
|
self.gun_removed = undefined;
|
|
}
|
|
|
|
/@
|
|
"Name: set_behavior_attribute()"
|
|
"Summary: Call on an AI to change a behavior interface attribute." +
|
|
"Available attributes are archetype specific and located in the archetype's interface gsc." +
|
|
"For example, robot attributes are in archetype_robot_interface.gsc"
|
|
"Module: AI"
|
|
"CallOn: an actor"
|
|
"MandatoryArg: <attribute> : The interface attribute to modify."
|
|
"MandatoryArg: <value> : Value to set."
|
|
"Example: guy set_behavior_attribute( "sprint", true );"
|
|
@/
|
|
function set_behavior_attribute( attribute, value )
|
|
{
|
|
if( ( SessionModeIsCampaignZombiesGame() ) )
|
|
{
|
|
if( has_behavior_attribute( attribute ) )
|
|
SetAiAttribute( self, attribute, value );
|
|
}
|
|
else
|
|
{
|
|
SetAiAttribute( self, attribute, value );
|
|
}
|
|
}
|
|
|
|
|
|
/@
|
|
"Name: get_behavior_attribute()"
|
|
"Summary: Call on an AI to return the current value of a behavior interface attribute." +
|
|
"Available attributes are archetype specific and located in the archetype's interface gsc." +
|
|
"For example, robot attributes are in archetype_robot_interface.gsc"
|
|
"Module: AI"
|
|
"CallOn: an actor"
|
|
"MandatoryArg: <attribute> : The interface attribute to retrieve."
|
|
"Example: guy get_behavior_attribute( "sprint" );"
|
|
@/
|
|
function get_behavior_attribute( attribute )
|
|
{
|
|
return GetAiAttribute( self, attribute );
|
|
}
|
|
|
|
/@
|
|
"Name: has_behavior_attribute()"
|
|
"Summary: Call on an AI to return whether the actor has a particular attribute defined."
|
|
"Module: AI"
|
|
"CallOn: an actor"
|
|
"MandatoryArg: <attribute> : The interface attribute to retrieve."
|
|
"Example: guy has_behavior_attribute( "sprint" );"
|
|
@/
|
|
function has_behavior_attribute( attribute )
|
|
{
|
|
return HasAiAttribute( self, attribute );
|
|
}
|
|
|
|
/@
|
|
"Name: is_dead_sentient()"
|
|
"Summary: Checks to see if the AI is not defined, not sentient, and dead"
|
|
"CallOn: AI"
|
|
"Example: if ( guy ai::is_dead_sentient() )"
|
|
@/
|
|
function is_dead_sentient()
|
|
{
|
|
if ( IsSentient( self ) && !IsAlive( self ) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: waittill_dead( <guys> , <num> , <timeoutLength> )"
|
|
"Summary: Waits until all the AI in array < guys > are dead."
|
|
"Module: AI"
|
|
"CallOn: "
|
|
"MandatoryArg: <guys> : Array of actors to wait until dead"
|
|
"OptionalArg: <timeoutLength> : Number of seconds before this function times out and continues"
|
|
"Example: waittill_dead( GetAITeamArray( "axis" ) );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function waittill_dead( guys, num, timeoutLength )
|
|
{
|
|
// verify the living - ness of the ai
|
|
allAlive = true;
|
|
for( i = 0;i < guys.size;i++ )
|
|
{
|
|
if( isalive( guys[ i ] ) )
|
|
{
|
|
continue;
|
|
}
|
|
allAlive = false;
|
|
break;
|
|
}
|
|
assert( allAlive, "Waittill_Dead was called with dead or removed AI in the array, meaning it will never pass." );
|
|
if( !allAlive )
|
|
{
|
|
newArray = [];
|
|
for( i = 0;i < guys.size;i++ )
|
|
{
|
|
if( isalive( guys[ i ] ) )
|
|
{
|
|
newArray[ newArray.size ] = guys[ i ];
|
|
}
|
|
}
|
|
guys = newArray;
|
|
}
|
|
|
|
ent = SpawnStruct();
|
|
if( isdefined( timeoutLength ) )
|
|
{
|
|
ent endon( "thread_timed_out" );
|
|
ent thread waittill_dead_timeout( timeoutLength );
|
|
}
|
|
|
|
ent.count = guys.size;
|
|
if( isdefined( num ) && num < ent.count )
|
|
{
|
|
ent.count = num;
|
|
}
|
|
array::thread_all( guys,&waittill_dead_thread, ent );
|
|
|
|
while( ent.count > 0 )
|
|
{
|
|
ent waittill( "waittill_dead guy died" );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: waittill_dead_or_dying( <guys> , <num> , <timeoutLength> )"
|
|
"Summary: Similar to waittill_dead(). Waits until all the AI in array < guys > are dead OR dying (long deaths)."
|
|
"Module: AI"
|
|
"CallOn: "
|
|
"MandatoryArg: <guys> : Array of actors to wait until dead or dying"
|
|
"OptionalArg: <num> : Number of guys that must die or be dying for this function to continue"
|
|
"OptionalArg: <timeoutLength> : Number of seconds before this function times out and continues"
|
|
"Example: waittill_dead_or_dying( GetAITeamArray( "axis" ) );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function waittill_dead_or_dying( guys, num, timeoutLength )
|
|
{
|
|
// verify the living - ness and healthy - ness of the ai
|
|
newArray = [];
|
|
for( i = 0;i < guys.size;i++ )
|
|
{
|
|
if( isalive( guys[ i ] ) && !guys[ i ].ignoreForFixedNodeSafeCheck )
|
|
{
|
|
newArray[ newArray.size ] = guys[ i ];
|
|
}
|
|
}
|
|
guys = newArray;
|
|
|
|
ent = spawnStruct();
|
|
if( isdefined( timeoutLength ) )
|
|
{
|
|
ent endon( "thread_timed_out" );
|
|
ent thread waittill_dead_timeout( timeoutLength );
|
|
}
|
|
|
|
ent.count = guys.size;
|
|
|
|
// optional override on count
|
|
if( isdefined( num ) && num < ent.count )
|
|
{
|
|
ent.count = num;
|
|
}
|
|
|
|
array::thread_all( guys,&waittill_dead_or_dying_thread, ent );
|
|
|
|
while( ent.count > 0 )
|
|
{
|
|
ent waittill( "waittill_dead_guy_dead_or_dying" );
|
|
}
|
|
}
|
|
|
|
function private waittill_dead_thread( ent )
|
|
{
|
|
self waittill( "death" );
|
|
ent.count-- ;
|
|
ent notify( "waittill_dead guy died" );
|
|
}
|
|
|
|
function waittill_dead_or_dying_thread( ent )
|
|
{
|
|
self util::waittill_either( "death", "pain_death" );
|
|
ent.count-- ;
|
|
ent notify( "waittill_dead_guy_dead_or_dying" );
|
|
}
|
|
|
|
function waittill_dead_timeout( timeoutLength )
|
|
{
|
|
wait( timeoutLength );
|
|
self notify( "thread_timed_out" );
|
|
}
|
|
|
|
//internal function called by shoot_at_target. This will ensure that we start computing the duration of shoot_at_target only after the first shot
|
|
function private wait_for_shoot()
|
|
{
|
|
self endon( "stop_shoot_at_target" );
|
|
if( IsVehicle( self ) )
|
|
{
|
|
self waittill( "weapon_fired" );
|
|
}
|
|
else
|
|
{
|
|
self waittill("shoot");
|
|
}
|
|
self.start_duration_comp = true;
|
|
}
|
|
|
|
/@
|
|
"Name: shoot_at_target(mode, target, tag, duration)"
|
|
"Summary: Force AI to aim and shoot at given target"
|
|
"Module: AI"
|
|
"CallOn: An AI"
|
|
"MandatoryArg: <mode> The mode of firing. The three modes are 'normal', 'shoot_until_target_dead', 'kill_within_time'"
|
|
"MandatoryArg: <target> The target entity to shoot at"
|
|
"OptionalArg: <tag> The tag of the entity to shoot at"
|
|
"OptionalArg: <duration> The duraton of firing. Leave undefined for one shot only."
|
|
"OptionalArg: <setHealth> If defined, sets the health of the target."
|
|
"OptionalArg: <ignoreFirstShotWait> If true, will not wait before calculating the duration."
|
|
"Example: ai_friendly shoot_at_target("normal", enemy, undefined, 5); This fires at the enemy for 5 seconds." +
|
|
"ai_friendly shoot_at_target("shoot_until_target_dead", enemy, "j_head"); This fires at the enemy till he dies. Also, the tag is set to aim for the head." +
|
|
"ai_friendly shoot_at_target("kill_within_time", enemy, undefined, 3); This ensures that the ai will kill the target within 3 seconds."
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function shoot_at_target( mode, target, tag, duration, setHealth, ignoreFirstShotWait )
|
|
{
|
|
self endon("death");
|
|
self endon("stop_shoot_at_target");
|
|
|
|
Assert( IsDefined( target ), "shoot_at_target was passed an undefined target" );
|
|
Assert( IsDefined( mode ), "Undefined mode. A mode must be passed to shoot_at_target" );
|
|
mode_flag = ( mode === "normal" ) || ( mode === "shoot_until_target_dead" ) || ( mode === "kill_within_time" );
|
|
Assert( mode_flag, "Unsupported mode. 'Mode must be normal', 'shoot_until_target_dead' or 'kill_within_time'" );
|
|
|
|
if( isdefined( duration ) )
|
|
{
|
|
Assert( duration >= 0, "Duration must be a zero or a positive quantity" );
|
|
}
|
|
else
|
|
{
|
|
duration = 0;
|
|
}
|
|
|
|
if ( isdefined(setHealth) && isdefined(target) )
|
|
{
|
|
target.health = setHealth;
|
|
}
|
|
|
|
if ( !isdefined( target ) || ( ( ( mode === "shoot_until_target_dead" ) ) && ( target.health <= 0 ) ) )
|
|
{
|
|
return; // undefined target or target already dead
|
|
}
|
|
|
|
if( IsDefined(tag) && tag != "" )
|
|
{
|
|
self SetEntityTarget( target, 1, tag );
|
|
}
|
|
else
|
|
{
|
|
self SetEntityTarget( target, 1 );
|
|
}
|
|
|
|
// make sure the AI shoots it, even if not visible
|
|
self.cansee_override = true;
|
|
self.start_duration_comp = false;
|
|
|
|
switch( mode )
|
|
{
|
|
case "normal":
|
|
break;
|
|
case "shoot_until_target_dead":
|
|
duration = -1;
|
|
break;
|
|
case "kill_within_time":
|
|
target DamageMode( "next_shot_kills" );
|
|
break;
|
|
}
|
|
|
|
if( IsVehicle( self ) )
|
|
{
|
|
// Reset vehicle firing cool downs so we can fire right away
|
|
self vehicle_ai::ClearAllCooldowns();
|
|
}
|
|
|
|
// wait for first shot
|
|
|
|
if(ignoreFirstShotWait === true)
|
|
{
|
|
self.start_duration_comp = true;
|
|
}
|
|
else
|
|
{
|
|
self thread wait_for_shoot();
|
|
}
|
|
|
|
// fire for duration
|
|
if( isdefined(duration) && isdefined( target ) && target.health > 0 )
|
|
{
|
|
if( duration >= 0)
|
|
{
|
|
elapsed = 0;
|
|
while( isdefined(target) && target.health > 0 && elapsed <= duration )
|
|
{
|
|
elapsed += 0.05;
|
|
if( !( isdefined( self.start_duration_comp ) && self.start_duration_comp ) )
|
|
elapsed = 0;
|
|
{wait(.05);};
|
|
}
|
|
|
|
if( isdefined(target) && mode == "kill_within_time" )
|
|
{
|
|
self.perfectaim = true;
|
|
self.aim_set_by_shoot_at_target = true;
|
|
|
|
target waittill( "death" );
|
|
}
|
|
}
|
|
else if (duration == -1)
|
|
{
|
|
target waittill( "death" );
|
|
}
|
|
}
|
|
|
|
stop_shoot_at_target();
|
|
}
|
|
|
|
/@
|
|
"Name: stop_shoot_at_target()"
|
|
"Summary: Give the AI his gun back."
|
|
"Module: AI"
|
|
"CallOn: An AI"
|
|
"Example: level.price stop_shoot_at_target();"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function stop_shoot_at_target()
|
|
{
|
|
self ClearEntityTarget();
|
|
|
|
if( ( isdefined( self.aim_set_by_shoot_at_target ) && self.aim_set_by_shoot_at_target ) )
|
|
{
|
|
self.perfectaim = false;
|
|
self.aim_set_by_shoot_at_target = false;
|
|
}
|
|
|
|
self.cansee_override = false;
|
|
|
|
self notify("stop_shoot_at_target");
|
|
}
|
|
|
|
function wait_until_done_speaking()
|
|
{
|
|
self endon( "death" );
|
|
while ( self.isSpeaking )
|
|
{
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: set_goal( <value>, [key = "targetname"], [b_force] )"
|
|
"Summary: Set's the ai's goal based on KVP."
|
|
"Module: AI"
|
|
"CallOn: An AI"
|
|
"MandatoryArg: <value>: Either a vector, or a KVP value of a node, ent, struct to set the goal to"
|
|
"OptionalArg: [key]: If the value is a KVP, use this key"
|
|
"OptionalArg: [b_force]: force goal param passed to SetGoal()"
|
|
"Example: guy ai::set_goal( value, key );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function set_goal( value, key = "targetname", b_force = false )
|
|
{
|
|
goal = GetNode( value, key );
|
|
|
|
if ( isdefined( goal ) )
|
|
{
|
|
self SetGoal( goal, b_force );
|
|
}
|
|
else
|
|
{
|
|
goal = GetEnt( value, key );
|
|
|
|
if ( isdefined( goal ) )
|
|
{
|
|
self SetGoal( goal, b_force );
|
|
}
|
|
else
|
|
{
|
|
goal = struct::get( value, key );
|
|
|
|
if ( isdefined( goal ) )
|
|
{
|
|
self SetGoal( goal.origin, b_force );
|
|
}
|
|
}
|
|
}
|
|
|
|
return goal;
|
|
}
|
|
|
|
/@
|
|
"Name: force_goal(<goto>, [n_radius], [b_shoot], [str_end_on], [b_keep_colors])"
|
|
"Summary: Force an AI to go to goal by temporarily disabling AI features."
|
|
"Module: AI"
|
|
"MandatoryArg: <pos>/<node>/<entity>/<volume> - position, node, entity or volume to go to"
|
|
"OptionalArg: [n_radius] : Option goal radius. AI will be considered at goal within this distance from goal."
|
|
"OptionalArg: [b_shoot] : Enable/Disable shoot while moving (defaults to true)."
|
|
"OptionalArg: [str_end_on] : The endon string that will set this AI back to normal (defaults to 'goal')."
|
|
"OptionalArg: [b_keep_colors] : If colors will be enabled again after force goal (defaults to 'false')."
|
|
"Example: self thread ai::force_goal( node, 20, true );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function force_goal( goto, n_radius, b_shoot = true, str_end_on, b_keep_colors = false, b_should_sprint = false )
|
|
{
|
|
self endon( "death" );
|
|
s_tracker = SpawnStruct();
|
|
self thread _force_goal( s_tracker, goto, n_radius, b_shoot, str_end_on, b_keep_colors, b_should_sprint );
|
|
s_tracker waittill( "done" );
|
|
}
|
|
|
|
function _force_goal( s_tracker, goto, n_radius, b_shoot = true, str_end_on, b_keep_colors = false, b_should_sprint = false )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self notify( "new_force_goal" );
|
|
|
|
flagsys::wait_till_clear( "force_goal" ); // let any previous force goal thread cleanup first
|
|
flagsys::set( "force_goal" );
|
|
|
|
goalradius = self.goalradius;
|
|
if ( IsDefined( n_radius ) )
|
|
{
|
|
Assert( ( IsFloat( n_radius ) || IsInt( n_radius ) ), "ai_shared::force_goal expects n_radius to be an int or a float." );
|
|
self.goalradius = n_radius;
|
|
}
|
|
|
|
color_enabled = false;
|
|
if ( !b_keep_colors )
|
|
{
|
|
if ( IsDefined( colors::get_force_color() ) )
|
|
{
|
|
color_enabled = true;
|
|
self colors::disable();
|
|
}
|
|
}
|
|
|
|
allowpain = self.allowpain;
|
|
ignoreall = self.ignoreall;
|
|
ignoreme = self.ignoreme;
|
|
ignoresuppression = self.ignoresuppression;
|
|
grenadeawareness = self.grenadeawareness;
|
|
|
|
if ( !b_shoot )
|
|
{
|
|
self set_ignoreall( true );
|
|
}
|
|
|
|
if( b_should_sprint )
|
|
{
|
|
self set_behavior_attribute( "sprint", true );
|
|
}
|
|
|
|
self.ignoresuppression = true;
|
|
self.grenadeawareness = 0;
|
|
self set_ignoreme( true );
|
|
self disable_pain();
|
|
|
|
self PushPlayer( true );
|
|
|
|
if ( isdefined( goto ) )
|
|
{
|
|
if ( IsDefined( n_radius ) )
|
|
{
|
|
Assert( ( IsFloat( n_radius ) || IsInt( n_radius ) ), "ai_shared::force_goal expects n_radius to be an int or a float." );
|
|
self SetGoal( goto );
|
|
}
|
|
else
|
|
{
|
|
self SetGoal( goto, true );
|
|
}
|
|
}
|
|
|
|
self util::waittill_any( "goal", "new_force_goal", str_end_on );
|
|
|
|
if ( color_enabled )
|
|
{
|
|
colors::enable();
|
|
}
|
|
|
|
self PushPlayer( false ); // assume we want this off once we have reached goal
|
|
|
|
self ClearForcedGoal();
|
|
|
|
self.goalradius = goalradius;
|
|
self set_ignoreall( ignoreall );
|
|
self set_ignoreme( ignoreme );
|
|
|
|
if ( allowpain )
|
|
{
|
|
self enable_pain();
|
|
}
|
|
|
|
self set_behavior_attribute( "sprint", false );
|
|
|
|
self.ignoresuppression = ignoresuppression;
|
|
self.grenadeawareness = grenadeawareness;
|
|
|
|
flagsys::clear( "force_goal" );
|
|
s_tracker notify( "done" );
|
|
}
|
|
|
|
/@
|
|
"Name: stopPainWaitInterval()"
|
|
"Summary: Removes pain block"
|
|
"Module: AI"
|
|
"Example: self thread ai::allowPainWithMinimumInterval( 5000 );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function stopPainWaitInterval()
|
|
{
|
|
self notify("painWaitIntervalRemove");
|
|
}
|
|
|
|
function private _allowPainRestore()
|
|
{
|
|
self endon("death");
|
|
self util::waittill_any("painWaitIntervalRemove","painWaitInterval");
|
|
self.allowPain = true;
|
|
}
|
|
|
|
/@
|
|
"Name: painWaitInterval(<mSec>)"
|
|
"Summary: Block pain reaction for mSec. AI still takes damage but doesn't play pain animations"
|
|
"Module: AI"
|
|
"MandatoryArg: <mSec> - number of milliseconds to block pain"
|
|
"Example: self thread ai::allowPainWithMinimumInterval( 5000 );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function painWaitInterval(mSec)
|
|
{
|
|
self endon("death");
|
|
self notify("painWaitInterval");
|
|
self endon("painWaitInterval");
|
|
self endon("painWaitIntervalRemove");
|
|
self thread _allowPainRestore();
|
|
|
|
if(!isDefined(mSec) || mSec < 20 )
|
|
mSec = 20;
|
|
|
|
while(isAlive(self))
|
|
{
|
|
self waittill("pain");
|
|
self.allowPain = false;
|
|
wait (mSec/1000);
|
|
self.allowPain = true;
|
|
}
|
|
}
|
|
|
|
|
|
/@
|
|
"Name: patrol( start_path_node )"
|
|
"Summary: Sets a human to patrol along the points of a path, stopping to play scenes or wait for a short period of time at each path node. Behavior ends when actor gets a target, or hits the end of the path. Be sure to set Alert on Spawn to false on the actor's spawner"
|
|
"Summary: Set self.should_stop_patrolling to true to end the patrol behavior early."
|
|
"Module: patrol_human"
|
|
"Mandatory Aarg: start_path_node - the first path node for the path the actor should follow"
|
|
"Example: guy patrol_human::patrol( begin_node)"
|
|
"SPMP: SP"
|
|
@/
|
|
function patrol( start_path_node )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "stop_patrolling" );
|
|
|
|
assert( isDefined( start_path_node ), self.targetname + " has not been assigned a valid node or scene scriptbundle for his patrol path to start on" );
|
|
if ( start_path_node.type == "BAD NODE" )
|
|
{
|
|
/#
|
|
errorMsg = "ERROR: patrol node '" + start_path_node.targetname + "' (" + int(start_path_node.origin[0]) + "," + int(start_path_node.origin[1]) + "," + int(start_path_node.origin[2]) + ") is 'BAD NODE'";
|
|
IPrintLn( errorMsg );
|
|
LogPrint( errorMsg );
|
|
#/
|
|
return;
|
|
}
|
|
assert( start_path_node.type == "Path" || isdefined( start_path_node.scriptbundlename ), "The starting point '" + start_path_node.targetname + "' for a patrol path is not a path node or scene script bundle" );
|
|
|
|
self notify( "go_to_spawner_target");
|
|
self.target = undefined;
|
|
|
|
self.old_goal_radius = self.goalradius;
|
|
|
|
self thread end_patrol_on_enemy_targetting();
|
|
|
|
self.currentgoal = start_path_node;
|
|
|
|
self.patroller = true;
|
|
|
|
While( 1 )
|
|
{
|
|
|
|
if( isDefined( self.currentgoal.type) && self.currentgoal.type == "Path" )//handle case where current goal is a path node
|
|
{
|
|
if ( self ai::has_behavior_attribute( "patrol" ) )
|
|
self ai::set_behavior_attribute( "patrol", true );
|
|
|
|
self setgoal( self.currentgoal, true );
|
|
|
|
self waittill( "goal" );
|
|
|
|
if( isDefined( self.currentgoal.script_notify ))
|
|
{
|
|
self notify ( self.currentgoal.script_notify );
|
|
level notify (self.currentgoal.script_notify );
|
|
}
|
|
|
|
|
|
if (isDefined( self.currentgoal.script_flag_set ))
|
|
{
|
|
flag = self.currentgoal.script_flag_set;
|
|
|
|
if ( !isdefined( level.flag[ flag ] ) )
|
|
{
|
|
level flag::init( flag );
|
|
}
|
|
|
|
level flag::set( flag );
|
|
}
|
|
|
|
|
|
if( !isDefined( self.currentgoal.script_wait_min ))
|
|
{
|
|
self.currentgoal.script_wait_min = 0;
|
|
}
|
|
|
|
if( !isDefined( self.currentgoal.script_wait_max ))
|
|
{
|
|
self.currentgoal.script_wait_max = 0;
|
|
}
|
|
|
|
assert( self.currentgoal.script_wait_min <= self.currentgoal.script_wait_max , "Patrol max wait is less than the min wait on " + self.currentgoal.targetname );
|
|
|
|
if( !isdefined( self.currentgoal.scriptbundlename) )
|
|
{
|
|
wait_variability = self.currentgoal.script_wait_max - self.currentgoal.script_wait_min;
|
|
wait_time = self.currentgoal.script_wait_min + randomfloat( wait_variability );
|
|
self notify( "patrol_goal", self.currentgoal );
|
|
wait wait_time;
|
|
}
|
|
else
|
|
{
|
|
self scene::play( self.currentgoal.scriptbundlename , self );
|
|
}
|
|
}
|
|
else //handle case where the current goal is a scene scriptbundle
|
|
{
|
|
self.currentgoal scene::play( self );
|
|
}
|
|
|
|
//once current goal handling is done, select a new goal from the targets of the current one
|
|
self patrol_next_node();
|
|
}
|
|
}
|
|
|
|
function patrol_next_node( )
|
|
{
|
|
//get current goal targets in an array if we have a next target
|
|
target_nodes = [];
|
|
target_scenes = [];
|
|
if ( isdefined( self.currentgoal.target ) )
|
|
{
|
|
target_nodes = getnodearray( self.currentgoal.target, "targetname" );
|
|
target_scenes = struct::get_array( self.currentgoal.target , "targetname" );
|
|
}
|
|
|
|
if( target_nodes.size == 0 && target_scenes.size == 0 )
|
|
{
|
|
self end_and_clean_patrol_behaviors();
|
|
}
|
|
else
|
|
{
|
|
if( target_nodes.size != 0)
|
|
{
|
|
self.currentgoal = array::random( target_nodes );
|
|
}
|
|
else
|
|
{
|
|
self.currentgoal = array::random( target_scenes );
|
|
}
|
|
}
|
|
}
|
|
|
|
function end_patrol_on_enemy_targetting()
|
|
{
|
|
|
|
self endon( "death" );
|
|
self endon( "stop_patrolling" );
|
|
|
|
While(1)
|
|
{
|
|
if( isdefined( self.enemy) || self.should_stop_patrolling === true )
|
|
{
|
|
self end_and_clean_patrol_behaviors();
|
|
}
|
|
|
|
wait 0.1;
|
|
}
|
|
}
|
|
|
|
function end_and_clean_patrol_behaviors()
|
|
{
|
|
if ( isdefined( self.currentgoal ) && isdefined( self.currentgoal.scriptbundlename ) && isDefined( self._o_scene ) )
|
|
self._o_scene cscene::stop();
|
|
|
|
if ( self ai::has_behavior_attribute( "patrol" ) )
|
|
self ai::set_behavior_attribute( "patrol", false );
|
|
if ( isDefined( self.old_goal_radius ) )
|
|
self.goalradius = self.old_goal_radius;
|
|
self ClearForcedGoal();
|
|
self notify( "stop_patrolling" );
|
|
self.patroller = undefined;
|
|
}
|
|
|
|
|
|
/@
|
|
"Name: bloody_death([n_delay], [hit_loc])"
|
|
"Summary: Kills the AI by damaging the given hit_loc. If not, then a random hit_loc is chosen."
|
|
"Module: AI"
|
|
"OptionalArg: <n_delay> : delay before death."
|
|
"OptionalArg: <hit_loc> : hit location to do the damage at."
|
|
"Example: self ai::bloody_death( 3, "neck" );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function bloody_death( n_delay, hit_loc )
|
|
{
|
|
self endon( "death" );
|
|
|
|
if( !IsDefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert( IsActor( self ) );
|
|
assert( IsAlive( self ) );
|
|
|
|
if( ( isdefined( self.__bloody_death ) && self.__bloody_death ) )
|
|
return;
|
|
|
|
self.__bloody_death = true;
|
|
|
|
if( IsDefined( n_delay ) )
|
|
{
|
|
wait n_delay;
|
|
}
|
|
|
|
if( !IsDefined( self ) || !IsAlive( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( IsDefined( hit_loc ) )
|
|
{
|
|
assert( IsInArray( array( "helmet", "head", "neck", "torso_upper", "torso_mid", "torso_lower", "right_arm_upper", "left_arm_upper", "right_arm_lower", "left_arm_lower", "right_hand", "left_hand", "right_leg_upper", "left_leg_upper", "right_leg_lower", "left_leg_lower", "right_foot", "left_foot", "gun", "riotshield" ), hit_loc ), "bloody_death() : Invalid hit location. Check the list of allowed hit location in blackboard.gsh, HITLOC_ALL." );
|
|
}
|
|
else
|
|
{
|
|
hit_loc = array::random( array( "helmet", "head", "neck", "torso_upper", "torso_mid", "torso_lower", "right_arm_upper", "left_arm_upper", "right_arm_lower", "left_arm_lower", "right_hand", "left_hand", "right_leg_upper", "left_leg_upper", "right_leg_lower", "left_leg_lower", "right_foot", "left_foot", "gun", "riotshield" ) );
|
|
}
|
|
|
|
self DoDamage( self.health + 100, self.origin, undefined, undefined, hit_loc );
|
|
}
|
|
|
|
/@
|
|
"Name: shouldRegisterClientFieldForArchetype([archetype])"
|
|
"Summary: returns true if clientfields should be registered for a given archetype. Need level.clientFieldAICheck set in level for this function to work."
|
|
"Module: AI"
|
|
"Mandatory Arg: archetype"
|
|
"Example: shouldRegisterClientFieldForArchetype(ARCHETYPE_WARLORD);"
|
|
"SPMP: both"
|
|
@/
|
|
function shouldRegisterClientFieldForArchetype( archetype )
|
|
{
|
|
if( ( isdefined( level.clientFieldAICheck ) && level.clientFieldAICheck ) && !IsArchetypeLoaded( archetype ) )
|
|
return false;
|
|
|
|
return true;
|
|
} |