IW4-Dump-Files/maps/_spawner.gsc

5781 lines
136 KiB
Plaintext

#include maps\_utility;
#include common_scripts\utility;
#include maps\_anim;
// .script_delete a group of guys, only one of which spawns
// .script_playerseek spawn and run to the player
// .script_patroller follow your targeted patrol
// .script_delayed_playerseek spawn and run to the player with decreasing goal radius
// .script_followmin
// .script_followmax
// .script_radius
// .script_friendname
// .script_startinghealth
// .script_accuracy
// .script_grenades
// .script_sightrange
// .script_ignoreme
main()
{
precachemodel( "grenade_bag" );
// precachemodel( "com_trashbag" );
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// connect auto AI spawners
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// create default threatbiasgroups;
createthreatbiasgroup( "allies" );
createthreatbiasgroup( "axis" );
createthreatbiasgroup( "team3" );
createthreatbiasgroup( "civilian" );
addNotetrack_customFunction( "generic", "rappel_pushoff_initial_npc", ::enable_achievement_harder_they_fall_guy );
addNotetrack_customFunction( "generic", "ps_rappel_pushoff_initial_npc", ::enable_achievement_harder_they_fall_guy );
addNotetrack_customFunction( "generic", "feet_on_ground", ::disable_achievement_harder_they_fall_guy );
addNotetrack_customFunction( "generic", "ps_rappel_clipout_npc", ::disable_achievement_harder_they_fall_guy );
foreach ( player in level.players )
{
player setthreatbiasgroup( "allies" );
}
// temp disabled, prototyping money
if( getdvar( "xp_enable", "0" ) == "1" )
thread maps\_rank::init();
if( getdvar( "money_enable", "0" ) == "1" )
thread maps\_money::init();
/#
// for combat mode testing
setDvarIfUninitialized( "scr_force_ai_combat_mode", "0" );
#/
/*
spawners = getSpawnerArray();
for ( i = 0; i < spawners.size; i++ )
{
spawner = spawners[ i ];
if ( !isDefined( spawner.targetname ) )
continue;
triggers = getEntArray( spawner.targetname, "target" );
for ( j = 0; j < triggers.size; j++ )
{
trigger = triggers[ j ];
if ( ( isdefined( trigger.targetname ) ) && ( trigger.targetname == "flood_spawner" ) )
continue;
switch( trigger.classname )
{
case "trigger_multiple":
case "trigger_once":
case "trigger_use":
case "trigger_damage":
case "trigger_radius":
case "trigger_lookat":
if ( spawner.count )
trigger thread doAutoSpawn( spawner );
break;
}
}
}
*/
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
level._nextcoverprint = 0;
level._ai_group = [];
level.killedaxis = 0;
level.ffpoints = 0;
level.missionfailed = false;
level.gather_delay = [];
level.smoke_thrown = [];
if ( !isdefined( level.deathflags ) )
level.deathflags = [];
level.spawner_number = 0;
level.go_to_node_arrays = [];
if ( !isdefined( level.subclass_spawn_functions ) )
level.subclass_spawn_functions = [];
level.subclass_spawn_functions[ "regular" ] = ::subclass_regular;
level.subclass_spawn_functions[ "elite" ] = ::subclass_elite;
level.team_specific_spawn_functions = [];
level.team_specific_spawn_functions[ "axis" ] = ::spawn_team_axis;
level.team_specific_spawn_functions[ "allies" ] = ::spawn_team_allies;
level.team_specific_spawn_functions[ "team3" ] = ::spawn_team_team3;
level.team_specific_spawn_functions[ "neutral" ] = ::spawn_team_neutral;
level.next_health_drop_time = 0;
level.guys_to_die_before_next_health_drop = randomintrange( 1, 4 );
if ( !isdefined( level.default_goalradius ) )
level.default_goalradius = 2048;
if ( !isdefined( level.default_goalheight ) )
level.default_goalheight = 512;
level.portable_mg_gun_tag = "J_Shoulder_RI";// need to get J_gun back to make it work properly
level.mg42_hide_distance = 1024;
if ( !isdefined( level.maxFriendlies ) )
level.maxFriendlies = 11;
level._max_script_health = 0;
ai = getaispeciesarray();
array_thread( ai, ::living_ai_prethink );
level.ai_classname_in_level = [];
level.drone_paths = [];
spawners = getspawnerarray();
for ( i = 0;i < spawners.size;i++ )
spawners[ i ] thread spawn_prethink();
level.drone_paths = undefined;
thread process_deathflags();
array_thread( ai, ::spawn_think );
level.ai_classname_in_level_keys = getarraykeys( level.ai_classname_in_level );
for ( i = 0 ; i < level.ai_classname_in_level_keys.size ; i++ )
{
if ( !issubstr( tolower( level.ai_classname_in_level_keys[ i ] ), "rpg" ) )
continue;
precacheItem( "rpg_player" );
break;
}
level.ai_classname_in_level_keys = undefined;
run_thread_on_noteworthy( "hiding_door_spawner", maps\_hiding_door::hiding_door_spawner );
/#
// check to see if the designer has placed at least the minimal number of script_char_groups
// check_script_char_group_ratio( spawners );
#/
}
// check to see if the designer has placed at least the minimal number of script_char_groups
check_script_char_group_ratio( spawners )
{
if ( spawners.size <= 16 )
return;
total = 0;
grouped = 0;
for ( i = 0; i < spawners.size; i++ )
{
if ( !spawners[ i ].team != "axis" )
continue;
total++;
if ( !spawners[ i ] has_char_group() )
continue;
grouped++;
}
assertex( grouped / total >= 0.65, "Please group your enemies with script_char_group so that each group gets a unique character mix. This minimizes duplicate characters in close proximity. Or you can specify precise character choice with script_group_index." );
}
has_char_group()
{
if ( isdefined( self.script_char_group ) )
return true;
return isdefined( self.script_char_index );
}
process_deathflags()
{
foreach ( deathflag, array in level.deathflags )
{
if ( !isdefined( level.flag[ deathflag ] ) )
{
flag_init( deathflag );
}
}
}
spawn_guys_until_death_or_no_count()
{
self endon( "death" );
for ( ;; )
{
if ( self.count > 0 )
{
self waittill( "spawned" );
}
// give the other waittill( "spawned" ) a chance to hit and increment the deathspawn
// on the ai or vehicle
waittillframeend;
if ( !self.count )
return;
}
}
ai_deathflag()
{
level.deathflags[ self.script_deathflag ][ "ai" ][ self.unique_id ] = self;
ai_number = self.unique_id;
deathflag = self.script_deathflag;
if ( isdefined( self.script_deathflag_longdeath ) )
{
self waittillDeathOrPainDeath();
}
else
{
self waittill( "death" );
}
level.deathflags[ deathflag ][ "ai" ][ ai_number ] = undefined;
update_deathflag( deathflag );
}
vehicle_deathflag()
{
ai_number = self.unique_id;
deathflag = self.script_deathflag;
if ( !isdefined( level.deathflags ) || !isdefined( level.deathflags[ self.script_deathflag ] ) )
{
waittillframeend; // if its the first frame then process deathflags hasn't happened yet
if ( !isdefined( self ) )
return;
}
level.deathflags[ deathflag ][ "vehicles" ][ ai_number ] = self;
self waittill( "death" );
level.deathflags[ deathflag ][ "vehicles" ][ ai_number ] = undefined;
update_deathflag( deathflag );
}
spawner_deathflag()
{
level.deathflags[ self.script_deathflag ] = [];
// wait for the process_deathflags script to run and setup the arrays
waittillframeend;
if ( !isdefined( self ) || self.count == 0 )
{
// the spawner was removed on the first frame
return;
}
// give each spawner a unique id
self.spawner_number = level.spawner_number;
level.spawner_number++;
// keep an array of spawner entities that have this deathflag
level.deathflags[ self.script_deathflag ][ "spawners" ][ self.spawner_number ] = self;
deathflag = self.script_deathflag;
id = self.spawner_number;
spawn_guys_until_death_or_no_count();
level.deathflags[ deathflag ][ "spawners" ][ id ] = undefined;
update_deathflag( deathflag );
}
vehicle_spawner_deathflag()
{
level.deathflags[ self.script_deathflag ] = [];
// wait for the process_deathflags script to run and setup the arrays
waittillframeend;
if ( !isdefined( self ) )
{
// the spawner was removed on the first frame
return;
}
// give each spawner a unique id
self.spawner_number = level.spawner_number;
level.spawner_number++;
// keep an array of spawner entities that have this deathflag
level.deathflags[ self.script_deathflag ][ "vehicle_spawners" ][ self.spawner_number ] = self;
deathflag = self.script_deathflag;
id = self.spawner_number;
spawn_guys_until_death_or_no_count();
level.deathflags[ deathflag ][ "vehicle_spawners" ][ id ] = undefined;
update_deathflag( deathflag );
}
update_deathflag( deathflag )
{
level notify( "updating_deathflag_" + deathflag );
level endon( "updating_deathflag_" + deathflag );
// notify and endon and waittill so we only do this a max of once per frame
// even if multiple spawners or ai are killed in the same frame
// also gives ai a chance to spawn and be added to the ai deathflag array
waittillframeend;
foreach ( index, array in level.deathflags[ deathflag ] )
{
if ( getarraykeys( array ).size > 0 )
return;
}
/*
spawnerKeys = getarraykeys( level.deathflags[ deathflag ][ "spawners" ] );
if ( spawnerKeys.size > 0 )
return;
aiKeys = getarraykeys( level.deathflags[ deathflag ][ "ai" ] );
if ( aiKeys.size > 0 )
return;
*/
// all the spawners and ai are gone
flag_set( deathflag );
}
outdoor_think( trigger )
{
assert( ( trigger.spawnflags & 1 ) || ( trigger.spawnflags & 2 ) || ( trigger.spawnflags & 4 ), "trigger_outdoor at " + trigger.origin + " is not set up to trigger AI! Check one of the AI checkboxes on the trigger." );
trigger endon( "death" );
for ( ;; )
{
trigger waittill( "trigger", guy );
if ( !isAI( guy ) )
continue;
guy thread ignore_triggers( 0.15 );
guy disable_cqbwalk();
guy.wantShotgun = false;
}
}
indoor_think( trigger )
{
assert( ( trigger.spawnflags & 1 ) || ( trigger.spawnflags & 2 ) || ( trigger.spawnflags & 4 ), "trigger_indoor at " + trigger.origin + " is not set up to trigger AI! Check one of the AI checkboxes on the trigger." );
trigger endon( "death" );
for ( ;; )
{
trigger waittill( "trigger", guy );
if ( !isAI( guy ) )
continue;
guy thread ignore_triggers( 0.15 );
guy enable_cqbwalk();
guy.wantShotgun = true;
}
}
doAutoSpawn( spawner )
{
spawner endon( "death" );
self endon( "death" );
for ( ;; )
{
self waittill( "trigger" );
if ( !spawner.count )
return;
if ( self.target != spawner.targetname )
return;// manually disconnected
if ( isdefined( spawner.triggerUnlocked ) )
return;// manually disconnected
guy = spawner spawn_ai();
if ( spawn_failed( guy ) )
spawner notify( "spawn_failed" );
if ( isdefined( self.Wait ) && ( self.Wait > 0 ) )
wait( self.Wait );
}
}
trigger_spawner( trigger )
{
assertEx( isdefined( trigger.target ), "Triggers with flag TRIGGER_SPAWN at " + trigger.origin + " must target at least one spawner." );
//trigger endon( "death" );
random_killspawner = trigger.random_killspawner;
target = trigger.target;
trigger waittill( "trigger" );
trigger script_delay();
if ( isdefined( random_killspawner ) )
waittillframeend;// let our random killspawner fire before spawning guys
spawners = getentarray( target, "targetname" );
foreach ( spawner in spawners )
{
if ( spawner.code_classname == "script_vehicle" )
{
spawner thread maps\_vehicle::spawn_vehicle_and_gopath();
continue;
}
spawner thread trigger_spawner_spawns_guys();
}
}
trigger_spawner_spawns_guys()
{
self endon( "death" );
self script_delay();
if ( !isdefined( self ) )
return undefined;
if ( isdefined( self.script_drone ) )
{
spawned = dronespawn( self );
return undefined;
}
else
if ( !issubstr( self.classname, "actor" ) )
return undefined;
// catch for stealth
dontShareEnemyInfo = ( isdefined( self.script_stealth ) && flag( "_stealth_enabled" ) && !flag( "_stealth_spotted" ) );
if ( isdefined( self.script_forcespawn ) )
spawned = self stalingradSpawn( dontShareEnemyInfo );
else
spawned = self doSpawn( dontShareEnemyInfo );
return spawned;
}
trigger_spawner_reinforcement( trigger )
{
assertEx( isdefined( trigger.target ), "Triggers with flag TRIGGER_SPAWN at " + trigger.origin + " must target or link to at least one spawner." );
target = trigger.target;
targetsReinforcement = false;
spawners = getentarray( target, "targetname" );
foreach ( spawner in spawners )
{
if ( !isdefined( spawner.target ) )
continue;
reinforcement_spawner = getent( spawner.target, "targetname" );
if ( !isdefined( reinforcement_spawner ) )
{
if ( !isdefined( spawner.script_linkto ) )
continue;
reinforcement_spawner = spawner get_linked_ent();
if ( !isdefined( reinforcement_spawner ) )
continue;
if ( !isSpawner( reinforcement_spawner ) )
continue;
}
targetsReinforcement = true;
break;
}
assertEx( targetsReinforcement == true, "trigger_multiple_spawn_reinforcement trigger needs at least one AI to target a reinforcement spawner. You should just be using trigger_multiple_spawn in this case." );
trigger waittill( "trigger" );
trigger script_delay();
// get array again because some might have been killspawned
spawners = getentarray( target, "targetname" );
foreach ( spawner in spawners )
{
spawner thread trigger_reinforcement_spawn_guys();
}
}
trigger_reinforcement_spawn_guys()
{
// get the reinforcement spawner
reinforcement = self trigger_reinforcement_get_reinforcement_spawner();
// spawn the first guy
guy = self trigger_spawner_spawns_guys();
// if the guy failed to spawn then try to spawn the reinforcement
if ( !isdefined( guy ) )
{
// delete failed spawner
self delete();
if ( isdefined( reinforcement ) )
{
guy = reinforcement trigger_spawner_spawns_guys();
reinforcement delete();
// reinforcement guy failed to spawn too
if ( !isdefined( guy ) )
return;
}
else
return;
}
if ( !isdefined( reinforcement ) )
return;
guy waittill( "death" );
// could have been killspawned
if ( !isdefined( reinforcement ) )
return;
if ( !isdefined( reinforcement.count ) )
reinforcement.count = 1;
for(;;)
{
if ( !isdefined( reinforcement ) )
break;
spawned = reinforcement thread trigger_spawner_spawns_guys();
if ( !isdefined( spawned ) )
{
reinforcement delete();
break;
}
spawned thread reincrement_count_if_deleted( reinforcement );
spawned waittill( "death", attacker );
if ( !player_saw_kill( spawned, attacker ) )
{
println( "^3player didn't see kill, respawning the reinforcement" );
// could have been killspawned
if ( !isdefined( reinforcement ) )
break;
reinforcement.count++;
}
// soldier was deleted, not killed
if ( !isDefined( spawned ) )
continue;
if ( !isdefined( reinforcement ) )
break;
if ( !isdefined( reinforcement.count ) )
break;
if ( reinforcement.count <= 0 )
break;
if ( !script_wait() )
wait( randomFloatRange( 1, 3 ) );
}
if ( isdefined( reinforcement ) )
reinforcement delete();
}
trigger_reinforcement_get_reinforcement_spawner()
{
if ( isdefined( self.target ) )
{
reinforcement = getent( self.target, "targetname" );
if ( isdefined( reinforcement ) && isSpawner( reinforcement ) )
return reinforcement;
}
if ( isdefined( self.script_linkto ) )
{
reinforcement = self get_linked_ent();
if ( isdefined( reinforcement ) && isSpawner( reinforcement ) )
return reinforcement;
}
return undefined;
}
flood_spawner_scripted( spawners )
{
assertEX( isDefined( spawners ) && spawners.size, "Script tried to flood spawn without any spawners" );
array_thread( spawners, ::flood_spawner_init );
array_thread( spawners, ::flood_spawner_think );
}
reincrement_count_if_deleted( spawner )
{
spawner endon( "death" );
if ( isdefined( self.script_force_count ) )
if ( self.script_force_count )
return;
self waittill( "death" );
if ( !isDefined( self ) )
spawner.count++;
}
delete_start( startnum )
{
for ( p = 0;p < 2;p++ )
{
switch( p )
{
case 0:
aitype = "axis";
break;
default:
assert( p == 1 );
aitype = "allies";
break;
}
ai = getentarray( aitype, "team" );
for ( i = 0;i < ai.size;i++ )
{
if ( isdefined( ai[ i ].script_start ) )
if ( ai[ i ].script_start == startnum )
ai[ i ] thread delete_me();
}
}
}
kill_trigger( trigger )
{
if ( !isdefined( trigger ) )
return;
if ( ( isdefined( trigger.targetname ) ) && ( trigger.targetname != "flood_spawner" ) )
return;
// temporary
if ( level.script == "sniperescape" )
return;
trigger delete();
}
random_killspawner( trigger )
{
trigger endon( "death" );
random_killspawner = trigger.script_random_killspawner;
waittillframeend;// wait for spawners to setup spawn_groups so we can verify ours exists
if ( !isdefined( level.killspawn_groups[ random_killspawner ] ) )
return;
// assertex( isdefined( level.killspawn_groups[ random_killspawner ] ), "Trigger at " + trigger.origin + " has random_killspawner " + random_killspawner + ". There are no spawners with that random_killspawner value." );
trigger waittill( "trigger" );
cull_spawners_from_killspawner( random_killspawner );
/*
triggered_spawners = [];
spawners = getspawnerarray();
for ( i = 0 ; i < spawners.size ; i++ )
{
if ( ( isdefined( spawners[ i ].script_random_killspawner ) ) && ( random_killspawner == spawners[ i ].script_random_killspawner ) )
{
triggered_spawners = add_to_array( triggered_spawners, spawners[ i ] );
}
}
cull_spawners_leaving_one_set( triggered_spawners );
*/
}
cull_spawners_from_killspawner( random_killspawner )
{
if ( !isdefined( level.killspawn_groups[ random_killspawner ] ) )
return;
spawn_groups = level.killspawn_groups[ random_killspawner ];
keys = getarraykeys( spawn_groups );
if ( keys.size <= 1 )
return;
save_key = random( keys );
spawn_groups[ save_key ] = undefined;
// spawn_groups has several arrays of spawners in it
// the array we randomly want to keep has been removed
// so go through each array and delete all the spawners that remain.
foreach ( key, spawners in spawn_groups )
{
foreach ( index, spawner in spawners )
{
if ( isdefined( spawner ) )
spawner delete();
}
level.killspawn_groups[ random_killspawner ][ key ] = undefined;
}
}
killspawner( killspawnerNum )
{
println( "killing killspawner: " + killspawnerNum );
spawners = getspawnerarray();
for ( i = 0 ; i < spawners.size ; i++ )
{
if ( ( isdefined( spawners[ i ].script_killspawner ) ) && ( killspawnerNum == spawners[ i ].script_killspawner ) )
{
spawners[ i ] delete();
}
}
}
kill_spawner( trigger )
{
killspawnerNum = trigger.script_killspawner;
trigger waittill( "trigger" );
// wait twice so random killspawners can first kill selective spawners
// then the trigger could spawn guys, then the spawners will be deleted
waittillframeend;
waittillframeend;
killspawner( killspawnerNum );
kill_trigger( trigger );
}
empty_spawner( trigger )
{
emptyspawner = trigger.script_emptyspawner;
trigger waittill( "trigger" );
spawners = getspawnerarray();
for ( i = 0;i < spawners.size;i++ )
{
if ( !isdefined( spawners[ i ].script_emptyspawner ) )
continue;
if ( emptyspawner != spawners[ i ].script_emptyspawner )
continue;
if ( isdefined( spawners[ i ].script_flanker ) )
level notify( "stop_flanker_behavior" + spawners[ i ].script_flanker );
spawners[ i ] set_count( 0 );
spawners[ i ] notify( "emptied spawner" );
}
trigger notify( "deleted spawners" );
}
kill_spawnerNum( number )
{
spawners = getspawnerarray();
for ( i = 0;i < spawners.size;i++ )
{
if ( !isdefined( spawners[ i ].script_killspawner ) )
continue;
if ( number != spawners[ i ].script_killspawner )
continue;
spawners[ i ] delete();
}
}
trigger_spawn( trigger )
{
/*
if ( isdefined( trigger.target ) )
{
spawners = getentarray( trigger.target, "targetname" );
for ( i = 0;i < spawners.size;i++ )
if ( ( spawners[ i ].team == "axis" ) || ( spawners[ i ].team == "allies" ) || ( spawners[ i ].team == "team3" ) )
level thread spawn_prethink( spawners[ i ] );
}
*/
}
// spawn maximum 16 grenades per team
spawn_grenade( origin, team )
{
// delete oldest grenade
if ( !isdefined( level.grenade_cache ) || !isdefined( level.grenade_cache[ team ] ) )
{
level.grenade_cache_index[ team ] = 0;
level.grenade_cache[ team ] = [];
}
index = level.grenade_cache_index[ team ];
grenade = level.grenade_cache[ team ][ index ];
if ( isdefined( grenade ) )
grenade delete();
grenade = spawn( "weapon_fraggrenade", origin );
level.grenade_cache[ team ][ index ] = grenade;
level.grenade_cache_index[ team ] = ( index + 1 ) % 16;
return grenade;
}
waittillDeathOrPainDeath()
{
self endon( "death" );
self waittill( "pain_death" );// pain that ends in death
}
drop_gear()
{
team = self.team;
waittillDeathOrPainDeath();
if ( !isdefined( self ) )
return;
if ( isdefined( self.noDrop ) )
return;
/*
if ( level.tire_explosion )
{
org = self.origin;
eye = self geteye();
// try to fix the delete ai during think error
waittillframeend;
for ( i = 0; i < 15; i++ )
{
thread random_tire( org, eye );
}
if ( isdefined( self ) )
{
//self hide();
self animscripts\shared::DropAllAIWeapons();
self delete();
}
return;
}
*/
self.ignoreForFixedNodeSafeCheck = true;
if ( self.grenadeAmmo <= 0 )
return;
level.nextGrenadeDrop -- ;
if ( level.nextGrenadeDrop > 0 )
return;
level.nextGrenadeDrop = 2 + randomint( 2 );
max = 25;
min = 12;
org = self.origin + ( randomint( max ) - min, randomint( max ) - min, 2 ) + ( 0, 0, 42 );
ang = ( 0, randomint( 360 ), 90 );
thread spawn_grenade_bag( org, ang, self.team );
}
random_tire( start, end )
{
if ( level.cheattirecount > 90 )
return;
level.cheattirecount++;
model = spawn( "script_model", ( 0, 0, 0 ) );
model.angles = ( 0, randomint( 360 ), 0 );
dif = randomfloat( 1 );
model.origin = start * dif + end * ( 1 - dif );
model setmodel( "com_junktire" );
vel = randomvector( 15000 );
vel = ( vel[ 0 ], vel[ 1 ], abs( vel[ 2 ] ) );
model PhysicsLaunchClient( model.origin, vel );
wait( randomintrange( 8, 12 ) );
level.cheattirecount -- ;
model delete();
}
spawn_grenade_bag( org, angles, team )
{
grenade = spawn_grenade( org, team );
grenade setmodel( "grenade_bag" );
grenade.angles = angles;
// grenade ammo determined by weapon settings
grenade hide(); // looks bad when it pops out of nowhere
wait( 0.7 );
if ( !isdefined( grenade ) )
return;
grenade show();
}
dronespawner_init()
{
self maps\_drone::drone_init_path();
}
empty()
{
}
spawn_prethink()
{
assert( self != level );
level.ai_classname_in_level[ self.classname ] = true;
/#
if ( getdvar( "noai", "off" ) != "off" )
{
// NO AI in the level plz
self set_count( 0 );
return;
}
#/
prof_begin( "spawn_prethink" );
if ( isdefined( self.script_difficulty ) )
{
switch( self.script_difficulty )
{
case "easy":
if ( level.gameSkill > 1 )// if on hard or veteran
{
self set_count( 0 );
}
break;
case "hard":
if ( level.gameSkill < 2 )// if on easy or regular
{
self set_count( 0 );
}
break;
}
}
if ( isdefined( self.script_drone ) )
self thread dronespawner_init();
if ( isdefined( self.script_aigroup ) )
{
aigroup = self.script_aigroup;
if ( !isdefined( level._ai_group[ aigroup ] ) )
aigroup_create( aigroup );
self thread aigroup_spawnerthink( level._ai_group[ aigroup ] );
}
if ( isdefined( self.script_delete ) )
{
array_size = 0;
if ( isdefined( level._ai_delete ) )
if ( isdefined( level._ai_delete[ self.script_delete ] ) )
array_size = level._ai_delete[ self.script_delete ].size;
level._ai_delete[ self.script_delete ][ array_size ] = self;
}
if ( isdefined( self.script_health ) )
{
if ( self.script_health > level._max_script_health )
level._max_script_health = self.script_health;
array_size = 0;
if ( isdefined( level._ai_health ) )
if ( isdefined( level._ai_health[ self.script_health ] ) )
array_size = level._ai_health[ self.script_health ].size;
level._ai_health[ self.script_health ][ array_size ] = self;
}
if ( isdefined( self.script_deathflag ) )
{
// sets this flag when all the spawners or ai with this flag are gone
thread spawner_deathflag();
}
if ( isdefined( self.target ) )
{
crawl_through_targets_to_init_flags();
}
if ( isdefined( self.script_spawngroup ) )
{
self add_to_spawngroup();
}
if ( isdefined( self.script_random_killspawner ) )
{
self add_random_killspawner_to_spawngroup();
}
/*
// all guns are setup by default now
// portable mg42 guys
if ( issubstr( self.classname, "mgportable" ) || issubstr( self.classname, "30cal" ) )
thread mg42setup_gun();
*/
if ( !isdefined( self.spawn_functions ) )
{
self.spawn_functions = [];
}
for ( ;; )
{
prof_begin( "spawn_prethink" );
spawn = undefined;
self waittill( "spawned", spawn );
if ( !isalive( spawn ) )
continue;
if ( isdefined( level.spawnerCallbackThread ) ) // this looks like pre - spawnfunc functionality, should be depricated
self thread [[ level.spawnerCallbackThread ]]( spawn );
if ( isdefined( self.script_delete ) )
{
for ( i = 0;i < level._ai_delete[ self.script_delete ].size;i++ )
{
if ( level._ai_delete[ self.script_delete ][ i ] != self )
level._ai_delete[ self.script_delete ][ i ] delete();
}
}
spawn.spawn_funcs = self.spawn_functions;
// stored temporarily so spawn functions can use it if they want it
spawn.spawner = self;
if ( isdefined( self.targetname ) )
spawn thread spawn_think( self.targetname );
else
spawn thread spawn_think();
}
}
// Wrapper for spawn_think
// should change this so run_spawn_functions() can also work on drones
// currently assumes AI
spawn_think( targetname )
{
assert( self != level );
level.ai_classname_in_level[ self.classname ] = true;
spawn_think_action( targetname );
assert( isalive( self ) );
self endon( "death" );
if ( shouldnt_spawn_because_of_script_difficulty() )
{
self delete();
assertEx( 0, "Should never get here" );
}
thread run_spawn_functions();
self.finished_spawning = true;
self notify( "finished spawning" );
assert( isdefined( self.team ) );
if ( self.team == "allies" && !isdefined( self.script_nofriendlywave ) )
self thread friendlydeath_thread();
}
shouldnt_spawn_because_of_script_difficulty()
{
//set .script_difficulty = "hard" to make AI not spawn in normal or easy
if ( !isdefined( self.script_difficulty ) )
return false;
should_delete = false;
switch( self.script_difficulty )
{
case "easy":
if ( level.gameSkill > 1 )// if on hard or veteran
{
should_delete = true;
}
break;
case "hard":
if ( level.gameSkill < 2 )// if on easy or regular
{
should_delete = true;
}
break;
}
return should_delete;
}
run_spawn_functions()
{
if ( !isdefined( self.spawn_funcs ) )
{
self.spawner = undefined;
return;
}
/*
if ( isdefined( self.script_vehicleride ) )
{
// guys that ride in a vehicle down run their spawn funcs until they land.
self endon( "death" );
self waittill( "jumpedout" );
}
*/
for ( i = 0; i < self.spawn_funcs.size; i++ )
{
func = self.spawn_funcs[ i ];
if ( isdefined( func[ "param5" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ], func[ "param3" ], func[ "param4" ], func[ "param5" ] );
else
if ( isdefined( func[ "param4" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ], func[ "param3" ], func[ "param4" ] );
else
if ( isdefined( func[ "param3" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ], func[ "param3" ] );
else
if ( isdefined( func[ "param2" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ] );
else
if ( isdefined( func[ "param1" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ] );
else
thread [[ func[ "function" ] ]]();
}
if ( isdefined( self.team ) )
{
// vehicles have no self team
for ( i = 0; i < level.spawn_funcs[ self.team ].size; i++ )
{
func = level.spawn_funcs[ self.team ][ i ];
if ( isdefined( func[ "param5" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ], func[ "param3" ], func[ "param4" ], func[ "param5" ] );
else
if ( isdefined( func[ "param4" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ], func[ "param3" ], func[ "param4" ] );
else
if ( isdefined( func[ "param3" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ], func[ "param3" ] );
else
if ( isdefined( func[ "param2" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ], func[ "param2" ] );
else
if ( isdefined( func[ "param1" ] ) )
thread [[ func[ "function" ] ]]( func[ "param1" ] );
else
thread [[ func[ "function" ] ]]();
}
}
/#
self.saved_spawn_functions = self.spawn_funcs;
#/
self.spawn_funcs = undefined;
// if you want to use the .spawner as reference then you need to yank it
// at the top of the spawn function, for var space sake.
self.spawner = undefined;
/#
// keep them around in developer mode, for debugging
self.spawn_funcs = self.saved_spawn_functions;
self.saved_spawn_functions = undefined;
#/
}
specops_think()
{
if ( !is_specialop() )
{
return;
}
self add_damage_function( ::specops_dmg );
}
// Keeps track of who last did damage to the given AI, and awards that person with the kill
specops_dmg( dmg, attacker, dir, point, type, model_name, tag_name )
{
if ( !IsDefined( self ) )
{
return;
}
if ( IsDefined( attacker ) && IsPlayer( attacker ) )
{
self.last_dmg_player = attacker;
self.last_dmg_type = type;
}
}
// the functions that run on death for the ai
deathFunctions()
{
self waittill( "death", attacker, cause );
level notify( "ai_killed", self );
if ( !IsDefined( self ) )
{
return;
}
if ( IsDefined( attacker ) )
{
if ( self.team == "axis" || self.team == "team3" )
{
// If the attacker is a vehicle, and the player is the owner, make the player the attacker
if ( attacker.code_classname == "script_vehicle" )
{
owner = attacker GetVehicleOwner();
if ( IsDefined( owner ) )
{
attacker = owner;
}
}
validAttacker = false;
if ( isplayer( attacker ) )
validAttacker = true;
if ( isdefined( level.pmc_match ) && level.pmc_match )
validAttacker = true;
if ( validAttacker )
{
level notify( "specops_player_kill", attacker );
attacker maps\_player_stats::register_kill( self, cause );
}
}
}
for ( i = 0; i < self.deathFuncs.size; i++ )
{
array = self.deathFuncs[ i ];
switch( array[ "params" ] )
{
case 0:
[[ array[ "func" ] ]]( attacker );
break;
case 1:
[[ array[ "func" ] ]]( attacker, array[ "param1" ] );
break;
case 2:
[[ array[ "func" ] ]]( attacker, array[ "param1" ], array[ "param2" ] );
break;
case 3:
[[ array[ "func" ] ]]( attacker, array[ "param1" ], array[ "param2" ], array[ "param3" ] );
break;
}
}
}
AI_damage_think()
{
// don't end on death
self.damage_functions = [];
for ( ;; )
{
self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName );
if ( isdefined( attacker ) && isPlayer( attacker ) )
attacker thread maps\_player_stats::register_shot_hit();
foreach ( func in self.damage_functions )
{
thread [[ func ]]( damage, attacker, direction_vec, point, type, modelName, tagName );
}
if ( !isalive( self ) || self.delayeddeath )
break;
}
}
living_ai_prethink()
{
if ( isdefined( self.script_deathflag ) )
{
// later this is turned into the real ddeathflag array
level.deathflags[ self.script_deathflag ] = true;
}
if ( isdefined( self.target ) )
{
crawl_through_targets_to_init_flags();
}
}
crawl_through_targets_to_init_flags()
{
// need to initialize flags on the path chain if need be
array = get_node_funcs_based_on_target();
if ( isdefined( array ) )
{
targets = array[ "destination" ];
get_func = array[ "get_target_func" ];
for ( i = 0; i < targets.size; i++ )
{
crawl_target_and_init_flags( targets[ i ], get_func );
}
}
}
spawn_team_allies()
{
self.useChokePoints = false;
// Set the followmin for friendlies
if ( isdefined( self.script_followmin ) )
self.followmin = self.script_followmin;
// Set the followmax for friendlies
if ( isdefined( self.script_followmax ) )
self.followmax = self.script_followmax;
}
spawn_team_axis()
{
// xp
if ( getdvar( "xp_enable", "0" ) == "1" )
self thread maps\_rank::AI_xp_init();
// money
if ( getdvar( "money_enable", "0" ) == "1" )
self thread maps\_money::AI_money_init();
if ( self.type == "human" )
self thread drop_gear();
self add_damage_function( maps\_gameskill::auto_adjust_enemy_death_detection );
if( IsDefined( self.script_combatmode ) )
{
self.combatMode = self.script_combatmode;
}
/#
// for combat mode testing
if ( getdvar( "scr_force_ai_combat_mode" ) == "ambush" )
self.combatMode = "ambush";
else if ( getdvar( "scr_force_ai_combat_mode" ) == "ambush_nodes_only" )
self.combatMode = "ambush_nodes_only";
#/
}
spawn_team_team3()
{
self spawn_team_axis();
}
spawn_team_neutral()
{
}
subclass_elite()
{
self endon( "death" );
self.elite = true;
self.doorFlashChance = .5;
if ( !isdefined( self.script_accuracy ) )
self.baseaccuracy = 5;
self.aggressivemode = true;
//give flashbanks if they have appropriate weapons
if ( self has_shotgun() )
{
flashAmmo = undefined;
switch( level.gameSkill )
{
case 0:// easy
flashAmmo = 0;
break;
case 1:// regular
flashAmmo = 2;
break;
case 2:// hardened
flashAmmo = 3;
break;
case 3:// veteran
flashAmmo = 4;
break;
}
if ( level.gameSkill > 0 )
{
self.grenadeWeapon = "flash_grenade";
self.grenadeAmmo = flashAmmo;
}
}
}
subclass_regular()
{
}
pain_resistance( damage, attacker, direction_vec, point, type, modelName, tagName )
{
self endon( "death" );
if ( self.health <= 0 )
return;
if ( damage >= self.minPainDamage )
{
old_amount = self.minPainDamage;
self.minPainDamage = ( old_amount * 3 );
wait 5;
self.minPainDamage = old_amount;
}
}
bullet_resistance( damage, attacker, direction_vec, point, type, modelName, tagName )
{
assertex( isdefined( self.bullet_resistance ), "bullet_resistance add_damage_function must be called on guys with self.bullet_resistance = n" );
if ( !isdefined( self ) || self.health <= 0 )
return;
if ( ! issubstr( type, "BULLET" ) )
return;
heal_amount = self.bullet_resistance;
if ( damage < self.bullet_resistance )
heal_amount = damage;
self.health += heal_amount;
}
spawn_think_game_skill_related()
{
//added .doorFragChance and .doorFlashChance for throwing frag/flash grenades through doors.
//Set it to a value between 0 and 1; 0 for never, 1 for always if possible.
//add script override check here if needed.
maps\_gameskill::default_door_node_flashbang_frequency();
maps\_gameskill::grenadeAwareness();
}
ai_lasers()
{
if ( !isalive( self ) )
return;
if ( self.health <= 1 ) // dying soon
return;
self LaserForceOn();
self waittill( "death" );
if ( !isdefined( self ) )
return;
self LaserForceOff();
}
spawn_think_script_inits()
{
if ( isdefined( self.script_dontshootwhilemoving ) )
{
self.dontshootwhilemoving = true;
}
if ( isdefined( self.script_deathflag ) )
{
thread ai_deathflag();
}
if ( isdefined( self.script_attackeraccuracy ) )
{
self.attackeraccuracy = self.script_attackeraccuracy;
}
if ( isdefined( self.script_startrunning ) )
{
self thread start_off_running();
}
if ( isdefined( self.script_deathtime ) )
{
self thread deathtime();
}
if ( isdefined( self.script_nosurprise ) )
{
self disable_surprise();
}
if ( isdefined( self.script_nobloodpool ) )
{
self.skipBloodPool = true;
}
if ( isdefined( self.script_laser ) )
{
self thread ai_lasers();
}
if ( isdefined( self.script_danger_react ) )
{
time = self.script_danger_react;
if ( time == 1 )
time = 8;
self enable_danger_react( time );
}
if ( isdefined( self.script_faceenemydist ) )
{
self.maxFaceEnemyDist = self.script_faceenemydist;
}
else
{
self.maxFaceEnemyDist = 512; // the code default!
}
// send all forcecolor through a centralized function
if ( isdefined( self.script_forceColor ) )
{
set_force_color( self.script_forceColor );
}
if ( isdefined( self.dontDropWeapon ) )
{
self.dropWeapon = false;
}
if ( isdefined( self.script_fixednode ) )
{
self.fixednode = ( self.script_fixednode == 1 );
}
else
{
self.fixednode = self.team == "allies";
}
self.provideCoveringFire = self.team == "allies" && self.fixedNode;
if ( isdefined( self.script_noteworthy ) && self.script_noteworthy == "mgpair" )
{
// mgpair guys get angry when their fellow buddy dies
thread maps\_mg_penetration::create_mg_team();
}
//if script_moveoverride is on an AI - then dont set his goalvolume, because most likely he doesn't have a goal inside the volume
//if script_stealth is set then don't give him a goalvolume, because we assume we want him to FIGHT in the goal volume when stealth is broken, not at spawn.
if ( isdefined( self.script_goalvolume ) && !( ( isdefined( self.script_moveoverride ) && self.script_moveoverride == 1 ) || isdefined( self.script_stealth ) ) )
{
// wait until frame end so that the AI's goal has a chance to get set
thread set_goal_volume();
}
// create threatbiasgroups
if ( isdefined( self.script_threatbiasgroup ) )
self setthreatbiasgroup( self.script_threatbiasgroup );
else if ( self.team == "neutral" )
self setthreatbiasgroup( "civilian" );
else
self setthreatbiasgroup( self.team );
if ( isdefined( self.script_bcdialog ) )
{
self set_battlechatter( self.script_bcdialog );
}
if ( isdefined( self.script_accuracy ) )
{
self.baseAccuracy = self.script_accuracy;
}
if ( isdefined( self.script_ignoreme ) )
{
assertEx( self.script_ignoreme == true, "Tried to set self.script_ignoreme to false, not allowed. Just set it to undefined." );
self.ignoreme = true;
}
if ( isdefined( self.script_ignore_suppression ) )
{
assertEx( self.script_ignore_suppression == true, "Tried to set self.script_ignore_suppresion to false, not allowed. Just set it to undefined." );
self.ignoreSuppression = true;
}
if ( isdefined( self.script_ignoreall ) )
{
assertEx( self.script_ignoreall == true, "Tried to set self.script_ignoreme to false, not allowed. Just set it to undefined." );
self.ignoreall = true;
self clearenemy();
}
if ( isdefined( self.script_sightrange ) )
{
self.maxSightDistSqrd = self.script_sightrange;
}
// sets the favorite enemy of a spawner
if ( isdefined( self.script_favoriteenemy ) )
{
if ( self.script_favoriteenemy == "player" )
{
self.favoriteenemy = level.player;
level.player.targetname = "player";
}
}
if ( isdefined( self.script_fightdist ) )
{
self.pathenemyfightdist = self.script_fightdist;
}
if ( isdefined( self.script_maxdist ) )
{
self.pathenemylookahead = self.script_maxdist;
}
// disable long death like dying pistol behavior
if ( isdefined( self.script_longdeath ) )
{
assertex( !self.script_longdeath, "Long death is enabled by default so don't set script_longdeath to true, check ai with export " + self.export );
self.a.disableLongDeath = true;
assertEX( self.team != "allies", "Allies can't do long death, so why disable it on guy with export " + self.export );
}
if ( isdefined( self.script_diequietly ) )
{
assertex( self.script_diequietly, "Quiet deaths are disabled by default so don't set script_diequietly to false, check ai with export " + self.export );
self.dieQuietly = true;
}
if ( isdefined( self.script_flashbangs ) )
{
self.grenadeWeapon = "flash_grenade";
self.grenadeAmmo = self.script_flashbangs;
}
// Puts AI in pacifist mode
if ( isdefined( self.script_pacifist ) )
{
self.pacifist = true;
}
// Set the health for special cases
if ( isdefined( self.script_startinghealth ) )
{
self.health = self.script_startinghealth;
}
if ( isdefined( self.script_nodrop ) )
{
self.nodrop = self.script_nodrop;
}
/#
if ( getdvarint( "scr_heat" ) == 1 )
self enable_heat_behavior();
#/
}
/#
spawn_think_debug_checks()
{
if ( getdebugdvar( "debug_misstime" ) == "start" )
self thread maps\_debug::debugMisstime();
thread show_bad_path();
if ( self.type == "human" )
assertEx( self.pathEnemyLookAhead == 0 && self.pathEnemyFightDist == 0, "Tried to change pathenemyFightDist or pathenemyLookAhead on an AI before running spawn_failed on guy with export " + self.export );
}
#/
// Actually do the spawn_think
spawn_think_action( targetname )
{
// handle default ai flags for ent_flag * functions
self thread AI_damage_think();
self thread tanksquish();
self thread death_achievements();
self thread specops_think();
//dont call this if you dont want AI guy to glow when the player uses thermal vision. Ai only glow when player is in thermal.
if( !isdefined( level.ai_dont_glow_in_thermal ) )
self ThermalDrawEnable();
// ai get their values from spawners and theres no need to have this value on ai
self.spawner_number = undefined;
if ( !isdefined( self.unique_id ) )
{
set_ai_number();
}
// functions called on death
if ( !isdefined( self.deathFuncs ) )
{
self.deathFuncs = [];
}
self thread deathFunctions();
level thread maps\_friendlyfire::friendly_fire_think( self );
self.walkdist = 16;
// which eq triggers am I touching?
//thread setup_ai_eq_triggers();
/# spawn_think_debug_checks(); #/
init_reset_AI();
spawn_think_game_skill_related();
spawn_think_script_inits();
[[ level.team_specific_spawn_functions[ self.team ] ]]();
// special function for this AI's subclass, juggernaut, etc
assertex( isdefined( level.subclass_spawn_functions[ self.subclass ] ), "subclass spawn function not defined for '" + self.subclass + "'" );
thread [[ level.subclass_spawn_functions[ self.subclass ] ]]();
self thread maps\_damagefeedback::monitorDamage();
self common_scripts\_dynamic_world::ai_init();
set_goal_height_from_settings();
//
// lots of returns from this point on. spawn_think_action may early out at any point.
//
// The AI will spawn and attack the player
if ( isdefined( self.script_playerseek ) )
{
self setgoalentity( level.player );
return;
}
// the AI will be linked into the stealth system
if ( isdefined( self.script_stealth ) )
{
if ( isdefined( self.script_stealth_function ) )
{
assertex( isdefined( level.stealth_default_func[ self.script_stealth_function ] ), "spawner at " + self.origin + " has .script_stealth_function set to key of '" + self.script_stealth_function + "' but there is no reference for that key. Use stealth_set_default_stealth_function( key, func ) to set the key to a proper stealth function" );
func = level.stealth_default_func[ self.script_stealth_function ];
self thread [[ func ]]();
}
else
self thread [[ level.global_callbacks[ "_spawner_stealth_default" ] ]]();
}
if ( isdefined( self.script_idleanim ) )
{
self thread [[ level.global_callbacks[ "_idle_call_idle_func" ] ]]();
return;
}
if ( isdefined( self.script_idlereach ) && !isdefined( self.script_moveoverride ) )
{
self thread [[ level.global_callbacks[ "_idle_call_idle_func" ] ]]();
}
// The AI will spawn and follow a patrol
if ( isdefined( self.script_patroller ) && !isdefined( self.script_moveoverride ) )
{
self thread maps\_patrol::patrol();
return;
}
// The AI will spawn and use his .radius as a goalradius, and his goalradius will get smaller over time.
if ( isdefined( self.script_delayed_playerseek ) )
{
if ( !isdefined( self.script_radius ) )
self.goalradius = 800;
self setgoalentity( level.player );
level thread delayed_player_seek_think( self );
return;
}
if ( isdefined( self.used_an_mg42 ) )// This AI was called upon to use an MG42 so he's not going to run to his node.
{
return;
}
if ( ( isdefined( self.script_moveoverride ) && self.script_moveoverride == 1 ) )
{
set_goal_from_settings();
self setgoalpos( self.origin );
return;
}
assertEx( self.goalradius == 4, "Changed the goalradius on guy with export " + self.export + " without waiting for spawn_failed. Note that this change will NOT show up by putting a breakpoint on the actors goalradius field because breakpoints don't properly handle the first frame an actor exists." );
set_goal_from_settings();
// The AI will run to a target node and use the node's .radius as his goalradius.
// If script_seekgoal is set, then he will run to his node with a goalradius of 0, then set his goal radius
// to the node's radius.
if ( isdefined( self.target ) )
self thread go_to_node();
}
// this is called during init (spawn_think_action) and reset (scrub_guy)
init_reset_AI()
{
self eqoff();
set_default_pathenemy_settings();
// Gives AI grenades
if ( isdefined( self.script_grenades ) )
{
self.grenadeAmmo = self.script_grenades;
}
else
{
self.grenadeAmmo = 3;
}
if ( isdefined( self.primaryweapon ) )
self.noAttackerAccuracyMod = self animscripts\combat_utility::isSniper();
if ( !is_specialop() )
self.neverSprintForVariation = true;
}
// reset this guy to default spec
scrub_guy()
{
if ( self.team == "neutral" )
self setthreatbiasgroup( "civilian" );
else
self setthreatbiasgroup( self.team );
init_reset_AI();
// Set the accuracy for the spawner
self.baseAccuracy = 1;
maps\_gameskill::grenadeAwareness();
self clear_force_color();
self.interval = 96;
self.disableArrivals = undefined;
self.ignoreme = false;
self.threatbias = 0;
self.pacifist = false;
self.pacifistWait = 20;
self.IgnoreRandomBulletDamage = false;
self.pushable = true;
// self.favoriteenemy = undefined;
self.accuracystationarymod = 1;
self.allowdeath = false;
self.anglelerprate = 540;
self.badplaceawareness = 0.75;
self.chainfallback = 0;
self.dontavoidplayer = 0;
self.drawoncompass = 1;
self.dropweapon = 1;
self.goalradius = level.default_goalradius;
self.goalheight = level.default_goalheight;
self.ignoresuppression = 0;
self pushplayer( false );
if ( isdefined( self.magic_bullet_shield ) && self.magic_bullet_shield )
{
stop_magic_bullet_shield();
}
self disable_replace_on_death();
self.maxsightdistsqrd = 8192 * 8192;
self.script_forceGrenade = 0;
self.walkdist = 16;
self unmake_hero();
self.pushable = true;
animscripts\init::set_anim_playback_rate();
// allies use fixednode by default
self.fixednode = self.team == "allies";
}
delayed_player_seek_think( spawned )
{
spawned endon( "death" );
while ( isalive( spawned ) )
{
if ( spawned.goalradius > 200 )
spawned.goalradius -= 200;
wait 6;
}
}
flag_turret_for_use( ai )
{
self endon( "death" );
if ( !self.flagged_for_use )
{
ai.used_an_mg42 = true;
self.flagged_for_use = true;
ai waittill( "death" );
self.flagged_for_use = false;
self notify( "get new user" );
return;
}
println( "Turret was already flagged for use" );
}
set_goal_volume()
{
self endon( "death" );
waittillframeend;
volume = level.goalVolumes[ self.script_goalvolume ];
if ( !isdefined( volume ) )
return;
if ( isdefined( volume.target ) )
{
node = getnode( volume.target, "targetname" );
ent = getent( volume.target, "targetname" );
struct = getstruct( volume.target, "targetname" );
pos = undefined;
if ( isdefined( node ) )
{
pos = node;
self setgoalnode( pos );
}
else
if ( isdefined( ent ) )
{
pos = ent;
self setgoalpos( pos.origin );
}
else
if ( isdefined( struct ) )
{
pos = struct;
self setgoalpos( pos.origin );
}
if ( isdefined( pos.radius ) && pos.radius != 0 )
self.goalradius = pos.radius;
if ( isdefined( pos.goalheight ) && pos.goalheight != 0 )
self.goalheight = pos.goalheight;
}
if ( isdefined( self.target ) )
{
self setgoalvolume( volume );
}
else
{
self setgoalvolumeauto( volume );
}
}
get_target_ents( target )
{
return getentarray( target, "targetname" );
}
get_target_nodes( target )
{
return getnodearray( target, "targetname" );
}
get_target_structs( target )
{
return getstructarray( target, "targetname" );
}
node_has_radius( node )
{
return isdefined( node.radius ) && node.radius != 0;
}
go_to_origin( node, optional_arrived_at_node_func )
{
self go_to_node( node, "origin", optional_arrived_at_node_func );
}
go_to_struct( node, optional_arrived_at_node_func )
{
self go_to_node( node, "struct", optional_arrived_at_node_func );
}
go_to_node( node, goal_type, optional_arrived_at_node_func, require_player_dist )
{
if ( isdefined( self.used_an_mg42 ) )// This AI was called upon to use an MG42 so he's not going to run to his node.
return;
array = get_node_funcs_based_on_target( node, goal_type );
if ( !isdefined( array ) )
{
self notify( "reached_path_end" );
// no goal type
return;
}
if ( !isdefined( optional_arrived_at_node_func ) )
{
optional_arrived_at_node_func = ::empty_arrived_func;
}
go_to_node_using_funcs( array[ "destination" ], array[ "get_target_func" ], array[ "set_goal_func_quits" ], optional_arrived_at_node_func, require_player_dist );
}
empty_arrived_func( node )
{
}
get_least_used_from_array( array )
{
assertex( array.size > 0, "Somehow array had zero entrees" );
if ( array.size == 1 )
return array[ 0 ];
targetname = array[ 0 ].targetname;
if ( !isdefined( level.go_to_node_arrays[ targetname ] ) )
{
level.go_to_node_arrays[ targetname ] = array;
}
array = level.go_to_node_arrays[ targetname ];
// return the node at the front of the array and move it to the back of the array.
first = array[ 0 ];
newarray = [];
for ( i = 0; i < array.size - 1; i++ )
{
newarray[ i ] = array[ i + 1 ];
}
newarray[ array.size - 1 ] = array[ 0 ];
level.go_to_node_arrays[ targetname ] = newarray;
return first;
}
go_to_node_using_funcs( node, get_target_func, set_goal_func_quits, optional_arrived_at_node_func, require_player_dist )
{
self notify( "stop_going_to_node" );// kills the last call to go_to_node
// AI is moving to a goal node
self endon( "stop_going_to_node" );
self endon( "death" );
for ( ;; )
{
// node should always be an array at this point, so lets get just 1 out of the array
node = get_least_used_from_array( node );
player_wait_dist = require_player_dist;
if( isdefined( node.script_requires_player ) )
{
if( node.script_requires_player > 1 )
player_wait_dist = node.script_requires_player;
node.script_requires_player = false;
}
if ( node_has_radius( node ) )
self.goalradius = node.radius;
else
self.goalradius = level.default_goalradius;
if ( isdefined( node.height ) )
self.goalheight = node.height;
else
self.goalheight = level.default_goalheight;
[[ set_goal_func_quits ]]( node );
//actually see if we're at our goal..._stealth might be tricking us
if ( self ent_flag_exist( "_stealth_override_goalpos" ) )
{
while ( 1 )
{
self waittill( "goal" );
if ( !( self ent_flag( "_stealth_override_goalpos" ) ) )
break;
self ent_flag_waitopen( "_stealth_override_goalpos" );
}
}
else
self waittill( "goal" );
node notify( "trigger", self );
[[ optional_arrived_at_node_func ]]( node );
if ( isdefined( node.script_flag_set ) )
{
flag_set( node.script_flag_set );
}
if ( isdefined( node.script_ent_flag_set ) )
{
self ent_flag_set( node.script_ent_flag_set );
}
if ( isdefined( node.script_flag_clear ) )
{
flag_clear( node.script_flag_clear );
}
if ( targets_and_uses_turret( node ) )
return true;
node script_delay();
if ( isdefined( node.script_flag_wait ) )
flag_wait( node.script_flag_wait );
if ( isdefined( node.script_delay_post ) )
wait node.script_delay_post;
while ( isdefined( node.script_requires_player ) )
{
node.script_requires_player = false;
if ( self go_to_node_wait_for_player( node, get_target_func, player_wait_dist ) )
{
node.script_requires_player = true;
node notify( "script_requires_player" );
break;
}
wait 0.1;
}
if ( !isdefined( node.target ) )
break;
nextNode_array = [[ get_target_func ]]( node.target );
if ( !nextNode_array.size )
break;
node = nextNode_array;
}
self notify( "reached_path_end" );
if ( isDefined( self.script_forcegoal ) )
return;
if ( isdefined( self getGoalVolume() ) )
self setGoalVolumeAuto( self getGoalVolume() );
else
self.goalradius = level.default_goalradius;
}
go_to_node_wait_for_player( node, get_target_func, dist )
{
//are any of the players closer to the node than we are?
foreach ( player in level.players )
{
if ( distancesquared( player.origin, node.origin ) < distancesquared( self.origin, node.origin ) )
return true;
}
//are any of the player ahead of us based on our forward angle?
vec = anglestoforward( self.angles );
if ( isdefined( node.target ) )
{
temp = [[ get_target_func ]]( node.target );
//if we only have one node then we can get the forward from that one to us
if ( temp.size == 1 )
vec = vectornormalize( temp[ 0 ].origin - node.origin );
//otherwise since we dont know which one we're taking yet the next best thing to do is to take the forward of the node we're on
else if ( isdefined( node.angles ) )
vec = anglestoforward( node.angles );
}
//also if there is no target since we're at the end of the chain, the next best thing to do is to take the forward of the node we're on
else if ( isdefined( node.angles ) )
vec = anglestoforward( node.angles );
vec2 = [];
foreach ( player in level.players )
{
vec2[ vec2.size ] = vectornormalize( ( player.origin - self.origin ) );
}
//i just created a vector which is in the direction i want to
//go, lets see if the player is closer to our goal than we are
foreach ( value in vec2 )
{
if ( vectordot( vec, value ) > 0 )
return true;
}
//ok so that just checked if he was a mile away but more towards the target
//than us...but we dont want him to be right on top of us before we start moving
//so lets also do a distance check to see if he's close behind
dist2rd = dist * dist;
foreach ( player in level.players )
{
if ( distancesquared( player.origin, self.origin ) < dist2rd )
return true;
}
//ok guess he's not here yet
return false;
}
go_to_node_set_goal_ent( ent )
{
if ( ent.classname == "info_volume" )
{
self setgoalvolumeauto( ent );
self notify( "go_to_node_new_goal" );
return;
}
self go_to_node_set_goal_pos( ent );
}
go_to_node_set_goal_pos( ent )
{
self set_goal_ent( ent );//this change by Mo should allow stealth and dynamic run speed to use structs for follow_path. reverted because of GI build
//self set_goal_pos( ent.origin );
self notify( "go_to_node_new_goal" );
}
go_to_node_set_goal_node( node )
{
self set_goal_node( node );
self notify( "go_to_node_new_goal" );
}
targets_and_uses_turret( node )
{
if ( !isdefined( node.target ) )
return false;
turrets = getentarray( node.target, "targetname" );
if ( !turrets.size )
return false;
turret = turrets[ 0 ];
if ( turret.classname != "misc_turret" )
return false;
thread use_a_turret( turret );
return true;
}
remove_crawled( ent )
{
waittillframeend;
if ( isdefined( ent ) )
ent.crawled = undefined;
}
crawl_target_and_init_flags( ent, get_func )
{
oldsize = 0;
targets = [];
index = 0;
for ( ;; )
{
if ( !isdefined( ent.crawled ) )
{
ent.crawled = true;
level thread remove_crawled( ent );
if ( isdefined( ent.script_flag_set ) )
{
if ( !isdefined( level.flag[ ent.script_flag_set ] ) )
{
flag_init( ent.script_flag_set );
}
}
if ( isdefined( ent.script_flag_wait ) )
{
if ( !isdefined( level.flag[ ent.script_flag_wait ] ) )
{
flag_init( ent.script_flag_wait );
}
}
if ( isdefined( ent.script_flag_clear ) )
{
if ( !isdefined( level.flag[ ent.script_flag_clear ] ) )
{
flag_init( ent.script_flag_clear );
}
}
if ( isdefined( ent.target ) )
{
new_targets = [[ get_func ]]( ent.target );
targets = add_to_array( targets, new_targets );
}
}
index++;
if ( index >= targets.size )
break;
ent = targets[ index ];
}
}
get_node_funcs_based_on_target( node, goal_type )
{
// figure out if its a node or script origin and set the goal_type index based on that.
// true is for script_origins, false is for nodes
get_target_func[ "entity" ] = ::get_target_ents;
get_target_func[ "node" ] = ::get_target_nodes;
get_target_func[ "struct" ] = ::get_target_structs;
set_goal_func_quits[ "entity" ] = ::go_to_node_set_goal_ent;
set_goal_func_quits[ "struct" ] = ::go_to_node_set_goal_pos;
set_goal_func_quits[ "node" ] = ::go_to_node_set_goal_node;
// if you pass a node, we'll assume you actually passed a node. We can make it find out if its a script origin later if we need that functionality.
if ( !isdefined( goal_type ) )
goal_type = "node";
array = [];
if ( isdefined( node ) )
{
array[ "destination" ][ 0 ] = node;
}
else
{
// if you dont pass a node then we need to figure out what type of target it is
node = getentarray( self.target, "targetname" );
if ( node.size > 0 )
{
goal_type = "entity";
}
if ( goal_type == "node" )
{
node = getnodearray( self.target, "targetname" );
if ( !node.size )
{
node = getstructarray( self.target, "targetname" );
if ( !node.size )
{
// Targetting neither
return;
}
goal_type = "struct";
}
}
array[ "destination" ] = node;
}
array[ "get_target_func" ] = get_target_func[ goal_type ];
array[ "set_goal_func_quits" ] = set_goal_func_quits[ goal_type ];
return array;
}
set_goal_height_from_settings()
{
if ( isdefined( self.script_goalheight ) )
self.goalheight = self.script_goalheight;
else
self.goalheight = level.default_goalheight;
}
set_goal_from_settings( node )
{
// sets goal radius
if ( isdefined( self.script_radius ) )
{
// use the override from radiant
self.goalradius = self.script_radius;
return;
}
if ( isDefined( self.script_forcegoal ) )
{
if ( isdefined( node ) && isdefined( node.radius ) )
{
// use the node's radius
self.goalradius = node.radius;
return;
}
}
// otherwise use the script default
if ( !isdefined( self getGoalVolume() ) )
{
if ( self.type == "civilian" )
self.goalradius = 128;
else
self.goalradius = level.default_goalradius;
}
}
autoTarget( targets )
{
for ( ;; )
{
user = self getturretowner();
if ( !isalive( user ) )
{
wait( 1.5 );
continue;
}
if ( !isdefined( user.enemy ) )
{
self settargetentity( random( targets ) );
self notify( "startfiring" );
self startFiring();
}
wait( 2 + randomfloat( 1 ) );
}
}
manualTarget( targets )
{
for ( ;; )
{
self settargetentity( random( targets ) );
self notify( "startfiring" );
self startFiring();
wait( 2 + randomfloat( 1 ) );
}
}
// this is called from two places w / generally identical code... maybe larger scale cleanup is called for.
use_a_turret( turret )
{
if ( self isBadGuy() && self.health == 150 )
{
self.health = 100;// mg42 operators aren't going to do long death
self.a.disableLongDeath = true;
}
// thread maps\_mg_penetration::gunner_think( turret );
self useturret( turret );// dude should be near the mg42
// turret setmode( "auto_ai" );// auto, auto_ai, manual
// turret settargetentity( level.player );
// turret setmode( "manual" );// auto, auto_ai, manual
if ( ( isdefined( turret.target ) ) && ( turret.target != turret.targetname ) )
{
ents = getentarray( turret.target, "targetname" );
targets = [];
for ( i = 0; i < ents.size;i++ )
{
if ( ents[ i ].classname == "script_origin" )
targets[ targets.size ] = ents[ i ];
}
if ( isdefined( turret.script_autotarget ) )
{
turret thread autoTarget( targets );
}
else
if ( isdefined( turret.script_manualtarget ) )
{
turret setmode( "manual_ai" );
turret thread manualTarget( targets );
}
else
if ( targets.size > 0 )
{
if ( targets.size == 1 )
{
turret.manual_target = targets[ 0 ];
turret settargetentity( targets[ 0 ] );
// turret setmode( "manual_ai" );// auto, auto_ai, manual
self thread maps\_mgturret::manual_think( turret );
// if ( isdefined( self.script_mg42auto ) )
// println( "AI at origin ", self.origin, " has script_mg42auto" );
}
else
{
turret thread maps\_mgturret::mg42_suppressionFire( targets );
}
}
}
self thread maps\_mgturret::mg42_firing( turret );
turret notify( "startfiring" );
}
fallback_spawner_think( num, node )
{
self endon( "death" );
level.current_fallbackers[ num ] += self.count;
firstspawn = true;
while ( self.count > 0 )
{
self waittill( "spawned", spawn );
if ( firstspawn )
{
if ( getDvar("fallback", "0") == "1" )
println( "^a First spawned: ", num );
level notify( ( "fallback_firstspawn" + num ) );
firstspawn = false;
}
waitframe();// Wait until he does all his usual spawned logic so he will run to his node
if ( maps\_utility::spawn_failed( spawn ) )
{
level notify( ( "fallbacker_died" + num ) );
level.current_fallbackers[ num ] -- ;
continue;
}
spawn thread fallback_ai_think( num, node, "is spawner" );
}
// level notify( ( "fallbacker_died" + num ) );
}
fallback_ai_think_death( ai, num )
{
ai waittill( "death" );
level.current_fallbackers[ num ] -- ;
level notify( ( "fallbacker_died" + num ) );
}
fallback_ai_think( num, node, spawner )
{
if ( ( !isdefined( self.fallback ) ) || ( !isdefined( self.fallback[ num ] ) ) )
self.fallback[ num ] = true;
else
return;
self.script_fallback = num;
if ( !isdefined( spawner ) )
level.current_fallbackers[ num ]++;
if ( ( isdefined( node ) ) && ( level.fallback_initiated[ num ] ) )
{
self thread fallback_ai( num, node );
/*
self notify( "stop_going_to_node" );
self setgoalnode( node );
if ( isdefined( node.radius ) )
self.goalradius = node.radius;
*/
}
level thread fallback_ai_think_death( self, num );
}
fallback_death( ai, num )
{
ai waittill( "death" );
level notify( ( "fallback_reached_goal" + num ) );
// ai notify( "fallback_notify" );
}
fallback_goal()
{
self waittill( "goal" );
self.ignoresuppression = false;
self notify( "fallback_notify" );
self notify( "stop_coverprint" );
}
fallback_ai( num, node )
{
self notify( "stop_going_to_node" );
self stopuseturret();
self.ignoresuppression = true;
self setgoalnode( node );
if ( node.radius != 0 )
self.goalradius = node.radius;
self endon( "death" );
level thread fallback_death( self, num );
self thread fallback_goal();
if ( getDvar("fallback", "0") == "1" )
self thread coverprint( node.origin );
self waittill( "fallback_notify" );
level notify( ( "fallback_reached_goal" + num ) );
}
coverprint( org )
{
self endon( "fallback_notify" );
self endon( "stop_coverprint" );
while ( 1 )
{
line( self.origin + ( 0, 0, 35 ), org, ( 0.2, 0.5, 0.8 ), 0.5 );
print3d( ( self.origin + ( 0, 0, 70 ) ), "Falling Back", ( 0.98, 0.4, 0.26 ), 0.85 );
waitframe();
}
}
newfallback_overmind( num, group )
{
fallback_nodes = undefined;
nodes = getallnodes();
for ( i = 0;i < nodes.size;i++ )
{
if ( ( isdefined( nodes[ i ].script_fallback ) ) && ( nodes[ i ].script_fallback == num ) )
fallback_nodes = add_to_array( fallback_nodes, nodes[ i ] );
}
if ( !isdefined( fallback_nodes ) )
return;
level.current_fallbackers[ num ] = 0;
level.spawner_fallbackers[ num ] = 0;
level.fallback_initiated[ num ] = false;
spawners = getspawnerarray();
for ( i = 0;i < spawners.size;i++ )
{
if ( ( isdefined( spawners[ i ].script_fallback ) ) && ( spawners[ i ].script_fallback == num ) )
{
if ( spawners[ i ].count > 0 )
{
spawners[ i ] thread fallback_spawner_think( num, fallback_nodes[ randomint( fallback_nodes.size ) ] );
level.spawner_fallbackers[ num ]++;
}
}
}
ai = getaiarray();
for ( i = 0;i < ai.size;i++ )
{
if ( ( isdefined( ai[ i ].script_fallback ) ) && ( ai[ i ].script_fallback == num ) )
ai[ i ] thread fallback_ai_think( num );
}
if ( ( !level.current_fallbackers[ num ] ) && ( !level.spawner_fallbackers[ num ] ) )
return;
spawners = undefined;
ai = undefined;
thread fallback_wait( num, group );
level waittill( ( "fallbacker_trigger" + num ) );
if ( getDvar("fallback", "0") == "1" )
println( "^a fallback trigger hit: ", num );
level.fallback_initiated[ num ] = true;
fallback_ai = undefined;
ai = getaiarray();
for ( i = 0;i < ai.size;i++ )
{
if ( ( ( isdefined( ai[ i ].script_fallback ) ) && ( ai[ i ].script_fallback == num ) ) ||
( ( isdefined( ai[ i ].script_fallback_group ) ) && ( isdefined( group ) ) && ( ai[ i ].script_fallback_group == group ) ) )
fallback_ai = add_to_array( fallback_ai, ai[ i ] );
}
ai = undefined;
if ( !isdefined( fallback_ai ) )
return;
first_half = fallback_ai.size * 0.4;
first_half = int( first_half );
level notify( "fallback initiated " + num );
fallback_text( fallback_ai, 0, first_half );
for ( i = 0;i < first_half;i++ )
fallback_ai[ i ] thread fallback_ai( num, fallback_nodes[ randomint( fallback_nodes.size ) ] );
for ( i = 0;i < first_half;i++ )
level waittill( ( "fallback_reached_goal" + num ) );
fallback_text( fallback_ai, first_half, fallback_ai.size );
for ( i = first_half;i < fallback_ai.size;i++ )
{
if ( isalive( fallback_ai[ i ] ) )
fallback_ai[ i ] thread fallback_ai( num, fallback_nodes[ randomint( fallback_nodes.size ) ] );
}
}
fallback_text( fallbackers, start, end )
{
if ( gettime() <= level._nextcoverprint )
return;
for ( i = start;i < end;i++ )
{
if ( !isalive( fallbackers[ i ] ) )
continue;
level._nextcoverprint = gettime() + 2500 + randomint( 2000 );
total = fallbackers.size;
temp = int( total * 0.4 );
if ( randomint( 100 ) > 50 )
{
if ( total - temp > 1 )
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_1";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_2";
else
msg = "dawnville_defensive_german_3";
}
else
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_4";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_5";
else
msg = "dawnville_defensive_german_1";
}
}
else
{
if ( temp > 1 )
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_2";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_3";
else
msg = "dawnville_defensive_german_4";
}
else
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_5";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_1";
else
msg = "dawnville_defensive_german_2";
}
}
fallbackers[ i ] animscripts\face::SaySpecificDialogue( undefined, msg, 1.0 );
return;
}
}
fallback_wait( num, group )
{
level endon( ( "fallbacker_trigger" + num ) );
if ( getDvar("fallback", "0") == "1" )
println( "^a Fallback wait: ", num );
for ( i = 0;i < level.spawner_fallbackers[ num ];i++ )
{
if ( getDvar("fallback", "0") == "1" )
println( "^a Waiting for spawners to be hit: ", num, " i: ", i );
level waittill( ( "fallback_firstspawn" + num ) );
}
if ( getDvar("fallback", "0") == "1" )
println( "^a Waiting for AI to die, fall backers for group ", num, " is ", level.current_fallbackers[ num ] );
// total_fallbackers = 0;
ai = getaiarray();
for ( i = 0;i < ai.size;i++ )
{
if ( ( ( isdefined( ai[ i ].script_fallback ) ) && ( ai[ i ].script_fallback == num ) ) ||
( ( isdefined( ai[ i ].script_fallback_group ) ) && ( isdefined( group ) ) && ( ai[ i ].script_fallback_group == group ) ) )
ai[ i ] thread fallback_ai_think( num );
}
ai = undefined;
// if ( !total_fallbackers )
// return;
max_fallbackers = level.current_fallbackers[ num ];
deadfallbackers = 0;
while ( level.current_fallbackers[ num ] > max_fallbackers * 0.5 )
{
if ( getDvar("fallback", "0") == "1" )
println( "^cwaiting for " + level.current_fallbackers[ num ] + " to be less than " + ( max_fallbackers * 0.5 ) );
level waittill( ( "fallbacker_died" + num ) );
deadfallbackers++;
}
println( deadfallbackers, " fallbackers have died, time to retreat" );
level notify( ( "fallbacker_trigger" + num ) );
}
fallback_think( trigger )// for fallback trigger
{
if ( ( !isdefined( level.fallback ) ) || ( !isdefined( level.fallback[ trigger.script_fallback ] ) ) )
level thread newfallback_overmind( trigger.script_fallback, trigger.script_fallback_group );
trigger waittill( "trigger" );
level notify( ( "fallbacker_trigger" + trigger.script_fallback ) );
// level notify( ( "fallback" + trigger.script_fallback ) );
// Maybe throw in a thing to kill triggers with the same fallback? God my hands are cold.
kill_trigger( trigger );
}
arrive( node )
{
self waittill( "goal" );
if ( node.radius != 0 )
self.goalradius = node.radius;
else
self.goalradius = level.default_goalradius;
}
fallback_coverprint()
{
// self endon( "death" );
self endon( "fallback" );
self endon( "fallback_clear_goal" );
self endon( "fallback_clear_death" );
while ( 1 )
{
if ( isdefined( self.coverpoint ) )
line( self.origin + ( 0, 0, 35 ), self.coverpoint.origin, ( 0.2, 0.5, 0.8 ), 0.5 );
print3d( ( self.origin + ( 0, 0, 70 ) ), "Covering", ( 0.98, 0.4, 0.26 ), 0.85 );
waitframe();
}
}
fallback_print()
{
// self endon( "death" );
self endon( "fallback_clear_goal" );
self endon( "fallback_clear_death" );
while ( 1 )
{
if ( isdefined( self.coverpoint ) )
line( self.origin + ( 0, 0, 35 ), self.coverpoint.origin, ( 0.2, 0.5, 0.8 ), 0.5 );
print3d( ( self.origin + ( 0, 0, 70 ) ), "Falling Back", ( 0.98, 0.4, 0.26 ), 0.85 );
waitframe();
}
}
fallback()
{
// self endon( "death" );
dest = getnode( self.target, "targetname" );
self.coverpoint = dest;
self setgoalnode( dest );
if ( isdefined( self.script_seekgoal ) )
self thread arrive( dest );
else
{
if ( dest.radius != 0 )
self.goalradius = dest.radius;
else
self.goalradius = level.default_goalradius;
}
while ( 1 )
{
self waittill( "fallback" );
self.interval = 20;
level thread fallback_death( self );
if ( getDvar("fallback", "0") == "1" )
self thread fallback_print();
if ( isdefined( dest.target ) )
{
dest = getnode( dest.target, "targetname" );
self.coverpoint = dest;
self setgoalnode( dest );
self thread fallback_goal();
if ( dest.radius != 0 )
self.goalradius = dest.radius;
}
else
{
level notify( ( "fallback_arrived" + self.script_fallback ) );
return;
}
}
}
delete_me()
{
waitframe();
self delete();
}
vlength( vec1, vec2 )
{
v0 = vec1[ 0 ] - vec2[ 0 ];
v1 = vec1[ 1 ] - vec2[ 1 ];
v2 = vec1[ 2 ] - vec2[ 2 ];
v0 = v0 * v0;
v1 = v1 * v1;
v2 = v2 * v2;
veclength = v0 + v1 + v2;
return veclength;
}
specialCheck( name )
{
for ( ;; )
{
assertEX( getentarray( name, "targetname" ).size, "Friendly wave trigger that targets " + name + " doesnt target any spawners" );
wait( 0.05 );
}
}
friendly_wave( trigger )
{
// thread specialCheck( trigger.target );
if ( !isdefined( level.friendly_wave_active ) )
thread friendly_wave_masterthread();
/#
if ( trigger.targetname == "friendly_wave" )
{
assert = false;
targs = getentarray( trigger.target, "targetname" );
for ( i = 0;i < targs.size;i++ )
{
if ( isdefined( targs[ i ].classname[ 7 ] ) )
if ( targs[ i ].classname[ 7 ] != "l" )
{
println( "Friendyl_wave spawner at ", targs[ i ].origin, " is not an ally" );
assert = true;
}
}
if ( assert )
error( "Look above" );
}
#/
while ( 1 )
{
trigger waittill( "trigger" );
level notify( "friendly_died" );
if ( trigger.targetname == "friendly_wave" )
level.friendly_wave_trigger = trigger;
else
{
level.friendly_wave_trigger = undefined;
println( "friendly wave OFF" );
}
wait( 1 );
}
}
set_spawncount( count )
{
if ( !isdefined( self.target ) )
return;
spawners = getentarray( self.target, "targetname" );
for ( i = 0;i < spawners.size;i++ )
spawners[ i ] set_count( count );
}
friendlydeath_thread()
{
if ( !isdefined( level.totalfriends ) )
level.totalfriends = 0;
level.totalfriends++;
self waittill( "death" );
level notify( "friendly_died" );
level.totalfriends -- ;
}
friendly_wave_masterthread()
{
level.friendly_wave_active = true;
// level.totalfriends = 0;
triggers = getentarray( "friendly_wave", "targetname" );
array_thread( triggers, ::set_spawncount, 0 );
// friends = getaiarray( "allies" );
// array_thread( friends, ::friendlydeath_thread );
if ( !isdefined( level.maxfriendlies ) )
level.maxfriendlies = 7;
names = 1;
while ( 1 )
{
if ( ( isdefined( level.friendly_wave_trigger ) ) && ( isdefined( level.friendly_wave_trigger.target ) ) )
{
old_friendly_wave_trigger = level.friendly_wave_trigger;
spawn = getentarray( level.friendly_wave_trigger.target, "targetname" );
if ( !spawn.size )
{
level waittill( "friendly_died" );
continue;
}
num = 0;
script_delay = isdefined( level.friendly_wave_trigger.script_delay );
while ( ( isdefined( level.friendly_wave_trigger ) ) && ( level.totalfriends < level.maxfriendlies ) )
{
if ( old_friendly_wave_trigger != level.friendly_wave_trigger )
{
script_delay = isdefined( level.friendly_wave_trigger.script_delay );
old_friendly_wave_trigger = level.friendly_wave_trigger;
assertex( isdefined( level.friendly_wave_trigger.target ), "Wave trigger must target spawner" );
spawn = getentarray( level.friendly_wave_trigger.target, "targetname" );
}
else if ( !script_delay )
num = randomint( spawn.size );
else if ( num == spawn.size )
num = 0;
spawn[ num ] set_count( 1 );
//catch for stealth
dontShareEnemyInfo = ( isdefined( spawn[ num ].script_stealth ) && flag( "_stealth_enabled" ) && !flag( "_stealth_spotted" ) );
if ( isdefined( spawn[ num ].script_forcespawn ) )
spawned = spawn[ num ] stalingradSpawn( dontShareEnemyInfo );
else
spawned = spawn[ num ] doSpawn( dontShareEnemyInfo );
spawn[ num ] set_count( 0 );
if ( spawn_failed( spawned ) )
{
wait( 0.2 );
continue;
}
if ( isdefined( level.friendlywave_thread ) )
level thread [[ level.friendlywave_thread ]]( spawned );
else
spawned setgoalentity( level.player );
if ( script_delay )
{
if ( level.friendly_wave_trigger.script_delay == 0 )
waittillframeend;
else
wait level.friendly_wave_trigger.script_delay;
num++;
}
else
wait( randomfloat( 5 ) );
}
}
level waittill( "friendly_died" );
}
}
friendly_mgTurret( trigger )
{
/#
if ( !isdefined( trigger.target ) )
error( "No target for friendly_mg42 trigger, origin:" + trigger getorigin() );
#/
node = getnode( trigger.target, "targetname" );
/#
if ( !isdefined( node.target ) )
error( "No mg42 for friendly_mg42 trigger's node, origin: " + node.origin );
#/
mg42 = getent( node.target, "targetname" );
mg42 setmode( "auto_ai" );// auto, auto_ai, manual
mg42 cleartargetentity();
in_use = false;
while ( 1 )
{
// println( "^a mg42 waiting for trigger" );
trigger waittill( "trigger", other );
// println( "^a MG42 TRIGGERED" );
if ( !isAI( other ) )
continue;
if ( !isdefined( other.team ) )
continue;
if ( other.team != "allies" )
continue;
if ( ( isdefined( other.script_usemg42 ) ) && ( other.script_usemg42 == false ) )
continue;
if ( other thread friendly_mg42_useable( mg42, node ) )
{
other thread friendly_mg42_think( mg42, node );
mg42 waittill( "friendly_finished_using_mg42" );
if ( isalive( other ) )
other.turret_use_time = gettime() + 10000;
}
wait( 1 );
}
}
friendly_mg42_death_notify( guy, mg42 )
{
mg42 endon( "friendly_finished_using_mg42" );
guy waittill( "death" );
mg42 notify( "friendly_finished_using_mg42" );
println( "^a guy using gun died" );
}
friendly_mg42_wait_for_use( mg42 )
{
mg42 endon( "friendly_finished_using_mg42" );
self.useable = true;
self setcursorhint( "HINT_NOICON" );
// Hold &&1 to commandeer the MG42
self setHintString( &"PLATFORM_USEAIONMG42" );
self waittill( "trigger" );
println( "^a was used by player, stop using turret" );
self.useable = false;
self setHintString( "" );
self stopuseturret();
self notify( "stopped_use_turret" );// special hook for decoytown guys - nate
mg42 notify( "friendly_finished_using_mg42" );
}
friendly_mg42_useable( mg42, node )
{
if ( self.useable )
return false;
if ( ( isdefined( self.turret_use_time ) ) && ( gettime() < self.turret_use_time ) )
{
// println( "^a Used gun too recently" );
return false;
}
if ( distance( level.player.origin, node.origin ) < 100 )
{
// println( "^a player too close" );
return false;
}
if ( isdefined( self.chainnode ) )
if ( distance( level.player.origin, self.chainnode.origin ) > 1100 )
{
// println( "^a too far from chain node" );
return false;
}
return true;
}
friendly_mg42_endtrigger( mg42, guy )
{
mg42 endon( "friendly_finished_using_mg42" );
self waittill( "trigger" );
println( "^a Told friendly to leave the MG42 now" );
// guy stopuseturret();
// badplace_cylinder( undefined, 3, level.player.origin, 150, 150, "allies" );
mg42 notify( "friendly_finished_using_mg42" );
}
friendly_mg42_stop_use()
{
if ( !isdefined( self.friendly_mg42 ) )
return;
self.friendly_mg42 notify( "friendly_finished_using_mg42" );
}
noFour()
{
self endon( "death" );
self waittill( "goal" );
self.goalradius = self.oldradius;
if ( self.goalradius < 32 )
self.goalradius = 400;
}
friendly_mg42_think( mg42, node )
{
self endon( "death" );
mg42 endon( "friendly_finished_using_mg42" );
// self endon( "death" );
level thread friendly_mg42_death_notify( self, mg42 );
// println( self.name + "^a is using an mg42" );
self.oldradius = self.goalradius;
self.goalradius = 28;
self thread noFour();
self setgoalnode( node );
self.ignoresuppression = true;
self waittill( "goal" );
self.goalradius = self.oldradius;
if ( self.goalradius < 32 )
self.goalradius = 400;
// println( "^3 my goal radius is ", self.goalradius );
self.ignoresuppression = false;
// Temporary fix waiting on new code command to see who the player is following.
// self setgoalentity( level.player );
self.goalradius = self.oldradius;
if ( distance( level.player.origin, node.origin ) < 32 )
{
mg42 notify( "friendly_finished_using_mg42" );
return;
}
self.friendly_mg42 = mg42;// For making him stop using the mg42 from another script
self thread friendly_mg42_wait_for_use( mg42 );
self thread friendly_mg42_cleanup( mg42 );
self useturret( mg42 );// dude should be near the mg42
// println( "^a Told AI to use mg42" );
if ( isdefined( mg42.target ) )
{
stoptrigger = getent( mg42.target, "targetname" );
if ( isdefined( stoptrigger ) )
stoptrigger thread friendly_mg42_endtrigger( mg42, self );
}
while ( 1 )
{
if ( distance( self.origin, node.origin ) < 32 )
self useturret( mg42 );// dude should be near the mg42
else
break;// a friendly is too far from mg42, stop using turret
if ( isdefined( self.chainnode ) )
{
if ( distance( self.origin, self.chainnode.origin ) > 1100 )
break;// friendly node is too far, stop using turret
}
wait( 1 );
}
mg42 notify( "friendly_finished_using_mg42" );
}
friendly_mg42_cleanup( mg42 )
{
self endon( "death" );
mg42 waittill( "friendly_finished_using_mg42" );
self friendly_mg42_doneUsingTurret();
}
friendly_mg42_doneUsingTurret()
{
self endon( "death" );
turret = self.friendly_mg42;
self.friendly_mg42 = undefined;
self stopuseturret();
self notify( "stopped_use_turret" );// special hook for decoytown guys - nate
self.useable = false;
self.goalradius = self.oldradius;
if ( !isdefined( turret ) )
return;
if ( !isdefined( turret.target ) )
return;
node = getnode( turret.target, "targetname" );
oldradius = self.goalradius;
self.goalradius = 8;
self setgoalnode( node );
wait( 2 );
self.goalradius = 384;
return;
self waittill( "goal" );
if ( isdefined( self.target ) )
{
node = getnode( self.target, "targetname" );
if ( isdefined( node.target ) )
node = getnode( node.target, "targetname" );
if ( isdefined( node ) )
self setgoalnode( node );
}
self.goalradius = oldradius;
}
tanksquish()
{
if ( isdefined( level.noTankSquish ) )
{
assertex( level.noTankSquish, "level.noTankSquish must be true or undefined" );
return;
}
if ( isdefined( level.levelHasVehicles ) && !level.levelHasVehicles )
return;
self add_damage_function( ::tanksquish_damage_check );
}
tanksquish_damage_check( amt, who, force, b, c, d, e )
{
if ( !isdefined( self ) )
{
// deleted?
return;
}
if ( isalive( self ) )
return;
if ( !isalive( who ) )
return;
if ( !isdefined( who.vehicletype ) )
return;
if ( who maps\_vehicle::ishelicopter() )
return;
if( !isdefined( self.noragdoll ) )
self startRagdoll();
if ( !isdefined( self ) )
{
return;
}
self remove_damage_function( ::tanksquish_damage_check );
// self playsound( "human_crunch" );
}
// Makes a panzer guy run to a spot and shoot a specific spot
panzer_target( ai, node, pos, targetEnt, targetEnt_offsetVec )
{
ai endon( "death" );
ai.panzer_node = node;
if ( isdefined( node.script_delay ) )
ai.panzer_delay = node.script_delay;
if ( ( isdefined( targetEnt ) ) && ( isdefined( targetEnt_offsetVec ) ) )
{
ai.panzer_ent = targetEnt;
ai.panzer_ent_offset = targetEnt_offsetVec;
}
else
ai.panzer_pos = pos;
ai setgoalpos( ai.origin );
ai setgoalnode( node );
ai.goalradius = 12;
ai waittill( "goal" );
ai.goalradius = 28;
ai waittill( "shot_at_target" );
ai.panzer_ent = undefined;
ai.panzer_pos = undefined;
ai.panzer_delay = undefined;
// ai.exception_exposed = animscripts\combat::exception_exposed_panzer_guy;
// ai.exception_stop = animscripts\combat::exception_exposed_panzer_guy;
// ai waittill( "panzer mission complete" );
}
#using_animtree( "generic_human" );
showStart( origin, angles, anime )
{
org = getstartorigin( origin, angles, anime );
for ( ;; )
{
print3d( org, "x", ( 0.0, 0.7, 1.0 ), 1, 0.25 ); // origin, text, RGB, alpha, scale
wait( 0.05 );
}
}
spawnWaypointFriendlies()
{
self set_count( 1 );
if ( isdefined( self.script_forcespawn ) )
spawn = self stalingradSpawn();
else
spawn = self doSpawn();
if ( spawn_failed( spawn ) )
return;
spawn.friendlyWaypoint = true;
}
// Newvillers global stuff:
waittillDeathOrLeaveSquad()
{
self endon( "death" );
self waittill( "leaveSquad" );
}
friendlySpawnWave()
{
/*
Triggers a spawn point for incoming friendlies.
trigger targetname friendly_spawn
Targets a trigger or triggers. The targetted trigger targets a script origin.
Touching the friendly_spawn trigger enables the targetted trigger.
Touching the enabled trigger causes friendlies to spawn from the targetted script origin.
Touching the original trigger again stops the friendlies from spawning.
The script origin may target an additional trigger that halts spawning.
Make friendly spawn spot sparkle
*/
/#
triggers = getentarray( self.target, "targetname" );
for ( i = 0;i < triggers.size;i++ )
{
if ( triggers[ i ] getentnum() == 526 )
println( "Target: " + triggers[ i ].target );
}
#/
array_thread( getentarray( self.target, "targetname" ), ::friendlySpawnWave_triggerThink, self );
for ( ;; )
{
self waittill( "trigger", other );
// If we're the current friendly spawn spot then stop friendly spawning because
// the player is backtracking
if ( activeFriendlySpawn() && getFriendlySpawnTrigger() == self )
unsetFriendlySpawn();
self waittill( "friendly_wave_start", startPoint );
setFriendlySpawn( startPoint, self );
// If the startpoint targets a trigger, that trigger can
// disable the startpoint too
if ( !isdefined( startPoint.target ) )
continue;
trigger = getent( startPoint.target, "targetname" );
trigger thread spawnWaveStopTrigger( self );
}
}
flood_and_secure( instantRespawn )
{
/*
Spawns AI that run to a spot then get a big goal radius. They stop spawning when auto delete kicks in, then start
again when they are retriggered or the player gets close.
trigger targetname flood_and_secure
ai spawn and run to goal with small goalradius then get large goalradius
spawner starts with a notify from any flood_and_secure trigger that triggers it
spawner stops when an AI from it is deleted to make space for a new AI or when count is depleted
spawners with count of 1 only make 1 guy.
Spawners with count of more than 1 only deplete in count when the player kills the AI.
spawner can target another spawner. When first spawner's ai dies from death( not deletion ), second spawner activates.
script_noteworth "instant_respawn" on the trigger will disable the wave respawning
*/
// Instantrespawn disables wave respawning or waiting for time to pass before respawning
if ( !isdefined( instantRespawn ) )
instantRespawn = false;
if ( ( isdefined( self.script_noteworthy ) ) && ( self.script_noteworthy == "instant_respawn" ) )
instantRespawn = true;
level.spawnerWave = [];
spawners = getentarray( self.target, "targetname" );
array_thread( spawners, ::flood_and_secure_spawner, instantRespawn );
playerTriggered = false;
didDelay = false;
for ( ;; )
{
self waittill( "trigger", other );
if ( !objectiveIsAllowed() )
continue;
if ( !didDelay )
{
didDelay = true;
script_delay();
}
if ( self isTouching( level.player ) )
playerTriggered = true;
else
{
if ( !isalive( other ) )
continue;
if ( isplayer( other ) )
playerTriggered = true;
else
if ( !isdefined( other.isSquad ) || !other.isSquad )
{
// Non squad AI are not allowed to spawn enemies
continue;
}
}
// Reacquire spawners in case one has died / been deleted and moved up to another
// because spawners can target other spawners that are used when the first spawner dies.
spawners = getentarray( self.target, "targetname" );
if ( isdefined( spawners[ 0 ] ) )
{
if ( isdefined( spawners[ 0 ].script_randomspawn ) )
{
cull_spawners_from_killspawner( spawners[ 0 ].script_randomspawn );
//cull_spawners_leaving_one_set( spawners );
}
}
spawners = getentarray( self.target, "targetname" );
for ( i = 0;i < spawners.size;i++ )
{
spawners[ i ].playerTriggered = playerTriggered;
spawners[ i ] notify( "flood_begin" );
}
if ( playerTriggered )
wait( 5 );
else
wait( 0.1 );
}
}
cull_spawners_leaving_one_set( spawners )
{
groups = [];
for ( i = 0; i < spawners.size; i++ )
{
assertEx( isdefined( spawners[ i ].script_randomspawn ), "Spawner at " + spawners[ i ].origin + " doesn't have script_randomspawn set" );
groups[ spawners[ i ].script_randomspawn ] = true;
}
keys = getarraykeys( groups );
num_that_lives = random( keys );
for ( i = 0; i < spawners.size; i++ )
{
if ( spawners[ i ].script_randomspawn != num_that_lives )
spawners[ i ] delete();
}
/*
highest_num = 0;
for ( i = 0;i < spawners.size;i++ )
{
if ( spawners[ i ].script_randomspawn > highest_num )
highest_num = spawners[ i ].script_randomspawn;
}
selection = randomint( highest_num + 1 );
for ( i = 0;i < spawners.size;i++ )
{
if ( spawners[ i ].script_randomspawn != selection )
spawners[ i ] delete();
}
*/
}
flood_and_secure_spawner( instantRespawn )
{
if ( isdefined( self.secureStarted ) )
{
// Multiple triggers can trigger a flood and secure spawner, but they need to run
// their logic just once so we exit out if its already running.
return;
}
self.secureStarted = true;
self.triggerUnlocked = true;// So we don't run auto targetting behavior
/*
mg42 = issubstr( self.classname, "mgportable" ) || issubstr( self.classname, "30cal" );
if ( !mg42 )
{
// So we don't go script error'ing or whatnot off auto spawn logic
// Unless we're an mg42 guy that has to set an mg42 up.
self.script_moveoverride = true;
}
*/
target = self.target;
targetname = self.targetname;
if ( ( !isdefined( target ) ) && ( !isdefined( self.script_moveoverride ) ) )
{
println( "Entity " + self.classname + " at origin " + self.origin + " has no target" );
waittillframeend;
assert( isdefined( target ) );
}
// follow up spawners
spawners = [];
if ( isdefined( target ) )
{
possibleSpawners = getentarray( target, "targetname" );
for ( i = 0;i < possibleSpawners.size;i++ )
{
if ( !issubstr( possibleSpawners[ i ].classname, "actor" ) )
continue;
spawners[ spawners.size ] = possibleSpawners[ i ];
}
}
ent = spawnstruct();
org = self.origin;
flood_and_secure_spawner_think( ent, spawners.size > 0, instantRespawn );
if ( isalive( ent.ai ) )
ent.ai waittill( "death" );
if ( !isdefined( target ) )
return;
// follow up spawners
possibleSpawners = getentarray( target, "targetname" );
if ( !possibleSpawners.size )
return;
for ( i = 0;i < possibleSpawners.size;i++ )
{
if ( !issubstr( possibleSpawners[ i ].classname, "actor" ) )
continue;
possibleSpawners[ i ].targetname = targetname;
newTarget = target;
if ( isdefined( possibleSpawners[ i ].target ) )
{
targetEnt = getent( possibleSpawners[ i ].target, "targetname" );
if ( !isdefined( targetEnt ) || !issubstr( targetEnt.classname, "actor" ) )
newTarget = possibleSpawners[ i ].target;
}
// The guy might already be targetting a different destination
// But if not, he goes to the node his parent went to.
possibleSpawners[ i ].target = newTarget;
possibleSpawners[ i ] thread flood_and_secure_spawner( instantRespawn );
// Pass playertriggered flag as true because at this point the player must have been involved because one shots dont
// spawn without the player triggering and multishot guys require player kills or presense to move along
possibleSpawners[ i ].playerTriggered = true;
possibleSpawners[ i ] notify( "flood_begin" );
}
}
flood_and_secure_spawner_think( ent, oneShot, instantRespawn )
{
assert( isdefined( instantRespawn ) );
self endon( "death" );
count = self.count;
// oneShot = ( count == 1 );
if ( !oneShot )
oneshot = ( isdefined( self.script_noteworthy ) && self.script_noteworthy == "delete" );
self set_count( 2 );// running out of count counts as a dead spawner to script_deathchain
if ( isdefined( self.script_delay ) )
delay = self.script_delay;
else
delay = 0;
for ( ;; )
{
self waittill( "flood_begin" );
if ( self.playerTriggered )
break;
/*
// Lets let AI spawn oneshots!
// Oneshots require player triggering to activate
if ( oneShot )
continue;
*/
// guys that have a delay require triggering from the player
if ( delay )
continue;
break;
}
dist = distance( level.player.origin, self.origin );
prof_begin( "flood_and_secure_spawner_think" );
while ( count )
{
self.trueCount = count;
self set_count( 2 );
wait( delay );
dontShareEnemyInfo = ( isdefined( self.script_stealth ) && flag( "_stealth_enabled" ) && !flag( "_stealth_spotted" ) );
if ( isdefined( self.script_forcespawn ) )
spawn = self stalingradSpawn( dontShareEnemyInfo );
else
spawn = self doSpawn( dontShareEnemyInfo );
if ( spawn_failed( spawn ) )
{
playerKill = false;
if ( delay < 2 )
wait( 2 );// debounce
continue;
}
else
{
thread addToWaveSpawner( spawn );
spawn thread flood_and_secure_spawn( self );
// Set the accuracy for the spawner
if ( isdefined( self.script_accuracy ) )
spawn.baseAccuracy = self.script_accuracy;
ent.ai = spawn;
ent notify( "got_ai" );
self waittill( "spawn_died", deleted, playerKill );
if ( delay > 2 )
delay = randomint( 4 ) + 2;// first delay can be long, after that its always a set amount.
else
delay = 0.5 + randomfloat( 0.5 );
}
if ( deleted )
{
// Deletion indicates that we've hit the max AI limit and this is the oldest / farthest AI
// so we need to stop this spawner until it gets triggered again or the player gets close
waittillRestartOrDistance( dist );
}
else
{
/*
// Only player kills count towards the count unless the spawner only has a count of 1
// or NOT
if ( playerKill || oneShot )
*/
if ( playerWasNearby( playerKill || oneShot, ent.ai ) )
count -- ;
if ( !instantRespawn )
waitUntilWaveRelease();
}
}
prof_end( "flood_and_secure_spawner_think" );
self delete();
}
waittillDeletedOrDeath( spawn )
{
self endon( "death" );
spawn waittill( "death" );
}
addToWaveSpawner( spawn )
{
name = self.targetname;
if ( !isdefined( level.spawnerWave[ name ] ) )
{
level.spawnerWave[ name ] = spawnStruct();
level.spawnerWave[ name ] set_count( 0 );
level.spawnerWave[ name ].total = 0;
}
if ( !isdefined( self.addedToWave ) )
{
self.addedToWave = true;
level.spawnerWave[ name ].total++;
}
level.spawnerWave[ name ].count++;
/*
/#
if ( level.debug_corevillers )
thread debugWaveCount( level.spawnerWave[ name ] );
#/
*/
waittillDeletedOrDeath( spawn );
level.spawnerWave[ name ].count -- ;
if ( !isdefined( self ) )
level.spawnerWave[ name ].total -- ;
/*
/#
if ( isdefined( self ) )
{
if ( level.debug_corevillers )
self notify( "debug_stop" );
}
#/
*/
// if ( !level.spawnerWave[ name ].count )
// Spawn the next wave if 68% of the AI from the wave are dead.
if ( level.spawnerWave[ name ].total )
{
if ( level.spawnerWave[ name ].count / level.spawnerWave[ name ].total < 0.32 )
level.spawnerWave[ name ] notify( "waveReady" );
}
}
debugWaveCount( ent )
{
self endon( "debug_stop" );
self endon( "death" );
for ( ;; )
{
print3d( self.origin, ent.count + "/" + ent.total, ( 0, 0.8, 1 ), 0.5 );
wait( 0.05 );
}
}
waitUntilWaveRelease()
{
name = self.targetName;
if ( level.spawnerWave[ name ].count )
level.spawnerWave[ name ] waittill( "waveReady" );
}
playerWasNearby( playerKill, ai )
{
if ( playerKill )
return true;
if ( isdefined( ai ) && isdefined( ai.origin ) )
{
org = ai.origin;
}
else
{
org = self.origin;
}
if ( distance( level.player.origin, org ) < 700 )
{
return true;
}
return bulletTracePassed( level.player geteye(), ai geteye(), false, undefined );
}
waittillRestartOrDistance( dist )
{
self endon( "flood_begin" );
dist = dist * 0.75;// require the player to get a bit closer to force restart the spawner
while ( distance( level.player.origin, self.origin ) > dist )
wait( 1 );
}
flood_and_secure_spawn( spawner )
{
self thread flood_and_secure_spawn_goal();
self waittill( "death", other );
playerKill = isalive( other ) && isplayer( other );
if ( !playerkill && isdefined( other ) && other.classname == "worldspawn" )// OR THE WORLDSPAWN???
{
playerKill = true;
}
deleted = !isdefined( self );
spawner notify( "spawn_died", deleted, playerKill );
}
flood_and_secure_spawn_goal()
{
if ( isdefined( self.script_moveoverride ) )
return;
self endon( "death" );
node = getnode( self.target, "targetname" );
self setgoalnode( node );
// if ( isdefined( self.script_deathChain ) )
// self setgoalvolume( level.deathchain_goalVolume[ self.script_deathChain ] );
if ( isdefined( level.fightdist ) )
{
self.pathenemyfightdist = level.fightdist;
self.pathenemylookahead = level.maxdist;
}
if ( node.radius )
self.goalradius = node.radius;
else
self.goalradius = 256;
self waittill( "goal" );
while ( isdefined( node.target ) )
{
newNode = getnode( node.target, "targetname" );
if ( isdefined( newNode ) )
node = newNode;
else
break;
self setgoalnode( node );
if ( node.radius )
self.goalradius = node.radius;
else
self.goalradius = 256;
self waittill( "goal" );
}
if ( isdefined( self.script_noteworthy ) )
{
if ( self.script_noteworthy == "delete" )
{
// self delete();
// Do damage instead of delete so he counts as "killed" and we dont have to write
// stuff to let the spawner know to stop trying to spawn him.
self kill();
return;
}
}
if ( isDefined( node.target ) )
{
turret = getEnt( node.target, "targetname" );
if ( isDefined( turret ) && ( turret.code_classname == "misc_mgturret" || turret.code_classname == "misc_turret" ) )
{
self setGoalNode( node );
self.goalradius = 4;
self waittill( "goal" );
if ( !isDefined( self.script_forcegoal ) )
self.goalradius = level.default_goalradius;
self maps\_spawner::use_a_turret( turret );
}
}
if ( isdefined( self.script_noteworthy ) )
{
if ( isdefined( self.script_noteworthy2 ) )
{
if ( self.script_noteworthy2 == "furniture_push" )
thread furniturePushSound();
}
if ( self.script_noteworthy == "hide" )
{
self thread set_battlechatter( false );
return;
}
}
if ( !isdefined( self.script_forcegoal ) && !isdefined( self getGoalVolume() ) )
{
self.goalradius = level.default_goalradius;
}
}
furniturePushSound()
{
org = getent( self.target, "targetname" ).origin;
play_sound_in_space( "furniture_slide", org );
wait( 0.9 );
if ( isdefined( level.whisper ) )
play_sound_in_space( random( level.whisper ), org );
}
friendlychain()
{
/*
Selectively enable and disable friendly chains with triggers
trigger targetname friendlychain
Targets a trigger. When the player hits the friendly chain trigger it enables the targetted trigger.
When the player hits the enabled trigger, it activates the friendly chain of nodes that it targets.
If the enabled trigger links to a "friendy_spawn" trigger, it enables that friendly_spawn trigger.
*/
waittillframeend;
triggers = getentarray( self.target, "targetname" );
if ( !triggers.size )
{
// trigger targets chain directly, has no direction
node = getnode( self.target, "targetname" );
assert( isdefined( node ) );
assert( isdefined( node.script_noteworthy ) );
for ( ;; )
{
self waittill( "trigger" );
if ( isdefined( level.lastFriendlyTrigger ) && level.lastFriendlyTrigger == self )
{
wait( 0.5 );
continue;
}
if ( !objectiveIsAllowed() )
{
wait( 0.5 );
continue;
}
level notify( "new_friendly_trigger" );
level.lastFriendlyTrigger = self;
rejoin = !isdefined( self.script_baseOfFire ) || self.script_baseOfFire == 0;
setNewPlayerChain( node, rejoin );
}
}
/#
for ( i = 0;i < triggers.size;i++ )
{
node = getnode( triggers[ i ].target, "targetname" );
assert( isdefined( node ) );
assert( isdefined( node.script_noteworthy ) );
}
#/
for ( ;; )
{
self waittill( "trigger" );
// if ( level.currentObjective != self.script_noteworthy2 )
while ( level.player istouching( self ) )
wait( 0.05 );
if ( !objectiveIsAllowed() )
{
wait( 0.05 );
continue;
}
if ( isdefined( level.lastFriendlyTrigger ) && level.lastFriendlyTrigger == self )
continue;
level notify( "new_friendly_trigger" );
level.lastFriendlyTrigger = self;
array_thread( triggers, ::friendlyTrigger );
wait( 0.5 );
}
}
objectiveIsAllowed()
{
active = true;
if ( isdefined( self.script_objective_active ) )
{
active = false;
// objective must be active for this trigger to hit
for ( i = 0;i < level.active_objective.size;i++ )
{
if ( !issubstr( self.script_objective_active, level.active_objective[ i ] ) )
continue;
active = true;
break;
}
if ( !active )
return false;
}
if ( !isdefined( self.script_objective_inactive ) )
return( active );
// trigger only hits if this objective is inactive
inactive = 0;
for ( i = 0;i < level.inactive_objective.size;i++ )
{
if ( !issubstr( self.script_objective_inactive, level.inactive_objective[ i ] ) )
continue;
inactive++;
}
tokens = strtok( self.script_objective_inactive, " " );
return( inactive == tokens.size );
}
friendlyTrigger( node )
{
level endon( "new_friendly_trigger" );
self waittill( "trigger" );
node = getnode( self.target, "targetname" );
rejoin = !isdefined( self.script_baseOfFire ) || self.script_baseOfFire == 0;
setNewPlayerChain( node, rejoin );
}
waittillDeathOrEmpty()
{
self endon( "death" );
num = self.script_deathChain;
while ( self.count )
{
self waittill( "spawned", spawn );
spawn thread deathChainAINotify( num );
}
}
deathChainAINotify( num )
{
level.deathSpawner[ num ]++;
self waittill( "death" );
level.deathSpawner[ num ] -- ;
level notify( "spawner_expired" + num );
}
deathChainSpawnerLogic()
{
num = self.script_deathChain;
level.deathSpawner[ num ]++;
/#
level.deathSpawnerEnts[ num ][ level.deathSpawnerEnts[ num ].size ] = self;
#/
org = self.origin;
self waittillDeathOrEmpty();
/#
newDeathSpawners = [];
if ( isdefined( self ) )
{
for ( i = 0;i < level.deathSpawnerEnts[ num ].size;i++ )
{
if ( !isdefined( level.deathSpawnerEnts[ num ][ i ] ) )
continue;
if ( self == level.deathSpawnerEnts[ num ][ i ] )
continue;
newDeathSpawners[ newDeathSpawners.size ] = level.deathSpawnerEnts[ num ][ i ];
}
}
else
{
for ( i = 0;i < level.deathSpawnerEnts[ num ].size;i++ )
{
if ( !isdefined( level.deathSpawnerEnts[ num ][ i ] ) )
continue;
newDeathSpawners[ newDeathSpawners.size ] = level.deathSpawnerEnts[ num ][ i ];
}
}
level.deathSpawnerEnts[ num ] = newDeathSpawners;
#/
level notify( "spawner dot" + org );
level.deathSpawner[ num ] -- ;
level notify( "spawner_expired" + num );
}
friendlychain_onDeath()
{
/*
Enables a friendly chain when certain AI are cleared
trigger targetname friendly_chain_on_death
trigger is script_deathchain grouped with spawners
When the spawners have depleted and all their ai are dead:
the triggers become active.
When triggered they set the friendly chain to the chain they target
The triggers deactivate when a "friendlychain" targetnamed trigger is hit.
*/
triggers = getentarray( "friendly_chain_on_death", "targetname" );
spawners = getspawnerarray();
level.deathSpawner = [];
/#
// for debugging deathspawners
level.deathSpawnerEnts = [];
#/
for ( i = 0;i < spawners.size;i++ )
{
if ( !isdefined( spawners[ i ].script_deathchain ) )
continue;
num = spawners[ i ].script_deathchain;
if ( !isdefined( level.deathSpawner[ num ] ) )
{
level.deathSpawner[ num ] = 0;
/#
level.deathSpawnerEnts[ num ] = [];
#/
}
spawners[ i ] thread deathChainSpawnerLogic();
// level.deathSpawner[ num ]++;
}
for ( i = 0;i < triggers.size;i++ )
{
if ( !isdefined( triggers[ i ].script_deathchain ) )
{
println( "trigger at origin " + triggers[ i ] getorigin() + " has no script_deathchain" );
return;
}
triggers[ i ] thread friendlyChain_onDeathThink();
}
}
friendlyChain_onDeathThink()
{
while ( level.deathSpawner[ self.script_deathChain ] > 0 )
level waittill( "spawner_expired" + self.script_deathChain );
level endon( "start_chain" );
node = getnode( self.target, "targetname" );
for ( ;; )
{
self waittill( "trigger" );
setNewPlayerChain( node, true );
iprintlnbold( "Area secured, move up!" );
wait( 5 );// debounce
}
}
setNewPlayerChain( node, rejoin )
{
level.player set_friendly_chain_wrapper( node );
level notify( "new_escort_trigger" );// stops escorting guy from getting back on escort chain
level notify( "new_escort_debug" );
level notify( "start_chain", rejoin );// get the SMG guy back on the friendly chain
}
friendlyChains()
{
level.friendlySpawnOrg = [];
level.friendlySpawnTrigger = [];
array_thread( getentarray( "friendlychain", "targetname" ), ::friendlychain );
}
unsetFriendlySpawn()
{
newOrg = [];
newTrig = [];
for ( i = 0;i < level.friendlySpawnOrg.size;i++ )
{
newOrg[ newOrg.size ] = level.friendlySpawnOrg[ i ];
newTrig[ newTrig.size ] = level.friendlySpawnTrigger[ i ];
}
level.friendlySpawnOrg = newOrg;
level.friendlySpawnTrigger = newTrig;
if ( activeFriendlySpawn() )
return;
// If we've stepped back through all the spawners then turn off spawning
flag_Clear( "spawning_friendlies" );
}
getFriendlySpawnStart()
{
assert( level.friendlySpawnOrg.size > 0 );
return( level.friendlySpawnOrg[ level.friendlySpawnOrg.size - 1 ] );
}
activeFriendlySpawn()
{
return level.friendlySpawnOrg.size > 0;
}
getFriendlySpawnTrigger()
{
assert( level.friendlySpawnTrigger.size > 0 );
return( level.friendlySpawnTrigger[ level.friendlySpawnTrigger.size - 1 ] );
}
setFriendlySpawn( org, trigger )
{
level.friendlySpawnOrg[ level.friendlySpawnOrg.size ] = org.origin;
level.friendlySpawnTrigger[ level.friendlySpawnTrigger.size ] = trigger;
flag_set( "spawning_friendlies" );
}
delayedPlayerGoal()
{
self endon( "death" );
self endon( "leaveSquad" );
wait( 0.5 );
self setgoalentity( level.player );
}
spawnWaveStopTrigger( startTrigger )
{
self notify( "stopTrigger" );
self endon( "stopTrigger" );
self waittill( "trigger" );
if ( getFriendlySpawnTrigger() != startTrigger )
return;
unsetFriendlySpawn();
}
friendlySpawnWave_triggerThink( startTrigger )
{
org = getent( self.target, "targetname" );
// thread linedraw();
for ( ;; )
{
self waittill( "trigger" );
startTrigger notify( "friendly_wave_start", org );
if ( !isdefined( org.target ) )
continue;
}
}
goalVolumes()
{
volumes = getentarray( "info_volume", "classname" );
level.deathchain_goalVolume = [];
level.goalVolumes = [];
for ( i = 0; i < volumes.size; i++ )
{
volume = volumes[ i ];
if ( isdefined( volume.script_deathChain ) )
{
level.deathchain_goalVolume[ volume.script_deathChain ] = volume;
}
if ( isdefined( volume.script_goalvolume ) )
{
assertex( !isdefined( level.goalVolumes[ volume.script_goalVolume ] ), "Tried to overwrite goalvolume with script_goalvolume " + volume.script_goalVolume + ". Maybe you are using the same script_goalvolume value in a prefab? Script_goalvolume is not autocast in prefabs." );
level.goalVolumes[ volume.script_goalVolume ] = volume;
}
}
}
debugPrint( msg, endonmsg, color )
{
// if ( !level.debug_corevillers )
if ( 1 )
return;
org = self getorigin();
height = 40 * sin( org[ 0 ] + org[ 1 ] ) - 40;
org = ( org[ 0 ], org[ 1 ], org[ 2 ] + height );
level endon( endonmsg );
self endon( "new_color" );
if ( !isdefined( color ) )
color = ( 0, 0.8, 0.6 );
num = 0;
for ( ;; )
{
num += 12;
scale = sin( num ) * 0.4;
if ( scale < 0 )
scale *= -1;
scale += 1;
print3d( org, msg, color, 1, scale );
wait( 0.05 );
}
}
aigroup_create( aigroup )
{
level._ai_group[ aigroup ] = spawnstruct();
level._ai_group[ aigroup ].aicount = 0;
level._ai_group[ aigroup ].spawnercount = 0;
level._ai_group[ aigroup ].ai = [];
level._ai_group[ aigroup ].spawners = [];
}
aigroup_spawnerthink( tracker )
{
self endon( "death" );
self.decremented = false;
tracker.spawnercount++;
self thread aigroup_spawnerdeath( tracker );
self thread aigroup_spawnerempty( tracker );
while ( self.count )
{
self waittill( "spawned", soldier );
if ( spawn_failed( soldier ) )
continue;
soldier thread aigroup_soldierthink( tracker );
}
waittillframeend;
if ( self.decremented )
return;
self.decremented = true;
tracker.spawnercount -- ;
}
aigroup_spawnerdeath( tracker )
{
self waittill( "death" );
if ( self.decremented )
return;
tracker.spawnercount -- ;
}
aigroup_spawnerempty( tracker )
{
self endon( "death" );
self waittill( "emptied spawner" );
waittillframeend;
if ( self.decremented )
return;
self.decremented = true;
tracker.spawnercount -- ;
}
aigroup_soldierthink( tracker )
{
tracker.aicount++;
tracker.ai[ tracker.ai.size ] = self;
if ( isdefined( self.script_deathflag_longdeath ) )
{
self waittillDeathOrPainDeath();
}
else
{
self waittill( "death" );
}
tracker.aicount -- ;
}
camper_trigger_think( trigger )
{
// wait( 0.05 );
tokens = strtok( trigger.script_linkto, " " );
spawners = [];
nodes = [];
for ( i = 0; i < tokens.size; i++ )
{
token = tokens[ i ];
ai = getent( token, "script_linkname" );
if ( isdefined( ai ) )
{
spawners = add_to_array( spawners, ai );
continue;
}
node = getnode( token, "script_linkname" );
if ( !isdefined( node ) )
{
println( "Warning: Trigger token number " + token + " did not exist." );
continue;
}
nodes = add_to_array( nodes, node );
}
assertEX( spawners.size, "camper_spawner without any spawners associated" );
assertEX( nodes.size, "camper_spawner without any nodes associated" );
assertEX( nodes.size >= spawners.size, "camper_spawner with less nodes than spawners" );
trigger waittill( "trigger" );
nodes = array_randomize( nodes );
for ( i = 0; i < nodes.size; i++ )
nodes[ i ].claimed = false;
j = 0;
for ( i = 0; i < spawners.size; i++ )
{
spawner = spawners[ i ];
if ( !isdefined( spawner ) )
continue;
if ( isdefined( spawner.script_spawn_here ) )
{
// these guys spawn where they're placed
continue;
}
while ( isdefined( nodes[ j ].script_noteworthy ) && nodes[ j ].script_noteworthy == "dont_spawn" )
j++;
spawner.origin = nodes[ j ].origin;
spawner.angles = nodes[ j ].angles;
spawner add_spawn_function( ::claim_a_node, nodes[ j ] );
j++;
}
array_thread( spawners, ::add_spawn_function, ::camper_guy );
array_thread( spawners, ::add_spawn_function, ::move_when_enemy_hides, nodes );
array_thread( spawners, ::spawn_ai );
}
camper_guy()
{
self.goalradius = 8;
self.fixednode = true;
}
move_when_enemy_hides( nodes )
{
self endon( "death" );
waitingForEnemyToDisappear = false;
while ( 1 )
{
// it is important that we check whether our enemy is defined before doing a cansee check on him.
if ( !isalive( self.enemy ) )
{
self waittill( "enemy" );
waitingForEnemyToDisappear = false;
continue;
}
if ( isplayer( self.enemy ) )
{
if ( self.enemy ent_flag( "player_has_red_flashing_overlay" ) || flag( "player_flashed" ) )
{
// player is wounded, chase him with a suicide charge. One must fall!
self.fixednode = 0;
for ( ;; )
{
self.goalradius = 180;
self setgoalpos( level.player.origin );
wait( 1 );
}
return;
}
}
if ( waitingForEnemyToDisappear )
{
if ( self cansee( self.enemy ) )
{
wait .05;
continue;
}
waitingForEnemyToDisappear = false;
}
else
{
if ( self cansee( self.enemy ) )
{
// enemy is seen, wait until you cant see him
waitingForEnemyToDisappear = true;
}
wait .05;
continue;
}
// you cant see him, 2 / 3rds of the time move to a different node
if ( randomint( 3 ) > 0 )
{
node = find_unclaimed_node( nodes );
if ( isdefined( node ) )
{
self claim_a_node( node, self.claimed_node );
self waittill( "goal" );
}
}
}
}
claim_a_node( claimed_node, old_claimed_node )
{
self setgoalnode( claimed_node );
self.claimed_node = claimed_node;
claimed_node.claimed = true;
if ( isdefined( old_claimed_node ) )
old_claimed_node.claimed = false;
// self OrientMode( "face angle", claimed_node.angles[ 1 ] );
}
find_unclaimed_node( nodes )
{
for ( i = 0; i < nodes.size; i++ )
{
if ( nodes[ i ].claimed )
continue;
else
return nodes[ i ];
}
return undefined;
}
// flood_spawner
flood_trigger_think( trigger )
{
assertEX( isDefined( trigger.target ), "flood_spawner at " + trigger.origin + " without target" );
floodSpawners = getEntArray( trigger.target, "targetname" );
assertEX( floodSpawners.size, "flood_spawner at with target " + trigger.target + " without any targets" );
array_thread( floodSpawners, ::flood_spawner_init );
trigger waittill( "trigger" );
// reget the target array since targets may have been deletes, etc... between initialization and triggering
floodSpawners = getEntArray( trigger.target, "targetname" );
array_thread( floodSpawners, ::flood_spawner_think, trigger );
}
flood_spawner_init( spawner )
{
assertEX( ( isDefined( self.spawnflags ) && self.spawnflags & 1 ), "Spawner at origin" + self.origin + "/" + ( self getOrigin() ) + " is not a spawner!" );
}
trigger_requires_player( trigger )
{
if ( !isdefined( trigger ) )
return false;
return isDefined( trigger.script_requires_player );
}
two_stage_spawner_think( trigger )
{
trigger_target = getent( trigger.target, "targetname" );
assertEx( isdefined( trigger_target ), "Trigger with targetname two_stage_spawner that doesnt target anything." );
assertEx( issubstr( trigger_target.classname, "trigger" ), "Triggers with targetname two_stage_spawner must target a trigger" );
assertEx( isdefined( trigger_target.target ), "The second trigger of a two_stage_spawner must target at least one spawner" );
// wait until _spawner has initialized before adding spawn functions
waittillframeend;
spawners = getentarray( trigger_target.target, "targetname" );
for ( i = 0; i < spawners.size; i++ )
{
spawners[ i ].script_moveoverride = true;
spawners[ i ] add_spawn_function( ::wait_to_go, trigger_target );
}
trigger waittill( "trigger" );
spawners = getentarray( trigger_target.target, "targetname" );
array_thread( spawners, ::spawn_ai );
}
wait_to_go( trigger_target )
{
trigger_target endon( "death" );
self endon( "death" );
self.goalradius = 8;
trigger_target waittill( "trigger" );
self thread go_to_node();
}
flood_spawner_think( trigger )
{
self endon( "death" );
self notify( "stop current floodspawner" );
self endon( "stop current floodspawner" );
// pyramid spawner is a spawner that targets another spawner or spawners
// First the targetted spawners spawn, then when they die, the reinforcement spawns from
// the spawner this initial spawner
if ( is_pyramid_spawner() )
{
pyramid_spawn( trigger );
return;
}
requires_player = trigger_requires_player( trigger );
script_delay();
while ( self.count > 0 )
{
while ( requires_player && !level.player isTouching( trigger ) )
wait( 0.5 );
dontShareEnemyInfo = ( isdefined( self.script_stealth ) && flag( "_stealth_enabled" ) && !flag( "_stealth_spotted" ) );
if ( isdefined( self.script_forcespawn ) )
soldier = self stalingradSpawn( dontShareEnemyInfo );
else
soldier = self doSpawn( dontShareEnemyInfo );
if ( spawn_failed( soldier ) )
{
wait( 2 );
continue;
}
soldier thread reincrement_count_if_deleted( self );
soldier thread expand_goalradius( trigger );
soldier waittill( "death", attacker );
if ( !player_saw_kill( soldier, attacker ) )
{
self.count++;
}
else if ( isdefined( level.ac130_flood_respawn ) )
{
if ( isdefined( level.ac130gunner ) && ( attacker == level.ac130gunner ) )
{
if ( randomint( 2 ) == 0 )
self.count++;
}
}
// soldier was deleted, not killed
if ( !isDefined( soldier ) )
continue;
if ( !script_wait() )
wait( randomFloatRange( 5, 9 ) );
}
}
player_saw_kill( guy, attacker )
{
if ( isdefined( self.script_force_count ) )
if ( self.script_force_count )
return true;
if ( !isdefined( guy ) )
{
return false;
}
if ( isalive( attacker ) )
{
if ( isplayer( attacker ) )
{
return true;
}
if ( distance( attacker.origin, level.player.origin ) < 200 )
{
// player was near the guy that killed the ai?
return true;
}
}
else
{
if ( isdefined( attacker ) )
{
if ( attacker.classname == "worldspawn" )
{
return false;
}
if ( distance( attacker.origin, level.player.origin ) < 200 )
{
// player was near the guy that killed the ai?
return true;
}
}
}
if ( distance( guy.origin, level.player.origin ) < 200 )
{
// player was near the guy that got killed?
return true;
}
// did the player see the guy die?
return bulletTracePassed( level.player geteye(), guy geteye(), false, undefined );
}
is_pyramid_spawner()
{
if ( !isdefined( self.target ) )
return false;
ent = getentarray( self.target, "targetname" );
if ( !ent.size )
return false;
return issubstr( ent[ 0 ].classname, "actor" );
}
pyramid_death_report( spawner )
{
spawner.spawn waittill( "death" );
self notify( "death_report" );
}
pyramid_spawn( trigger )
{
self endon( "death" );
requires_player = trigger_requires_player( trigger );
script_delay();
if ( requires_player )
{
while ( !level.player isTouching( trigger ) )
wait( 0.5 );
}
// first spawn all the guys we target. They decrement our count tho, so we spawn them in a random order in case
// our count is just 1( default )
spawners = getentarray( self.target, "targetname" );
/#
for ( i = 0; i < spawners.size; i++ )
assertEx( issubstr( spawners[ i ].classname, "actor" ), "Pyramid spawner targets non AI!" );
#/
// the spawners have to report their death to the head of the pyramid so it can kill itself when they're all gone
self.spawners = 0;
array_thread( spawners, ::pyramid_spawner_reports_death, self );
offset = randomint( spawners.size );
for ( i = 0; i < spawners.size; i++ )
{
if ( self.count <= 0 )
return;
offset++;
if ( offset >= spawners.size )
offset = 0;
spawner = spawners[ offset ];
// the count is local to self, not to the spawners that are targetted
spawner set_count( 1 );
soldier = spawner spawn_ai();
if ( spawn_failed( soldier ) )
{
// assertEx( 0, "Initial spawning from spawner at " + self.origin + " failed." );
wait( 2 );
continue;
}
self.count -- ;
spawner.spawn = soldier;
soldier thread reincrement_count_if_deleted( self );
soldier thread expand_goalradius( trigger );
thread pyramid_death_report( spawner );
}
culmulative_wait = 0.01;
while ( self.count > 0 )
{
self waittill( "death_report" );
script_wait();
wait( culmulative_wait );
culmulative_wait += 2.5;
offset = randomint( spawners.size );
for ( i = 0; i < spawners.size; i++ )
{
// cleanup in case any spawners were deleted
spawners = array_removeUndefined( spawners );
if ( !spawners.size )
{
if ( isdefined( self ) )
self delete();
return;
}
offset++;
if ( offset >= spawners.size )
offset = 0;
spawner = spawners[ offset ];
// find a spawner that has lost its AI
if ( isalive( spawner.spawn ) )
continue;
// spawn from self now, we're reinforcement
if ( isdefined( spawner.target ) )
{
self.target = spawner.target;
}
else
{
self.target = undefined;
}
soldier = self spawn_ai();
if ( spawn_failed( soldier ) )
{
wait( 2 );
continue;
}
assertEx( isdefined( spawner ), "Theoretically impossible." );
soldier thread reincrement_count_if_deleted( self );
soldier thread expand_goalradius( trigger );
spawner.spawn = soldier;
thread pyramid_death_report( spawner );
if ( self.count <= 0 )
return;
}
}
}
pyramid_spawner_reports_death( parent )
{
parent endon( "death" );
parent.spawners++;
self waittill( "death" );
parent.spawners -- ;
if ( !parent.spawners )
parent delete();
}
expand_goalradius( trigger )
{
if ( isDefined( self.script_forcegoal ) )
return;
// triggers with a script_radius of - 1 dont override the goalradius
// triggers with a script_radius of anything else set the goalradius to that size
radius = level.default_goalradius;
if ( isdefined( trigger ) )
{
if ( isdefined( trigger.script_radius ) )
{
if ( trigger.script_radius == -1 )
return;
radius = trigger.script_radius;
}
}
if ( isdefined( self.script_forcegoal ) )
return;
// expands the goalradius of the ai after they reach there initial goal.
self endon( "death" );
self waittill( "goal" );
self.goalradius = radius;
}
drop_health_timeout_thread()
{
self endon( "death" );
wait( 95 );
self notify( "timeout" );
}
drop_health_trigger_think()
{
self endon( "timeout" );
thread drop_health_timeout_thread();
self waittill( "trigger" );
change_player_health_packets( 1 );
}
traceShow( org )
{
for ( ;; )
{
line( org + ( 0, 0, 100 ), org, ( 0.2, 0.5, 0.8 ), 0.5 );
wait( 0.05 );
}
}
/*drophealth()
{
// wait until regular scripts have a change to set self.script_nohealth on the guy from script, after spawn_failed.
waittillframeend;
waittillframeend;
if ( !isalive( self ) )
return;
if ( isdefined( self.script_nohealth ) )
return;
self waittill( "death" );
if ( !isdefined( self ) )
return;
// drop health disabled once again
if ( 1 )
return;
// has enough time passed since the last health drop?
if ( gettime() < level.next_health_drop_time )
return;
// have enough guys died?
level.guys_to_die_before_next_health_drop -- ;
if ( level.guys_to_die_before_next_health_drop > 0 )
return;
level.guys_to_die_before_next_health_drop = randomintrange( 2, 5 );
level.next_health_drop_time = gettime() + 3500;// probably make this a _gameskill thing later
trace = bullettrace( self.origin + ( 0, 0, 50 ), self.origin + ( 0, 0, -220 ), true, self );
health = spawn( "script_model", self.origin + ( 0, 0, 10 ) );
health.origin = trace[ "position" ];
// health setmodel( "com_trashbag" );
trigger = spawn( "trigger_radius", self.origin + ( 0, 0, 10 ), 0, 10, 32 );
trigger.radius = 10;
trigger drop_health_trigger_think();
trigger delete();
health delete();
// health = spawn( "item_health", self.origin + ( 0, 0, 10 ) );
// health.angles = ( 0, randomint( 360 ), 0 );
/*
if ( isdefined( level._health_queue ) )
{
if ( isdefined( level._health_queue[ level._health_queue_num ] ) )
level._health_queue[ level._health_queue_num ] delete();
}
level._health_queue[ level._health_queue_num ] = health;
level._health_queue_num++;
if ( level._health_queue_num > level._health_queue_max )
level._health_queue_num = 0;
*/
//}
show_bad_path()
{
/#
if ( getdebugdvar( "debug_badpath_count" ) == "" )
setdvar( "debug_badpath_count", 10 );
self endon( "death" );
last_bad_path_time = -5000;
bad_path_count = 0;
for ( ;; )
{
self waittill( "bad_path", badPathPos );
if ( !level.debug_badpath )
continue;
if ( gettime() - last_bad_path_time > 5000 )
{
bad_path_count = 0;
}
else
{
bad_path_count++;
}
last_bad_path_time = gettime();
if ( bad_path_count < getdebugdvarint( "debug_badpath_count" ) )
continue;
for ( p = 0; p < 10 * 20; p++ )
{
line( self.origin, badPathPos, ( 1, 0.4, 0.1 ), 0, 10 * 20 );
wait( 0.05 );
}
}
#/
}
random_spawn( trigger )
{
trigger waittill( "trigger" );
// get a random target and all the links to that target and spawn them
spawners = getentarray( trigger.target, "targetname" );
if ( !spawners.size )
return;
spawner = random( spawners );
spawners = [];
spawners[ spawners.size ] = spawner;
// grab the other spawners linked to the parent spawner
if ( isdefined( spawner.script_linkto ) )
{
links = strTok( spawner.script_linkto, " " );
for ( i = 0; i < links.size; i++ )
{
spawners[ spawners.size ] = getent( links[ i ], "script_linkname" );
}
}
waittillframeend;// _load needs to finish entirely before we can add spawn functions to spawners
array_thread( spawners, ::add_spawn_function, ::blowout_goalradius_on_pathend );
array_thread( spawners, ::spawn_ai );
}
blowout_goalradius_on_pathend()
{
if ( isDefined( self.script_forcegoal ) )
return;
self endon( "death" );
self waittill( "reached_path_end" );
if ( !isdefined( self getGoalVolume() ) )
self.goalradius = level.default_goalradius;
}
objective_event_init( trigger )
{
flag = trigger get_trigger_flag();
assertEx( isdefined( flag ), "Objective event at origin " + trigger.origin + " does not have a script_flag. " );
flag_init( flag );
assertEx( isdefined( level.deathSpawner[ trigger.script_deathChain ] ), "The objective event trigger for deathchain " + trigger.script_deathchain + " is not associated with any AI." );
/#
if ( !isdefined( level.deathSpawner[ trigger.script_deathChain ] ) )
return;
#/
while ( level.deathSpawner[ trigger.script_deathChain ] > 0 )
level waittill( "spawner_expired" + trigger.script_deathChain );
flag_set( flag );
}
setup_ai_eq_triggers()
{
self endon( "death" );
// ai placed in the level run their spawn func before the triggers are initialized
waittillframeend;
self.is_the_player = isplayer( self );
self.eq_table = [];
self.eq_touching = [];
for ( i = 0; i < level.eq_trigger_num; i++ )
{
self.eq_table[ i ] = false;
}
}
ai_array()
{
level.ai_array[ level.unique_id ] = self;
self waittill( "death" );
waittillframeend;
level.ai_array[ level.unique_id ] = undefined;
}
#using_animtree( "generic_human" );
spawner_dronespawn( spawner )
{
drone = spawner spawnDrone();
drone UseAnimTree( #animtree );
if ( drone.weapon != "none" )
{
weapon_model = getWeaponModel( drone.weapon );
drone attach( weapon_model, "tag_weapon_right" );
hideTagList = GetWeaponHideTags( drone.weapon );
for ( i = 0; i < hideTagList.size; i++ )
drone HidePart( hideTagList[ i ], weapon_model );
}
drone.spawner = spawner;
drone.drone_delete_on_unload = ( isdefined( spawner.script_noteworthy ) && spawner.script_noteworthy == "drone_delete_on_unload" );
spawner notify( "drone_spawned", drone );
return drone;
}
spawner_makerealai( drone )
{
if ( !isdefined( drone.spawner ) )
{
println( " -- -- failed dronespawned guy info -- -- " );
println( "drone.classname: " + drone.classname );
println( "drone.origin : " + drone.origin );
assertmsg( "makerealai called on drone does with no .spawner" );
}
orgorg = drone.spawner.origin;
organg = drone.spawner.angles;
drone.spawner.origin = drone.origin;
drone.spawner.angles = drone.angles;
guy = drone.spawner stalingradspawn();
failed = spawn_failed( guy );
if ( failed )
{
println( " -- -- failed dronespawned guy info -- -- " );
println( "failed guys spawn position : " + drone.origin );
println( "failed guys spawner export key: " + drone.spawner.export );
println( "getaiarray size is: " + getaiarray().size );
println( " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- " );
assertMSG( "failed to make real ai out of drone( see console for more info )" );
}
guy.vehicle_idling = drone.vehicle_idling;
guy.vehicle_position = drone.vehicle_position;
guy.standing = drone.standing;
guy.forceColor = drone.forceColor;
drone.spawner.origin = orgorg;
drone.spawner.angles = organg;
drone delete();
return guy;
}
death_achievements()
{
self thread death_achievements_rappel_hack();
self waittill( "death", attacker, type, weapon );
if ( ! isdefined( self ) )
return;// deleted
if ( !self isBadGuy() )
return;
if ( ! isdefined( attacker ) )
return;
// thread achieve_ten_plus_hellfire( attacker );->moved to _REMOVEMISSLE
//dont want these to include long death because it's not as obvious
thread achieve_2_birds_1_stone( attacker, type );
thread achieve_driveby( attacker );
thread achieve_harder_they_fall( attacker );
thread achieve_riotshield_melee( attacker, type );
thread achieve_slowmo_breach_kills( attacker );
thread achieve_downed_kills( attacker );
thread achieve_stealth_knife( attacker, type );
thread achieve_threesome( attacker, type, weapon );
//long deaths
if( isdefined( self.last_dmg_type ) )
type = self.last_dmg_type;
thread achieve_some_like_hot_thermal( attacker, type );
thread achieve_one_man_army( attacker, type, weapon );
thread achieve_akimbo( attacker, type );
}
death_achievements_rappel_hack()
{
self waittill( "rope_death", attacker );
if ( ! isdefined( self ) )
return;// deleted
thread achieve_harder_they_fall( attacker );
}
achieve_engineer_turret( attacker )
{
if ( attacker.code_classname != "misc_turret" )
return;
if ( ! isdefined( attacker.owner ) )
return;
if ( !isplayer( attacker.owner ) )
return;
if ( !isdefined( attacker.owner.achieve_engineer_turret ) )
attacker.owner.achieve_engineer_turret = 1;
else
attacker.owner.achieve_engineer_turret++;
if ( attacker.owner.achieve_engineer_turret == 10 )
attacker.owner player_giveachievement_wrapper( "ENGINEER" );
}
achieve_ten_plus_hellfire( attacker )
{
Bplayer = false;
if( isplayer( attacker ) || ( isdefined( attacker.attacker ) && isplayer( attacker.attacker ) ) )
Bplayer = true;
if ( !Bplayer )
return;
if ( ! isdefined( attacker.is_controlling_UAV ) )
return;
if ( !isdefined( attacker.achieve_ten_plus_hellfire ) )
attacker.achieve_ten_plus_hellfire = 1;
else
attacker.achieve_ten_plus_hellfire++;
if ( attacker.achieve_ten_plus_hellfire == 10 )
attacker player_giveachievement_wrapper( "TEN_PLUS_FOOT_MOBILES" );
level notify( "achieve_ten_plus_hellfire" );
level endon( "achieve_ten_plus_hellfire" );
wait .5;
attacker.achieve_ten_plus_hellfire = undefined;
}
achieve_key_master_shotgun( attacker, type )
{
if ( ! isplayer( attacker ) )
return;
weapon = attacker getcurrentweapon();
if ( ! isdefined( weapon ) )
{
attacker.achieve_key_master_shotgun = undefined;
return;
}
if( weapon == "none" )
{
attacker.achieve_key_master_shotgun = undefined;
return;
}
if ( weaponinventorytype( weapon ) != "altmode" )
{
attacker.achieve_key_master_shotgun = undefined;
return;
}
class = weaponClass( weapon );
if ( ! isdefined( class ) )
{
attacker.achieve_key_master_shotgun = undefined;
return;
}
if( type != "MOD_PISTOL_BULLET" || class != "spread" )
{
attacker.achieve_key_master_shotgun = undefined;
return;
}
if ( !isdefined( attacker.achieve_key_master_shotgun ) )
attacker.achieve_key_master_shotgun = 1;
else
attacker.achieve_key_master_shotgun++;
if ( attacker.achieve_key_master_shotgun == 5 )
attacker player_giveachievement_wrapper( "KEY_MASTER" );
level notify( "achieve_key_master_shotgun" );
level endon( "achieve_key_master_shotgun" );
wait 12;
level.achieve_key_master_shotgun = undefined;
}
achieve_some_like_hot_thermal( attacker, type )
{
if ( ! isplayer( attacker ) )
return;
weapon = attacker getcurrentweapon();
if ( ! isdefined( weapon ) )
{
attacker.achieve_some_like_hot_thermal = undefined;
return;
}
if( weapon == "none" )
{
attacker.achieve_some_like_hot_thermal = undefined;
return;
}
if ( ! WeaponHasThermalScope( weapon ) )
{
attacker.achieve_some_like_hot_thermal = undefined;
return;
}
if( !( type == "MOD_PISTOL_BULLET" || type == "MOD_RIFLE_BULLET" ) )
{
attacker.achieve_some_like_hot_thermal = undefined;
return;
}
if ( !isdefined( attacker.achieve_some_like_hot_thermal ) )
attacker.achieve_some_like_hot_thermal = 1;
else
attacker.achieve_some_like_hot_thermal++;
if ( attacker.achieve_some_like_hot_thermal == 6 )
attacker player_giveachievement_wrapper( "SOME_LIKE_IT_HOT" );
level notify( "achieve_some_like_hot_thermal" );
level endon( "achieve_some_like_hot_thermal" );
wait 12;
level.achieve_some_like_hot_thermal = undefined;
}
achieve_2_birds_1_stone( attacker, type )
{
if ( ! isplayer( attacker ) )
return;
if( !( type == "MOD_PISTOL_BULLET" || type == "MOD_RIFLE_BULLET" ) )
return;
if ( isdefined( attacker.drivingVehicle ) )
{
attacker.achieve_2_birds_1_stone = undefined;
return;
}
if ( !isdefined( attacker.achieve_2_birds_1_stone ) )
attacker.achieve_2_birds_1_stone = 1;
else
attacker.achieve_2_birds_1_stone++;
if ( attacker.achieve_2_birds_1_stone == 2 )
attacker player_giveachievement_wrapper( "TWO_BIRDS_WITH_ONE_STONE" );
waittillframeend;
attacker.achieve_2_birds_1_stone = undefined;
}
achieve_driveby( attacker )
{
if ( ! isplayer( attacker ) )
return;
if ( ! isdefined( attacker.drivingVehicle ) )
{
attacker.achieve_driveby = undefined;
return;
}
if ( !isdefined( attacker.achieve_driveby ) )
attacker.achieve_driveby = 1;
else
attacker.achieve_driveby++;
if ( attacker.achieve_driveby == 20 )
attacker player_giveachievement_wrapper( "DRIVE_BY" );
}
achieve_harder_they_fall( attacker )
{
if( isdefined( self.achieve_harder_they_fall ) )
return;
self.achieve_harder_they_fall = 1;
if ( ! isplayer( attacker ) )
return;
if ( ! isdefined( self.rappeller ) )
{
attacker.achieve_harder_they_fall = undefined;
return;
}
if ( !isdefined( attacker.achieve_harder_they_fall ) )
attacker.achieve_harder_they_fall = 1;
else
attacker.achieve_harder_they_fall++;
if ( attacker.achieve_harder_they_fall == 2 )
attacker player_giveachievement_wrapper( "THE_HARDER_THEY_FALL" );
level notify( "achieve_harder_they_fall" );
level endon( "achieve_harder_they_fall" );
wait 12;
attacker.achieve_harder_they_fall = undefined;
}
achieve_riotshield_melee( attacker, type )
{
if ( !isplayer( attacker ) )
return;
if ( type != "MOD_MELEE" )
return;
weapon = attacker getcurrentweapon();
if ( ! isdefined( weapon ) )
return;
if( weapon != "riotshield" )
return;
attacker player_giveachievement_wrapper( "UNNECESSARY_ROUGHNESS" );
}
achieve_slowmo_breach_kills( attacker )
{
if ( !isplayer( attacker ) )
return;
if ( !isdefined( attacker.isbreaching ) )
{
attacker.achieve_slowmo_breach_kills = undefined;
return;
}
if ( !isdefined( attacker.achieve_slowmo_breach_kills ) )
attacker.achieve_slowmo_breach_kills = 1;
else
attacker.achieve_slowmo_breach_kills++;
//wait a second to make sure the player didn't fire off a 5th shot in a reasonable amount of time
wait .1;
//killed a hostage
if( !isdefined( attacker.achieve_slowmo_breach_kills ) )
return;
if ( attacker.achieve_slowmo_breach_kills == 4 && attacker.breaching_shots_fired <= 4 )
attacker player_giveachievement_wrapper( "KNOCK_KNOCK" );
}
achieve_downed_kills( attacker )
{
if ( !isplayer( attacker ) )
return;
if ( !attacker.laststand )
{
attacker.achieve_downed_kills = undefined;
return;
}
if ( !isdefined( attacker.achieve_downed_kills ) )
attacker.achieve_downed_kills = 1;
else
attacker.achieve_downed_kills++;
if ( attacker.achieve_downed_kills == 4 )
attacker player_giveachievement_wrapper( "DOWNED_BUT_NOT_OUT" );
}
achieve_one_man_army( attacker, type, weapon )
{
if ( !isplayer( attacker ) )
return;
if ( ! isdefined( weapon ) )
{
if( attacker isusingturret() )
{
weapon = "turret";
}
else
{
attacker.achieve_one_man_army = [];
return;
}
}
else
if( type == "MOD_MELEE" && weapon != "riotshield" )
weapon = "knife";
if ( !isdefined( attacker.achieve_one_man_army ) )
attacker.achieve_one_man_army = [];
foreach( weap in attacker.achieve_one_man_army )
{
if( weapon != weap )
continue;
attacker.achieve_one_man_army = [];
}
attacker.achieve_one_man_army[ attacker.achieve_one_man_army.size ] = weapon;
if( attacker.achieve_one_man_army.size == 5 )
attacker player_giveachievement_wrapper( "ONE_MAN_ARMY" );
}
achieve_akimbo( attacker, type )
{
if ( !isplayer( attacker ) )
return;
if( !( attacker isDualWielding() ) )
{
attacker.achieve_akimbo = undefined;
return;
}
if( !( type == "MOD_PISTOL_BULLET" || type == "MOD_RIFLE_BULLET" ) )
{
attacker.achieve_akimbo = undefined;
return;
}
if ( !isdefined( attacker.achieve_akimbo ) )
attacker.achieve_akimbo = 1;
else
attacker.achieve_akimbo++;
if ( attacker.achieve_akimbo == 10 )
attacker player_giveachievement_wrapper( "LOOK_MA_TWO_HANDS" );
}
achieve_stealth_knife( attacker, type )
{
if ( !isplayer( attacker ) )
return;
if ( type != "MOD_MELEE" )
return;
if ( ! self ent_flag_exist( "_stealth_normal" ) )
return;
if ( ! self ent_flag( "_stealth_normal" ) )
return;
if ( isdefined( self.lastenemysightpos ) )
return;
if ( isdefined( self.lastenemysighttime ) && self.lastenemysighttime > 0 )
return;
attacker player_giveachievement_wrapper( "NO_REST_FOR_THE_WARY" );
}
achieve_threesome( attacker, type, weapon )
{
if ( !isplayer( attacker ) )
return;
if( type != "MOD_GRENADE_SPLASH" )
{
attacker.achieve_threesome = undefined;
return;
}
if( !isdefined( weapon ) )
{
attacker.achieve_threesome = undefined;
return;
}
if( weaponinventorytype( weapon ) == "offhand" )
{
attacker.achieve_threesome = undefined;
return;
}
if ( !isdefined( attacker.achieve_threesome ) )
attacker.achieve_threesome = 1;
else
attacker.achieve_threesome++;
if ( attacker.achieve_threesome == 3 )
attacker player_giveachievement_wrapper( "THREESOME" );
waittillframeend;
attacker.achieve_threesome = undefined;
}
add_random_killspawner_to_spawngroup()
{
assertex( isdefined( self.script_randomspawn ), "Spawner at origin " + self.origin + " has no script_randomspawn!" );
spawngroup = self.script_random_killspawner;
subgroup = self.script_randomspawn;
if ( !isdefined( level.killspawn_groups[ spawngroup ] ) )
level.killspawn_groups[ spawngroup ] = [];
if ( !isdefined( level.killspawn_groups[ spawngroup ][ subgroup ] ) )
level.killspawn_groups[ spawngroup ][ subgroup ] = [];
level.killspawn_groups[ spawngroup ][ subgroup ][ self.export ] = self;
}
add_to_spawngroup()
{
assertex( isdefined( self.script_spawnsubgroup ), "Spawner at origin " + self.origin + " has no script_spawnsubgroup!" );
spawngroup = self.script_spawngroup;
subgroup = self.script_spawnsubgroup;
if ( !isdefined( level.spawn_groups[ spawngroup ] ) )
level.spawn_groups[ spawngroup ] = [];
if ( !isdefined( level.spawn_groups[ spawngroup ][ subgroup ] ) )
level.spawn_groups[ spawngroup ][ subgroup ] = [];
level.spawn_groups[ spawngroup ][ subgroup ][ self.export ] = self;
}
start_off_running()
{
self endon( "death" );
self.disableexits = true;
wait( 3 );
self.disableexits = false;
}
deathtime()
{
self endon( "death" );
wait( self.script_deathtime );
wait( randomfloat( 10 ) );
self kill();
}