boiii-scripts/shared/ai_shared.gsc
2023-04-13 17:30:38 +02:00

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;
}