#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(); }