cod5-sdk/raw/maps/_spawner.gsc
2009-07-17 00:00:00 +00:00

5870 lines
139 KiB
Text

#include maps\_utility;
#include maps\_anim;
#include common_scripts\utility;
// .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()
{
// CODER_MOD: Bryce (05/08/08): Useful output for debugging replay system
/#
if( getdebugdvar( "replay_debug" ) == "1" )
println("File: _spawner.gsc. Function: main()\n");
#/
precachemodel("grenade_bag");
// precachemodel("com_trashbag");
//****************************************************************************
// connect auto AI spawners
//****************************************************************************
// create default threatbiasgroups;
CreateThreatBiasGroup( "allies" );
CreateThreatBiasGroup( "axis" );
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, moved this into the player init.
// level.player SetThreatBiasGroup( "allies" );
//level.player thread xp_init();
/*
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 = [];
level.deathflags = [];
level.spawner_number = 0;
level.go_to_node_arrays = [];
level.next_health_drop_time = 0;
level.guys_to_die_before_next_health_drop = RandomIntRange( 1, 4 );
level.default_goalradius = 2048;
level.default_goalheight = 80;
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 );
/*
if( IsDefined( level._ai_health ) )
{
for( i = 0; i < level._max_script_health+1; i++ )
{
if( IsDefined( level._ai_health[i] ) )
{
rnum = RandomInt( level._ai_health[i].size );
level._ai_health[i][rnum].drophealth = true;
}
}
for( i = 0; i < ai.size; i++ )
{
if( IsDefined( ai[i].drophealth ) )
{
ai[i] thread drophealth();
}
}
}
*/
level.ai_classname_in_level = [];
spawners = GetSpawnerArray();
for( i = 0; i < spawners.size; i++ )
{
spawners[i] thread spawn_prethink();
}
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", ::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 );
#/
// CODER_MOD: Bryce (05/08/08): Useful output for debugging replay system
/#
if( getdebugdvar( "replay_debug" ) == "1" )
println("File: _spawner.gsc. Function: main() - COMPLETE\n");
#/
// level thread flood_spawner_monitor();
level thread trigger_spawner_monitor();
}
// 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()
{
keys = getarraykeys( level.deathflags );
level.deathflags = [];
for ( i=0; i < keys.size; i++ )
{
deathflag = keys[ i ];
level.deathflags[ deathflag ] = [];
level.deathflags[ deathflag ][ "spawners" ] = [];
level.deathflags[ deathflag ][ "ai" ] = [];
if ( !isdefined( level.flag[ deathflag ] ) )
{
flag_init( deathflag );
}
}
}
spawn_guys_until_death_or_no_count()
{
self endon( "death" );
self waittill( "count_gone" );
}
deathflag_check_count()
{
self endon( "death" );
// wait until the end of the frame, so other scripts have a chance to reincrement the spawners count
waittillframeend;
if ( self.count > 0 )
return;
self notify( "count_gone" );
}
ai_deathflag()
{
level.deathflags[ self.script_deathflag ][ "ai" ][ self.ai_number ] = self;
ai_number = self.ai_number;
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 );
}
spawner_deathflag()
{
level.deathflags[ self.script_deathflag ] = true;
// 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 );
}
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;
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
}
while(!(self ok_to_trigger_spawn()))
{
wait_network_frame();
}
guy = spawner spawn_ai();
if( spawn_failed( guy ) )
{
spawner notify( "spawn_failed" );
}
level._numTriggerSpawned ++;
if( IsDefined( self.Wait ) &&( self.Wait > 0 ) )
{
wait( self.Wait );
}
}
}
trigger_spawner_monitor()
{
println("Trigger spawner monitor running...");
level._numTriggerSpawned = 0;
while(1)
{
wait_network_frame();
wait_network_frame();
level._numTriggerSpawned = 0;
}
}
ok_to_trigger_spawn(forceChoke)
{
if(isdefined(forceChoke))
{
choked = forceChoke;
}
else
{
choked = false;
}
if(isdefined(self.script_trigger) && NumRemoteClients())
{
trigger = self.script_trigger;
if(isdefined(trigger.targetname) && (trigger.targetname == "flood_spawner"))
{
choked = true;
if(isdefined(trigger.script_choke) && !trigger.script_choke)
{
choked = false;
}
}
else if(isdefined(trigger.spawnflags) && (trigger.spawnflags & 32))
{
if(isdefined(trigger.script_choke) && trigger.script_choke)
{
choked = true;
}
}
}
if(isdefined(self.targetname) && (self.targetname == "drone_axis" || self.targetname == "drone_allies"))
{
choked = true;
}
if(choked && NumRemoteClients())
{
if(level._numTriggerSpawned > 2)
{
println("Triggerspawn choke.");
return false;
}
}
return true;
}
trigger_spawner( trigger )
{
assertEx( isdefined( trigger.target ), "Triggers with flag TRIGGER_SPAWN at " + trigger.origin + " must target at least one spawner." );
trigger endon( "death" );
trigger waittill( "trigger" );
spawners = getentarray( trigger.target, "targetname" );
for(i = 0; i < spawners.size; i ++)
{
spawners[i].script_trigger = trigger;
}
/* choke = false;
if(isdefined(trigger.script_choke) && trigger.script_choke)
{
choke = true;
for(i = 0; i < spawners.size; i ++)
{
spawners.script_choke = trigger.script_choke;
}
} */
/* if(choke && NumRemoteClients())
{
spread_array_thread( spawners, ::trigger_spawner_spawns_guys);
}
else */
{
array_thread( spawners, ::trigger_spawner_spawns_guys );
}
}
trigger_spawner_spawns_guys()
{
self endon( "death" );
self script_delay();
while(!self ok_to_trigger_spawn())
{
wait_network_frame();
}
if ( isdefined( self.script_drone ) )
{
spawned = dronespawn( self );
level._numTriggerSpawned ++;
assertEx( isdefined( level.drone_spawn_func ), "You need to put maps\_drone::init(); in your level script!" );
spawned thread [[ level.drone_spawn_func ]]();
return;
}
else
if ( !issubstr( self.classname, "actor" ) )
return;
if( isdefined( self.script_noenemyinfo ) && isdefined( self.script_forcespawn ) )
spawned = self stalingradSpawn( true );
else if( isdefined( self.script_noenemyinfo ) )
spawned = self doSpawn( true );
else if( isdefined( self.script_forcespawn ) )
spawned = self stalingradSpawn();
else
spawned = self doSpawn();
level._numTriggerSpawned ++;
}
flood_spawner_scripted( spawners )
{
assertex( IsDefined( spawners ) && spawners.size, "Script tried to flood spawn without any spawners" );
array_thread( spawners, ::flood_spawner_init );
/* if(NumRemoteClients())
{
spread_array_thread(spawners, ::flood_spawner_think);
}
else */
{
array_thread( spawners, ::flood_spawner_think );
}
}
reincrement_count_if_deleted( spawner )
{
spawner endon( "death" );
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;
}
trigger Delete();
}
random_killspawner( trigger )
{
random_killspawner = trigger.script_random_killspawner;
trigger waittill ("trigger");
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 ] );
}
}
select_random_spawn( triggered_spawners );
}
kill_spawner( trigger )
{
killspawner = trigger.script_killspawner;
trigger waittill( "trigger" );
spawners = GetSpawnerArray();
for( i = 0 ; i < spawners.size ; i++ )
{
if( ( IsDefined( spawners[i].script_killspawner ) ) &&( killspawner == spawners[i].script_killspawner ) )
{
spawners[i] Delete();
}
}
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].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" ) )
{
level thread spawn_prethink( spawners[i] );
}
}
}
*/
}
waittillDeathOrPainDeath()
{
self endon( "death" );
self waittill( "pain_death" ); // pain that ends in death
}
drop_gear()
{
team = self.team;
waittillDeathOrPainDeath();
if( !IsDefined( self ) )
{
return;
}
// CODER_MOD - Austin (7/3/08): added cold dead hands collectible
if ( maps\_collectibles::has_collectible( "collectible_dead_hands" ) )
{
return;
}
self.ignoreForFixedNodeSafeCheck = true;
if( self.grenadeAmmo <= 0 )
{
return;
}
if( IsDefined( self.dropweapon ) && !self.dropweapon )
{
return;
}
level.nextGrenadeDrop--;
if( level.nextGrenadeDrop > 0 )
{
return;
}
level.nextGrenadeDrop = 2 + RandomInt( 2 );
max = 25;
min = 12;
spawn_grenade_bag( self.origin +( RandomInt( max )-min, RandomInt( max )-min, 2 ) +( 0, 0, 42 ), ( 0, RandomInt( 360 ), 0 ), self.team );
}
random_tire( start, end )
{
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 physicslaunch( model.origin, vel );
wait ( randomintrange ( 8, 12 ) );
model delete();
}
spawn_grenade_bag( origin, angles, 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_" + self.grenadeWeapon, origin );
level.grenade_cache[team][index] = grenade;
level.grenade_cache_index[team] = ( index + 1 ) % 16;
grenade.angles = angles;
grenade.count = self.grenadeammo;
grenade SetModel( "grenade_bag" );
}
// This spawns the AI and then deletes him
dronespawn_setstruct( spawner )
{
// spawn a guy, grab his info, delete him. Poor guy
if ( dronespawn_check() )
return;
guy = spawner stalingradspawn(); // first frame everybody spawns and spawn_failed isn't effective yet
// failed = spawn_failed( guy );
// assert( !failed );
spawner.count++ ; // replenish the spawner
dronespawn_setstruct_from_guy( guy );
guy delete();
}
dronespawn_check()
{
//spawn a guy, grab his info. delete him. Poor guy
if(isdefined(level.dronestruct[self.classname]))
return true;
return false;
}
// This creates the struct of models from the AI and remembers it
dronespawn_setstruct_from_guy ( guy )
{
//spawn a guy, grab his info. delete him. Poor guy
if(dronespawn_check())
return;
struct = spawnstruct();
size = guy getattachsize();
struct.attachedmodels = [];
for( i = 0; i < size; i++ )
{
struct.attachedmodels[i] = guy GetAttachModelName( i );
struct.attachedtags[i] = guy GetAttachTagName( i );
}
struct.model = guy.model;
// return struct;
level.dronestruct[guy.classname] = struct;
}
empty()
{
}
spawn_prethink()
{
assert( self != level );
level.ai_classname_in_level[ self.classname ] = true;
/#
if (getdvar ("noai") != "off")
{
// NO AI in the level plz
self.count = 0;
return;
}
#/
prof_begin( "spawn_prethink" );
if( IsDefined( self.script_drone ) )
{
self thread dronespawn_setstruct(self); //threaded because spawn_failed is weird
}
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;
}
deathflag_func = ::empty;
if ( isdefined( self.script_deathflag ) )
{
deathflag_func = ::deathflag_check_count;
// 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();
}
/*
// 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" );
self waittill( "spawned", spawn );
[[ deathflag_func ]]();
if( !IsAlive( spawn ) )
{
continue;
}
//assertex( IsAlive( spawn ), "Spawner spawned a dead guy somehow." );
if( IsDefined( level.spawnerCallbackThread ) )
{
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;
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" );
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();
}
run_spawn_functions()
{
self endon( "death" );
if ( !isdefined( self.spawn_funcs ) )
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[ "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" ] ]]();
}
for ( i=0; i < level.spawn_funcs[ self.team ].size; i++ )
{
func = level.spawn_funcs[ self.team ][ i ];
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;
/#
// keep them around in developer mode, for debugging
self.spawn_funcs = self.saved_spawn_functions;
self.saved_spawn_functions = undefined;
#/
self.spawn_funcs = undefined;
}
// the functions that run on death for the ai
deathFunctions()
{
self waittill( "death", other );
for ( i=0; i < self.deathFuncs.size; i++ )
{
array = self.deathFuncs[ i ];
switch( array[ "params" ] )
{
case 0:
[[ array[ "func" ] ]]( other );
break;
case 1:
[[ array[ "func" ] ]]( other, array[ "param1" ] );
break;
case 2:
[[ array[ "func" ] ]]( other, array[ "param1" ], array[ "param2" ] );
break;
case 3:
[[ array[ "func" ] ]]( other, array[ "param1" ], array[ "param2" ], array[ "param3" ] );
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[ "node" ];
get_func = array[ "get_target_func" ];
for ( i = 0; i < targets.size; i++ )
{
crawl_target_and_init_flags( targets[ i ], get_func );
}
}
}
// Actually do the spawn_think
spawn_think_action( targetname )
{
// handle default ai flags for ent_flag * functions
self thread maps\_utility::ent_flag_init_ai_standards();
self thread tanksquish();
// CODER_MOD: Austin (6/24/08): used to track berserker kill streaks
if ( maps\_collectibles::has_collectible( "collectible_berserker" ) )
{
self thread maps\_collectibles_game::berserker_death();
}
// ai get their values from spawners and theres no need to have this value on ai
self.spawner_number = undefined;
/#
// if( GetDebugDvar( "debug_misstime" ) == "start" )
// {
// self thread maps\_debug::debugMisstime();
// }
thread show_bad_path();
#/
if( !IsDefined( self.ai_number ) )
{
set_ai_number();
}
if ( isdefined( self.script_dontshootwhilemoving ) )
{
self.dontshootwhilemoving = true;
}
// functions called on death
if ( !isdefined( self.deathFuncs ) )
{
self.deathFuncs = [];
}
thread deathFunctions();
if ( isdefined( self.script_deathflag ) )
{
thread ai_deathflag();
}
// MikeD (4/12/2008): Added the ability to set script_animname which will be the animname for the AI
if( IsDefined( self.script_animname ) )
{
self.animname = self.script_animname;
}
if ( isdefined( self.script_forceColor ) )
{
// send all forcecolor through a centralized function
set_force_color( self.script_forceColor );
// MikeD (9/5/2007): All AI spawned will do "replace_on_death" unless told not to.
if( !IsDefined( self.script_no_respawn ) || self.script_no_respawn < 1 )
{
self thread replace_on_death();
}
}
if ( isdefined( self.script_fixednode ) )
{
// force the fixednode setting on an ai
self.fixednode = ( self.script_fixednode == 1 );
}
else
{
// allies use fixednode by default
self.fixednode = self.team == "allies";
}
set_default_covering_fire();
// which eq triggers am I touching?
// thread setup_ai_eq_triggers();
// thread ai_array();
self EqOff();
if( ( IsDefined( self.script_moveoverride ) ) &&( self.script_moveoverride == 1 ) )
{
override = true;
}
else
{
override = false;
}
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();
}
level thread maps\_friendlyfire::friendly_fire_think( self );
if( IsDefined( self.script_goalvolume ) )
{
// 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 ) )
{
// if( !ThreatBiasGroupExists( self.script_threatbiasgroup ) )
// CreateThreatBiasGroup( self.script_threatbiasgroup );
self SetThreatBiasGroup( self.script_threatbiasgroup );
}
else if( self.team == "allies" )
{
self SetThreatBiasGroup( "allies" );
}
else
{
self SetThreatBiasGroup( "axis" );
}
/#
if ( self.type == "human" && isdefined(self.export))
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 );
#/
set_default_pathenemy_settings();
self.heavy_machine_gunner = IsSubStr( self.classname, "mgportable" ) ||
IsSubStr( self.classname, "30cal" ) ||
IsSubStr( self.classname, "mg42" ) ||
IsSubStr( self.classname, "dp28" );
maps\_gameskill::grenadeAwareness();
if( IsDefined( self.script_bcdialog ) )
{
self set_battlechatter( self.script_bcdialog );
}
// Set the accuracy for the spawner
if( IsDefined( self.script_accuracy ) )
{
self.baseAccuracy = self.script_accuracy;
}
self.walkdist = 16;
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;
}
else if( WeaponClass( self.weapon ) == "gas" )
{
self.maxSightDistSqrd = 1024 * 1024;
}
if( self.team != "axis" )
{
self thread use_for_ammo();
// 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;
}
// Set the on death thread for friendlies
// self maps\_names::get_name();
level thread friendly_waittill_death( self );
}
if ( self.team == "axis" )
{
if ( self.type == "human" )
{
self thread drop_gear();
//self thread drophealth();
}
/#
self thread maps\_damagefeedback::monitorDamage();
#/
// SCRIPTER_MOD: JesseS (4/14/2008): Need to add this back in, used for arcade mode.
self thread maps\_gameskill::auto_adjust_enemy_death_detection();
// CODER_MOD: Austin (6/10/2008): Added health regen thread for zombie collectible
if ( maps\_collectibles::has_collectible( "collectible_zombie" ) )
{
self.gib_override = true;
self thread maps\_collectibles_game::zombie_health_regen();
}
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, we will no longer use script_favoriteenemy, since it only
// was used here for "player"
// sets the favorite enemy of a spawner
// if( IsDefined( self.script_favoriteenemy ) )
// {
// // println( "favorite enemy is defined" );
// if( self.script_favoriteenemy == "player" )
// {
// self.favoriteenemy = level.player;
// level.player.targetname = "player";
// // println( "setting favoriteenemy = 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 );
}
else
{
// Make axis have 150 health so they can do dying pain.
if( self.team == "axis")
{
self.health = 150;
}
}
//player score stuff
//self thread player_score_think();
// Gives AI grenades
if( IsDefined( self.script_grenades ) )
{
self.grenadeAmmo = self.script_grenades;
}
// slayback 10/22/07: defaults for each cod5 aitype are specified in aitypes_charactersettings.gdt
/*
else
{
self.grenadeAmmo = 3;
}
*/
// Guzzo 7/31/08, removing COD4 check
// 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;
}
// GLocke [8/30] allow the AI to be killed during animations (like traversals)
if( IsDefined(self.script_allowdeath) )
{
self.allowdeath = self.script_allowdeath;
}
// SCRIPTER_MOD: JesseS (1/11/2008): turn off weapon drops. ask a lead before using!
if( IsDefined(self.script_nodropweapon) )
{
self.dropweapon = 0;
}
// The AI will spawn and attack the player
if( IsDefined( self.script_playerseek ) )
{
if( IsDefined( self.script_radius ) )
{
self.goalradius = self.script_radius;
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, now we will find the closest player to me, and make him my goalentity
// self SetGoalEntity( level.player );
self SetGoalEntity( get_closest_player(self.origin) );
if (isdefined(self.script_sound))
{
self animscripts\face::SaySpecificDialogue( undefined, self.script_sound, 1.0 );
}
return;
}
// The AI will spawn and follow a patrol
if( IsDefined( self.script_patroller ) )
{
self thread maps\_patrol::patrol();
return;
}
// MikeD: New spiderhole feature
if( IsDefined( self.script_spiderhole ) )
{
self thread maps\_spiderhole::spiderhole();
return;
}
// MikeD: New Banzai feature
if( IsDefined( self.script_banzai ) )
{
self thread maps\_banzai::spawned_banzai_dynamic();
}
else if( IsDefined( self.script_banzai_spawn ) )
{
self thread maps\_banzai::spawned_banzai_immediate();
}
// 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;
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, now we will find the closest player to me, and make him my goalentity
// self SetGoalEntity( level.player );
self SetGoalEntity( get_closest_player(self.origin) );
if (isdefined(self.script_sound))
{
self animscripts\face::SaySpecificDialogue( undefined, self.script_sound, 1.0 );
}
level thread delayed_player_seek_think( self );
return;
}
if( self.heavy_machine_gunner )
{
// SCRIPTER_MOD: JesseS (6/25/2007): took this out, I think this is how they handled cod2 stuff,
// more of this logic is contained else where now (anim scripts and _mg_penetration.gsc).
thread maps\_mgturret::portable_mg_behavior();
}
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( override )
{
set_goalradius_based_on_settings();
self SetGoalPos( self.origin );
return;
}
//Crazy Coder Mod! King (7/23/08): ACTOR_MIN_GOAL_RADIUS is now 4
assertEx( (self.goalradius == 8 || 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_goalradius_based_on_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();
}
}
set_default_covering_fire()
{
// allies use "provide covering fire" mode in fixednode mode.
self.provideCoveringFire = self.team == "allies" && self.fixedNode;
}
// reset this guy to default spec
scrub_guy()
{
self EqOff();
// if( self.team == "allies" )
// self SetThreatBiasGroup( "allies" );
// else
self SetThreatBiasGroup( self.team );
// if( IsDefined( self.script_bcdialog ) )
// self set_battlechatter( self.script_bcdialog );
// Set the accuracy for the spawner
self.baseAccuracy = 1;
set_default_pathenemy_settings();
maps\_gameskill::grenadeAwareness();
self clear_force_color();
set_default_covering_fire();
self.interval = 96;
self.disableArrivals = undefined;
self.ignoreme = false;
self.threatbias = 0;
self.pacifist = false;
self.pacifistWait = 20;
self.IgnoreRandomBulletDamage = false;
self.playerPushable = true;
self.precombatrunEnabled = 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.activatecrosshair = true;
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;
if( WeaponClass( self.weapon ) == "gas" )
{
self.maxSightDistSqrd = 1024 * 1024;
}
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";
// Gives AI grenades
if( IsDefined( self.script_grenades ) )
{
self.grenadeAmmo = self.script_grenades;
}
else
{
self.grenadeAmmo = 3;
}
}
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;
self SetGoalVolume( level.goalVolumes[self.script_goalvolume] );
}
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 )
{
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[ "node" ], array[ "get_target_func" ], array[ "set_goal_func_quits" ], optional_arrived_at_node_func );
}
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 )
{
// 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 );
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 );
self waittill( "goal" );
[[ optional_arrived_at_node_func ]]( node );
if ( isdefined( node.script_flag_set ) )
{
flag_set( node.script_flag_set );
}
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.target ) )
break;
nextNode_array = [[ get_target_func ]]( node.target );
if ( !nextNode_array.size )
break;
node = nextNode_array;
}
self notify( "reached_path_end" );
self set_goalradius_based_on_settings( node );
}
go_to_node_set_goal_pos( ent )
{
self set_goal_pos( ent.origin );
}
go_to_node_set_goal_node( node )
{
self set_goal_node( node );
}
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.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[ "origin" ] = ::get_target_ents;
get_target_func[ "node" ] = ::get_target_nodes;
get_target_func[ "struct" ] = ::get_target_structs;
set_goal_func_quits[ "origin" ] = ::go_to_node_set_goal_pos;
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[ "node" ][ 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 = "origin";
}
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[ "node" ] = node;
}
array[ "get_target_func" ] = get_target_func[ goal_type ];
array[ "set_goal_func_quits" ] = set_goal_func_quits[ goal_type ];
return array;
}
set_goalradius_based_on_settings( node )
{
if( IsDefined( self.script_radius ) )
{
// use the override from radiant
self.goalradius = self.script_radius;
return;
}
if( IsDefined( self.script_banzai_spawn ) )
{
self.goalradius = 64;
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
self.goalradius = level.default_goalradius;
}
reachPathEnd()
{
self waittill( "goal" );
self notify( "reached_path_end" );
}
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.team == "axis" && self.health == 150 )
{
self.health = 100; // mg42 operators aren't going to do long death
self.a.disableLongDeath = true;
}
unmanned = false;
// 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 );
// }
// }
// MikeD (4/7/2008): Updated manual targets
if( targets.size > 0 )
{
turret.manual_targets = targets;
turret SetMode( "auto_nonai" );
turret thread maps\_mgturret::burst_fire_unmanned();
unmanned = true;
}
}
if( !unmanned )
{
self thread maps\_mgturret::mg42_firing( turret );
}
turret notify( "startfiring" );
}
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
fallback_spawner_think( num, node_array, ignoreWhileFallingBack )
{
self endon( "death" );
level.max_fallbackers[num]+= self.count;
firstspawn = true;
while( self.count > 0 )
{
self waittill( "spawned", spawn );
if( firstspawn )
{
if( GetDvar( "fallback" ) == "1" )
{
println( "^a First spawned: ", num );
}
level notify( ( "fallback_firstspawn" + num ) );
firstspawn = false;
}
maps\_spawner::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.max_fallbackers[num]--;
continue;
}
spawn thread fallback_ai_think( num, node_array, "is spawner", ignoreWhileFallingBack ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
}
// level notify( ( "fallbacker_died" + num ) );
}
fallback_ai_think_death( ai, num )
{
ai waittill( "death" );
level.current_fallbackers[num]--;
level notify( ( "fallbacker_died" + num ) );
}
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
fallback_ai_think( num, node_array, spawner, ignoreWhileFallingBack )
{
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_array ) ) &&( level.fallback_initiated[num] ) )
{
self thread fallback_ai( num, node_array, ignoreWhileFallingBack ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
/*
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" );
if (isdefined(ai.fallback_node))
{
ai.fallback_node.fallback_occupied = false;
}
level notify( ( "fallback_reached_goal" + num ) );
// ai notify( "fallback_notify" );
}
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
fallback_goal( ignoreWhileFallingBack )
{
self waittill( "goal" );
self.ignoresuppression = false;
if( IsDefined( ignoreWhileFallingBack ) && ignoreWhileFallingBack )
{
self.ignoreall = false;
}
self notify( "fallback_notify" );
self notify( "stop_coverprint" );
}
fallback_interrupt()
{
self notify( "stop_fallback_interrupt" );
self endon( "stop_fallback_interrupt" );
self endon( "stop_going_to_node" );
self endon ("goto next fallback");
self endon ("fallback_notify");
self endon( "death" );
while(1)
{
origin = self.origin;
wait 2;
if ( self.origin == origin )
{
self.ignoreall = false;
return;
}
}
}
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
fallback_ai( num, node_array, ignoreWhileFallingBack )
{
self notify( "stop_going_to_node" );
self endon( "stop_going_to_node" );
self endon ("goto next fallback");
self endon( "death" ); // SRS 5/21/2008: bugfix - this kept running after AIs were dead
node = undefined;
// set the goalnode
while( 1 )
{
ASSERTEX((node_array.size >= level.current_fallbackers[num]), "Number of fallbackers exceeds number of fallback nodes for fallback # " + num + ". Add more fallback nodes or reduce possible fallbackers.");
node = node_array[RandomInt( node_array.size )];
if (!isdefined(node.fallback_occupied) || !node.fallback_occupied)
{
// set the node occupied so we know to find another one
node.fallback_occupied = true;
self.fallback_node = node;
break;
}
wait( 0.1 );
}
self StopUseTurret();
self.ignoresuppression = true;
if( self.ignoreall == false && IsDefined( ignoreWhileFallingBack ) && ignoreWhileFallingBack )
{
self.ignoreall = true;
self thread fallback_interrupt();
}
self SetGoalNode( node );
if( node.radius != 0 )
{
self.goalradius = node.radius;
}
self endon( "death" );
level thread fallback_death( self, num );
self thread fallback_goal( ignoreWhileFallingBack ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
if( GetDvar( "fallback" ) == "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" );
self endon ("death");
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 );
maps\_spawner::waitframe();
}
}
// This gets set up on each fallback trigger
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
fallback_overmind( num, group, ignoreWhileFallingBack, percent )
{
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 = maps\_utility::add_to_array( fallback_nodes, nodes[i] );
}
}
if( IsDefined( fallback_nodes ) )
{
level thread fallback_overmind_internal( num, group, fallback_nodes, ignoreWhileFallingBack, percent ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
}
}
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
fallback_overmind_internal( num, group, fallback_nodes, ignoreWhileFallingBack, percent )
{
// SCRIPTER_MOD: JesseS (7/13/200): fixed up current vs max fallbackers, added a level max per
// script_fallback
level.current_fallbackers[num] = 0; // currently alive fallbackers
level.max_fallbackers[num] = 0; // maximum fallbackers
level.spawner_fallbackers[num] = 0; // number of fallback spawners
level.fallback_initiated[num] = false; // has fallback started?
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, ignoreWhileFallingBack ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
level.spawner_fallbackers[num]++;
}
}
}
assertex ( level.spawner_fallbackers[num] <= fallback_nodes.size, "There are more fallback spawners than fallback nodes. Add more node or remove spawners from script_fallback: "+ 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, undefined, undefined, ignoreWhileFallingBack ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
}
}
if( ( !level.current_fallbackers[num] ) &&( !level.spawner_fallbackers[num] ) )
{
return;
}
spawners = undefined;
ai = undefined;
thread fallback_wait( num, group, ignoreWhileFallingBack, percent ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
level waittill( ( "fallbacker_trigger" + num ) );
fallback_add_previous_group(num, fallback_nodes);
if( GetDvar( "fallback" ) == "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 = maps\_utility::add_to_array( fallback_ai, ai[i] );
}
}
ai = undefined;
if( !IsDefined( fallback_ai ) )
{
return;
}
if( !IsDefined( percent ) )
{
percent = 0.4;
}
first_half = fallback_ai.size * percent;
first_half = Int( first_half );
level notify( "fallback initiated " + num );
fallback_text( fallback_ai, 0, first_half );
first_half_ai = [];
for( i = 0; i < first_half; i++ )
{
fallback_ai[i] thread fallback_ai( num, fallback_nodes, ignoreWhileFallingBack );
first_half_ai[i] = fallback_ai[i];
}
// waits until everyone as at their goalnode
for( i = 0; i < first_half; i++ )
{
level waittill( ( "fallback_reached_goal" + num ) );
}
fallback_text( fallback_ai, first_half, fallback_ai.size );
// SCRIPTER_MOD: JesseS (7/13/2007): added some stuff to check to make sure both halves get to go to nodes
// turns out that someone else is getting other guys' nodes, so we gotta pick a new one
for( i = 0; i < fallback_ai.size; i++ )
{
if( IsAlive( fallback_ai[i] ) )
{
set_fallback = true;
for (p = 0; p < first_half_ai.size; p++)
{
if ( isalive(first_half_ai[p]))
{
if (fallback_ai[i] == first_half_ai[p])
{
set_fallback = false;
}
}
}
if (set_fallback)
{
fallback_ai[i] thread fallback_ai( num, fallback_nodes, ignoreWhileFallingBack );
}
}
}
}
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 );
/* WTF is this?
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;
}
}
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
fallback_wait( num, group, ignoreWhileFallingBack, percent )
{
level endon( ( "fallbacker_trigger" + num ) );
if( GetDvar( "fallback" ) == "1" )
{
println( "^a Fallback wait: ", num );
}
for( i = 0; i < level.spawner_fallbackers[num]; i++ )
{
if( GetDvar( "fallback" ) == "1" )
{
println( "^a Waiting for spawners to be hit: ", num, " i: ", i );
}
level waittill( ( "fallback_firstspawn" + num ) );
}
if( GetDvar( "fallback" ) == "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, undefined, undefined, ignoreWhileFallingBack ); // SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
}
}
ai = undefined;
// if( !total_fallbackers )
// return;
deadfallbackers = 0;
while( deadfallbackers < level.max_fallbackers[num] * percent )
{
if( GetDvar( "fallback" ) == "1" )
{
println( "^cwaiting for " + deadfallbackers + " to be more than " +( level.max_fallbackers[num] * 0.5 ) );
}
level waittill( ( "fallbacker_died" + num ) );
deadfallbackers++;
}
println( deadfallbackers , " fallbackers have died, time to retreat" );
level notify( ( "fallbacker_trigger" + num ) );
}
// for fallback trigger
// SRS 5/3/2008: updated to allow AIs to ignoreall while falling back
// Note: multiple triggers will need to have script_ignoreall set identically on all triggers
// for consistency when AIs decide to ignore while falling back or not.
fallback_think( trigger )
{
ignoreWhileFallingBack = false;
if( IsDefined( trigger.script_ignoreall ) && trigger.script_ignoreall )
{
ignoreWhileFallingBack = true;
}
if( ( !IsDefined( level.fallback ) ) ||( !IsDefined( level.fallback[trigger.script_fallback] ) ) )
{
// MikeD (5/7/2008): Added the ability to determine the percent of guys that will fallback
// when this trigger is hit.
percent = 0.5;
if( IsDefined( trigger.script_percent ) )
{
percent = trigger.script_percent / 100;
}
level thread fallback_overmind( trigger.script_fallback, trigger.script_fallback_group, ignoreWhileFallingBack, percent );
}
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 );
}
// This is for allowing guys to fallback more than once
fallback_add_previous_group(num, node_array)
{
// check to see if its the first time or not
if (!isdefined (level.current_fallbackers[num - 1]))
{
return;
}
// max fallbackers for next group add stragglers from previous group
for (i = 0; i < level.current_fallbackers[num - 1]; i++)
{
level.max_fallbackers[num]++;
}
// current fallbackers from previous group should be added to next group as well
for (i = 0; i < level.current_fallbackers[num - 1]; i++)
{
level.current_fallbackers[num]++;
}
ai = GetAiArray();
for( i = 0; i < ai.size; i++ )
{
if( ( ( IsDefined( ai[i].script_fallback ) ) && ( ai[i].script_fallback == (num - 1) ) ) )
{
//ai[i] notify( "stop_going_to_node" );
ai[i].script_fallback++;
if (isdefined (ai[i].fallback_node))
{
ai[i].fallback_node.fallback_occupied = false;
ai[i].fallback_node = undefined;
}
//ai[i] thread fallback_ai( num, node_array );
}
}
}
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 );
maps\_spawner::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 );
maps\_spawner::waitframe();
}
}
// SCRIPTER_MOD: JesseS (7/13/2007): not being used it appears, but we should keep it around for reference
//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" ) == "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;
// }
// }
//}
use_for_ammo()
{
// Use for ammo is disabled pending further design decisions.
/*
while( IsAlive( self ) )
{
self waittill( "trigger" );
currentweapon = level.player GetCurrentWeapon();
level.player GiveMaxAmmo( currentweapon );
println( "Giving player ammo for current weapon" );
wait( 3 );
}
*/
}
friendly_waittill_death( spawned )
{
// Disabled globally by Zied, addresses bug 3092, too much text on screen.
/////////////
/*
if( IsDefined( spawned.script_noDeathMessage ) )
{
return;
}
name = spawned.name;
spawned waittill( "death" );
if( ( level.script != "stalingrad" ) &&( level.script != "stalingrad_nolight" ) )
{
println( name, " - KIA" );
}
*/
}
delete_me()
{
maps\_spawner::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;
}
waitframe()
{
wait( 0.05 );
}
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] ) )
{
// SCRIPTER_MOD
// MikeD( 03/08/07 ): Added support for the temp allies.
// MikeD( 03/14/07 ): Added the check for info_player_respawn
if( targs[i].classname[7] != "l" && targs[i].classname[8] != "l" && targs[i].classname != "info_player_respawn" )
{
println( "Friendyl_wave spawner at ", targs[i].origin, " is not an ally" );
assert = true;
}
}
}
if( assert )
{
maps\_utility::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].count = 0;
}
}
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" );
// SCRIPTER_MOD
// MikeD: Filter out any info_player_respawns from the friendlywave spawn points.
spawn = filter_player_respawns( spawn );
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" );
// SCRIPTER_MOD
// MikeD: Filter out any info_player_respawns from the friendlywave spawn points.
spawn = filter_player_respawns( spawn );
}
else if( !script_delay )
{
num = RandomInt( spawn.size );
}
else if( num == spawn.size )
{
num = 0;
}
spawn[num].count = 1;
if( isdefined( spawn[ num ].script_noenemyinfo ) && isdefined( spawn[ num ].script_forcespawn ) )
spawned = spawn[ num ] stalingradSpawn( true );
else if( isdefined( spawn[ num ].script_noenemyinfo ) )
spawned = spawn[ num ] doSpawn( true );
else if( IsDefined( spawn[num].script_forcespawn ) )
{
spawned = spawn[num] StalingradSpawn();
}
else
{
spawned = spawn[num] DoSpawn();
}
spawn[num].count = 0;
if( spawn_failed( spawned ) )
{
wait( 0.2 );
continue;
}
if( IsDefined( level.friendlywave_thread ) )
{
level thread[[level.friendlywave_thread]]( spawned );
}
else
{
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, TODO, we need the SETFRIENDLYGLOBALCHAIN, in order to fix this properly.
// For now, we'll have the spawned ai's goalentity be players[0]
// spawned SetGoalEntity( level.player );
players = get_players();
if ( isdefined( players ) && players.size > 0 )
{
spawned SetGoalEntity( players[0] );
}
}
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" );
}
}
// SCRIPTER_MOD
// MikeD: Filter out any info_player_respawns from the friendlywave spawn points.
filter_player_respawns( array )
{
newarray = [];
clear_player_spawnpoints();
for( i = 0; i < array.size; i++ )
{
if( IsDefined( array[i].classname ) && array[i].classname == "info_player_respawn" )
{
add_player_spawnpoInt( array[i] );
continue;
}
newarray[newarray.size] = array[i];
}
return newarray;
}
friendly_mgTurret( trigger )
{
/#
if( !IsDefined( trigger.target ) )
{
maps\_utility::error( "No target for friendly_mg42 trigger, origin:" + trigger GetOrigin() );
}
#/
node = GetNode( trigger.target, "targetname" );
/#
if( !IsDefined( node.target ) )
{
maps\_utility::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( IsSentient( other ) )
{
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player
// if( other == level.player )
if( IsPlayer( 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");
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;
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, now we check to see if any player is too close.
// if( Distance( level.player.origin, node.origin ) < 100 )
players = get_players();
for( q = 0; q < players.size; q++ )
{
if( Distancesquared( players[q].origin, node.origin ) < 100 * 100 )
{
// println( "^a player too close" );
return false;
}
}
if( IsDefined( self.chainnode ) )
{
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, now we check to see if all players are too far
// if( Distance( level.player.origin, self.chainnode.origin ) > 1100 )
player_count = 0;
for( q = 0; q < players.size; q++ )
{
if( Distancesquared( players[q].origin, self.chainnode.origin ) > 1100 * 1100 )
{
player_count++;
}
}
if( player_count == players.size )
{
// 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;
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, now we check to see if any play is too close
// if( Distance( level.player.origin, node.origin ) < 32 )
players = get_players();
for( q = 0; q < players.size; q++ )
{
if( Distancesquared( players[q].origin, node.origin ) < 32 * 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;
}
// self thread maps\_mgturret::mg42_firing( mg42 );
// mg42 notify( "startfiring" );
tanksquish()
{
if ( isdefined( level.noTankSquish ) )
{
assertex( level.noTankSquish, "level.noTankSquish must be true or undefined" );
return;
}
if ( isdefined( level.levelHasVehicles ) && !level.levelHasVehicles )
return;
while ( 1 )
{
self waittill( "damage", amt, who, force, b, mod, d, e );
if ( mod != "MOD_CRUSH" ) //GLocke: used to be "MOD_CRUSHED"
{
continue;
}
if ( !isdefined( self ) )
{
// deleted?
return;
}
if ( isalive( self ) )
continue;
if ( !isalive( who ) )
return;
//Glocke: 8/21/08 removed this and I hope that doesn't cause a problem
//if ( !isdefined( who.vehicletype ) )
// return;
force = vectorscale( force, 50000 );
force = ( force[ 0 ], force[ 1 ], abs( force[ 2 ] ) );
// GLocke: 8/21/08 if level._effect["tanksquish"] is defined then play it, didn't want to add this fx to every level
if(IsDefined( level._effect ) && IsDefined( level._effect["tanksquish"] ) )
{
PlayFX( level._effect["tanksquish"], self.origin + (0, 0, 30));
}
self startRagdoll();
// CODER MOD: Lucas - This could cause destructibles to run out of memory by shooting it out of the level
/* org = self.origin;
for ( i = 0; i < 5; i++ )
{
if ( isdefined( self ) )
org = self.origin;
PhysicsJolt( org, 250, 250, force );
// Print3d( org, ".", (1,1,1) );
wait( 0.05 );
}*/
self playsound( "human_crunch" );
return;
}
}
// 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.count = 1;
if( isdefined( self.script_noenemyinfo ) && isdefined( self.script_forcespawn ) )
spawn = self stalingradSpawn( true );
else if( isdefined( self.script_noenemyinfo ) )
spawn = self doSpawn( true );
else 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;
for( ;; )
{
self waittill( "trigger", other );
if( !objectiveIsAllowed() )
{
continue;
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player
// if( self IsTouching( level.player ) )
if( any_player_IsTouching( self ) )
{
playerTriggered = true;
}
else
{
if( !IsAlive( other ) )
{
continue;
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player
// if( other == level.player )
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 ) )
{
select_random_Spawn( 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 );
}
}
}
select_random_spawn(spawners)
{
groups = [];
for ( i=0; i < spawners.size; i++ )
{
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.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;
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, since this wants the current distance, let's get the closest player and
// get that distance.
// dist = Distance( level.player.origin, self.origin );
closest_player = get_closest_player( self.origin );
// MikeD: Distancesquared() is cheaper, since we're comparing numbers, and not using the actual distance.
dist = Distancesquared( closest_player.origin, self.origin );
prof_begin( "flood_and_secure_spawner_think" );
while ( count )
{
self.trueCount = count;
self.count = 2;
wait( delay );
if( isdefined( self.script_noenemyinfo ) && isdefined( self.script_forcespawn ) )
spawn = self stalingradSpawn( true );
else if( isdefined( self.script_noenemyinfo ) )
spawn = self doSpawn( true );
else if( isdefined( self.script_forcespawn ) )
spawn = self stalingradSpawn();
else
spawn = self doSpawn();
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].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;
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, we'll get the closest player instead
// if( Distance( level.player.origin, org ) < 700 )
closest_player = get_closest_player( org );
if( Distancesquared( closest_player.origin, org ) < 700 * 700 )
{
return true;
}
// /#thread animscripts\utility::debugline( level.player.origin, org, ( 0, 1, 0 ), 20 ); #/
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, we'll get the closest player instead
// return BulletTracePassed( level.player GetEye(), ai GetEye(), false, undefined );
return BulletTracePassed( closest_player GetEye(), ai GetEye(), false, undefined );
}
waittillRestartOrDistance( dist )
{
self endon( "flood_begin" );
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): Changed to "squared", cheaper.
// dist = dist * 0.75; // require the player to get a bit closer to force restart the spawner
dist = dist * ( 0.75 * 0.75 ); // require the player to get a bit closer to force restart the spawner
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, instead we'll check the closest player
// while( Distance( level.player.origin, self.origin ) > dist )
closest_player = get_closest_player( self.origin );
while( Distancesquared( closest_player.origin, self.origin ) > dist )
{
wait( 1 );
closest_player = get_closest_player( self.origin );
}
}
flood_and_secure_spawn( spawner )
{
self thread flood_and_secure_spawn_goal();
self waittill( "death", other );
// CODER_MOD
// JamesS 11/28/07 no more level.player
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 = 64;
}
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 = 64;
}
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 DoDamage( ( self.health *0.5 ), ( 0, 0, 0 ) );
return;
}
}
if( IsDefined( node.target ) )
{
turret = GetEnt( node.target, "targetname" );
if( IsDefined( turret ) &&( turret.classname == "misc_mgturret" || turret.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 ) )
{
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 )
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, instead check to see if any player is touching this trigger
// while( level.player IsTouching( self ) )
while( any_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 )
{
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, TODO: we need the setglobalfriendlychain for this to be setup properly.
// For now, I'm going to use players[0].
// level.player set_friendly_chain_wrapper( node );
players = get_players();
players[0] 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" );
}
// SCRIPTER_MOD
// MikeD( 3/23/2007 ): This is not used.
//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 ) )
{
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;
}
// CODER_MOD - JamesS
// no more level.player
if ( IsPlayer(self.enemy) )
{
if ( 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( self.enemy.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 setgoalnode( 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" );
for(i = 0; i < floodSpawners.size; i ++)
{
floodSpawners[i].script_trigger = trigger;
}
/* choke = true;
if(isdefined(trigger.script_choke) && !trigger.script_choke)
{
choke = true;
}
for(i = 0; i < floodSpawners.size; i ++)
{
floodSpawners[i].script_choke = choke;
}
*/
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" );
/* if(choke && NumRemoteClients())
{
spread_array_thread(floodSpawners, ::flood_spawner_think, trigger);
}
else*/
{
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 )
{
// SCRIPTER_MOD
// MikeD( 3/23/2007 ): No more level.player. So now we see if any player is touching the trigger.
// while( requires_player && !level.player IsTouching( trigger ) )
// AlexL (6/26/2007): This line asserts when trigger is undefined. New version accomodated undefined trigger.
// while( requires_player && !any_player_IsTouching( trigger ) )
if( requires_player ) // 0 if trigger is undefined
{
while( !any_player_IsTouching( trigger ) )
{
wait( 0.5 );
}
}
while(!(self ok_to_trigger_spawn()))
{
wait_network_frame();
}
if( isdefined( self.script_noenemyinfo ) && isdefined( self.script_forcespawn ) )
soldier = self stalingradSpawn( true );
else if( isdefined( self.script_noenemyinfo ) )
soldier = self doSpawn( true );
else if( isdefined( self.script_forcespawn ) )
soldier = self stalingradSpawn();
else
soldier = self doSpawn();
if( spawn_failed( soldier ) )
{
wait( 2 );
continue;
}
level._numTriggerSpawned ++;
soldier thread reincrement_count_if_deleted( self );
soldier thread expand_goalradius( trigger );
soldier waittill( "death", attacker );
if ( !player_saw_kill( soldier, attacker ) )
{
self.count++;
}
// soldier was deleted, not killed
if( !IsDefined( soldier ) )
{
continue;
}
if( !script_wait( true ) )
{
players = get_players();
if (players.size == 1)
{
wait( RandomFloatrange( 5, 9 ) );
}
else if (players.size == 2)
{
wait( RandomFloatrange( 3, 6 ) );
}
else if (players.size == 3)
{
wait( RandomFloatrange( 1, 4 ) );
}
else if (players.size == 4)
{
wait( RandomFloatrange( 0.5, 1.5 ) );
}
}
}
}
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;
}
players = get_players();
for( q = 0; q < players.size; q++ )
{
if ( DistanceSquared( attacker.origin, players[q].origin ) < 200 * 200 )
{
// player was near the guy that killed the ai?
return true;
}
}
}
else
{
if ( isdefined( attacker ) )
{
if ( attacker.classname == "worldspawn" )
{
return false;
}
// CODER_MOD got rid of level.player
player = get_closest_player( attacker.origin );
if ( isdefined( player ) && distance( attacker.origin, player.origin ) < 200 )
{
// player was near the guy that killed the ai?
return true;
}
}
}
// SCRIPTER_MOD
// MikeD( 3/22/2007 ): No more level.player, we'll get the closest player instead
// if( Distance( level.player.origin, org ) < 700 )
closest_player = get_closest_player( guy.origin );
if ( isdefined( closest_player ) && distance( guy.origin, closest_player.origin ) < 200 )
{
// player was near the guy that got killed?
return true;
}
// did the player see the guy die?
return bulletTracePassed( closest_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 )
{
// SCRIPTER_MOD
// MikeD( 3/23/2007 ): No more level.player, now we check to see if any player is touching the trigger.
// while( !level.player IsTouching( trigger ) )
while( !any_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.count = 1;
while(!(self ok_to_trigger_spawn()))
{
wait_network_frame();
}
soldier = spawner spawn_ai();
level._numTriggerSpawned++;
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( true );
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;
}
while(!(self ok_to_trigger_spawn()))
{
wait_network_frame();
}
soldier = self spawn_ai();
if( spawn_failed( soldier ) )
{
wait( 2 );
continue;
}
level._numTriggerSpawned ++;
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" ) == "" )
setdvar( "debug_badpath", "" );
self endon( "death" );
last_bad_path_time = -5000;
bad_path_count = 0;
for ( ;; )
{
self waittill( "bad_path", badPathPos );
if ( !isDefined(level.debug_badpath) || !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 < 10 )
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" );
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;
// SCRIPTER_MOD
// MikeD( 3/23/2007 ): No more level.player
// self.is_the_player = self == level.player;
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.ai_number] = self;
self waittill( "death" );
waittillframeend;
level.ai_array[level.ai_number] = undefined;
}
player_score_think()
{
if( self.team == "allies" )
{
return;
}
self waittill( "death", attacker );
// SCRIPTER_MOD
// MikeD( 3/23/2007 ): No more level.player
// if( ( IsDefined( attacker ) ) &&( attacker == level.player ) )
// level.player thread updatePlayerScore( 1 + RandomInt( 3 ) );
if( IsDefined( attacker ) && IsPlayer( attacker ) )
{
attacker thread updatePlayerScore( 1 + RandomInt( 3 ) );
}
}
updatePlayerScore( amount )
{
if( amount == 0 )
{
return;
}
self notify( "update_xp" );
self endon( "update_xp" );
self.rankUpdateTotal += amount;
self.hud_rankscroreupdate.label = &"SCRIPT_PLUS";
self.hud_rankscroreupdate Setvalue( self.rankUpdateTotal );
self.hud_rankscroreupdate.alpha = 1;
self.hud_rankscroreupdate thread fontPulse( self );
wait( 1 );
self.hud_rankscroreupdate FadeOverTime( 0.75 );
self.hud_rankscroreupdate.alpha = 0;
self.rankUpdateTotal = 0;
}
xp_init()
{
self.rankUpdateTotal = 0;
self.hud_rankscroreupdate = NewHudElem( self );
self.hud_rankscroreupdate.horzAlign = "center";
self.hud_rankscroreupdate.vertAlign = "middle";
self.hud_rankscroreupdate.alignX = "center";
self.hud_rankscroreupdate.alignY = "middle";
self.hud_rankscroreupdate.x = 0;
self.hud_rankscroreupdate.y = -60;
self.hud_rankscroreupdate.font = "default";
self.hud_rankscroreupdate.fontscale = 2;
self.hud_rankscroreupdate.archived = false;
self.hud_rankscroreupdate.color = ( 1, 1, 1 );
self.hud_rankscroreupdate fontPulseInit();
}
fontPulseInit()
{
self.baseFontScale = self.fontScale;
self.maxFontScale = self.fontScale * 2;
self.inFrames = 3;
self.outFrames = 5;
}
fontPulse( player )
{
self notify( "fontPulse" );
self endon( "fontPulse" );
scaleRange = self.maxFontScale - self.baseFontScale;
while( self.fontScale < self.maxFontScale )
{
self.fontScale = min( self.maxFontScale, self.fontScale +( scaleRange / self.inFrames ) );
wait( 0.05 );
}
while( self.fontScale > self.baseFontScale )
{
self.fontScale = max( self.baseFontScale, self.fontScale -( scaleRange / self.outFrames ) );
wait( 0.05 );
}
}
#using_animtree( "generic_human" );
spawner_dronespawn( spawner )
{
assert( isdefined( level.dronestruct[ spawner.classname ] ) );
struct = level.dronestruct[ spawner.classname ];
drone = spawn( "script_model", spawner.origin );
drone.angles = spawner.angles;
drone setmodel( struct.model );
// drone hide();
drone UseAnimTree( #animtree );
drone makefakeai();
attachedmodels = struct.attachedmodels;
attachedtags = struct.attachedtags;
for ( i = 0;i < attachedmodels.size;i++ )
drone attach( attachedmodels[ i ], attachedtags[ i ] );
if ( isdefined( spawner.script_startingposition ) )
drone.script_startingposition = spawner.script_startingposition;
if ( isdefined( spawner.script_noteworthy ) )
drone.script_noteworthy = spawner.script_noteworthy;
if ( isdefined( spawner.script_deleteai ) )
drone.script_deleteai = spawner.script_deleteai;
if ( isdefined( spawner.script_linkto ) )
drone.script_linkto = spawner.script_linkto;
if ( isdefined( spawner.script_moveoverride ) )
drone.script_moveoverride = spawner.script_moveoverride;
// for later use to makerealai
if ( issubstr( spawner.classname, "ally" ) )
drone.team = "allies";
else if ( issubstr( spawner.classname, "enemy" ) )
drone.team = "axis";
else
drone.team = "neutral";
if ( isdefined( spawner.target ) )
drone.target = spawner.target;
drone.spawner = spawner;
assert( isdefined( drone ) );
if ( isdefined( spawner.script_noteworthy ) && spawner.script_noteworthy == "drone_delete_on_unload" )
drone.drone_delete_on_unload = true;
else
drone.drone_delete_on_unload = false;
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)");
}
drone.spawner.origin = orgorg;
drone.spawner.angles = organg;
drone Delete();
return guy;
}
hiding_door_spawner()
{
// place a hiding_door_guy prefab and then place a spawner next to it with script_noteworthy "hiding_door_spawner".
// Spawn the guy however you like (trigger or script)
// Target the spawner to a trigger, this trigger will make the guy open the door.
// Alternatively put a script_flag_wait on the spawner. The guy will wait for the flag to be set before opening the door.
// If you put neither, a trigger_radius will be spawned, using the radius of the spawner if a radius is set
door_orgs = getentarray( "hiding_door_guy_org", "targetname" );
assertex( door_orgs.size, "Hiding door guy with export " + self.export + " couldn't find a hiding_door_org!" );
door_org = getclosest( self.origin, door_orgs );
assertex( distance( door_org.origin, self.origin ) < 256, "Hiding door guy with export " + self.export + " was not placed within 256 units of a hiding_door_org" );
door_org.targetname = undefined; // so future searches won't grab this one
door_model = getent( door_org.target, "targetname" );
door_clip = getent( door_model.target, "targetname" );
assert( isdefined( door_model.target ) );
pushPlayerClip = undefined;
if ( isdefined( door_clip.target ) )
pushPlayerClip = getent( door_clip.target, "targetname" );
if ( isdefined( pushPlayerClip ) )
door_org thread hiding_door_guy_pushplayer( pushPlayerClip );
door_model delete(); // we spawn our own door, the one in the prefab is just to aid placement
door = spawn_anim_model( "hiding_door" );
door_org thread anim_first_frame_solo( door, "fire_3" );
if( isdefined( door_clip ) )
{
door_clip linkto( door, "door_hinge_jnt" );
door_clip disconnectPaths();
}
trigger = undefined;
if ( isdefined( self.target ) )
{
trigger = getent( self.target, "targetname" );
if ( !issubstr( trigger.classname, "trigger" ) )
trigger = undefined;
}
if ( !isdefined( self.script_flag_wait ) && !isdefined( trigger ) )
{
radius = 200;
if ( isdefined( self.radius ) )
radius = self.radius;
// no trigger mechanism specified, so add a radius trigger
trigger = spawn( "trigger_radius", door_org.origin, 0, radius, 48 );
}
self add_spawn_function( ::hiding_door_guy, door_org, trigger, door, door_clip );
self waittill( "spawned" );
}
hiding_door_guy( door_org, trigger, door, door_clip )
{
starts_open = hiding_door_starts_open( door_org );
self.animname = "hiding_door_guy";
self endon( "death" );
self.grenadeammo = 2;
self set_deathanim( "death_2" );
self.allowdeath = true;
self.health = 50000; // buffer health, he "dies" in one hit
guy_and_door = [];
guy_and_door[ guy_and_door.size ] = door;
guy_and_door[ guy_and_door.size ] = self;
thread hiding_door_guy_cleanup( door_org, self, door, door_clip );
thread hiding_door_death( door, door_org, self, door_clip );
if ( starts_open )
{
// wait for trigger before closing the door
door_org thread anim_loop( guy_and_door, "idle" );
}
else
{
door_org thread anim_first_frame( guy_and_door, "fire_3" );
}
if ( isdefined( trigger ) )
{
trigger waittill( "trigger" );
}
else
{
flag_wait( self.script_flag_wait );
}
if ( starts_open )
{
door_org notify( "stop_loop" );
door_org anim_single( guy_and_door, "close" );
}
for ( ;; )
{
scene = "fire_3";
if ( randomint( 100 ) < 25 * self.grenadeammo )
{
self.grenadeammo--;
scene = "grenade"; // once the grenade throw has the notetrack we'll change this
}
door_org thread anim_single( guy_and_door, scene );
// delay the settime by a frame or it wont work
delaythread( 0.05, ::anim_set_time, guy_and_door, scene, 0.4 );
door_org waittill( scene );
door_org thread anim_loop( guy_and_door, "idle" );
wait( randomfloat( 0.25, 1.5 ) );
door_org notify( "stop_loop" );
}
}
hiding_door_guy_cleanup( door_org, guy, door, door_clip )
{
// if the guy gets deleted before the sequence happens this thread will catch that and clean up any problems that could arise
guy waittill( "death" );
// stop the looping animations because the guy is removed now
door_org notify( "stop_loop" );
thread hiding_door_death_door_connections( door_clip );
door_org notify( "push_player" );
door_org thread anim_single_solo( door, "death_2" );
}
hiding_door_guy_pushplayer( pushPlayerClip )
{
self waittill( "push_player" );
pushPlayerClip moveto( self.origin, 1.5 );
wait 3.0;
pushPlayerClip delete();
}
hiding_door_guy_grenade_throw( guy )
{
// called from a notetrack
startOrigin = guy getTagOrigin( "J_Wrist_RI" );
player = get_closest_player( guy.origin );
strength = ( distance( player.origin, guy.origin ) * 2.0 );
if ( strength < 300 )
strength = 300;
if ( strength > 1000 )
strength = 1000;
vector = vectorNormalize( player.origin - guy.origin );
velocity = vectorScale( vector, strength );
guy magicGrenadeManual( startOrigin, velocity, randomfloatrange( 3.0, 5.0 ) );
}
hiding_door_death( door, door_org, guy, door_clip )
{
guy waittill( "damage" );
if ( !isalive( guy ) )
return;
guys = [];
guys[ guys.size ] = door;
guys[ guys.size ] = guy;
thread hiding_door_death_door_connections( door_clip );
door_org notify( "push_player" );
door_org thread anim_single( guys, "death_2" );
wait( 0.5 );
if ( isalive( guy ) )
{
//guy.a.nodeath = true;
guy dodamage( guy.health + 150, (0,0,0) );
}
}
hiding_door_death_door_connections( door_clip )
{
if( !isdefined( door_clip ) )
return;
door_clip connectpaths();
wait 2;
door_clip disconnectpaths();
}
hiding_door_starts_open( door_org )
{
return isdefined( door_org.script_noteworthy );
}