cod4-sdk/raw/maps/_spawner.gsc
2008-01-19 00:00:00 +00:00

4906 lines
121 KiB
Text

#include maps\_utility;
#include common_scripts\utility;
#include maps\_anim;
// .script_delete a group of guys, only one of which spawns
// .script_playerseek spawn and run to the player
// .script_patroller follow your targeted patrol
// .script_delayed_playerseek spawn and run to the player with decreasing goal radius
// .script_followmin
// .script_followmax
// .script_radius
// .script_friendname
// .script_startinghealth
// .script_accuracy
// .script_grenades
// .script_sightrange
// .script_ignoreme
main()
{
precachemodel( "grenade_bag" );
// precachemodel( "com_trashbag" );
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// connect auto AI spawners
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// create default threatbiasgroups;
createthreatbiasgroup( "allies" );
createthreatbiasgroup( "axis" );
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 );
#/
}
// 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
guy = spawner spawn_ai();
if ( spawn_failed( guy ) )
spawner notify( "spawn_failed" );
if ( isdefined( self.Wait ) && ( self.Wait > 0 ) )
wait( self.Wait );
}
}
trigger_spawner( trigger )
{
assertEx( isdefined( trigger.target ), "Triggers with flag TRIGGER_SPAWN at " + trigger.origin + " must target at least one spawner." );
trigger endon( "death" );
trigger waittill( "trigger" );
spawners = getentarray( trigger.target, "targetname" );
array_thread( spawners, ::trigger_spawner_spawns_guys );
}
trigger_spawner_spawns_guys()
{
self endon( "death" );
self script_delay();
if ( isdefined( self.script_drone ) )
{
spawned = dronespawn( self );
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();
}
flood_spawner_scripted( spawners )
{
assertEX( isDefined( spawners ) && spawners.size, "Script tried to flood spawn without any spawners" );
array_thread( spawners, ::flood_spawner_init );
array_thread( spawners, ::flood_spawner_think );
}
reincrement_count_if_deleted( spawner )
{
spawner endon( "death" );
self waittill( "death" );
if ( !isDefined( self ) )
spawner.count++ ;
}
delete_start( startnum )
{
for ( p = 0;p < 2;p++ )
{
switch( p )
{
case 0:
aitype = "axis";
break;
default:
assert( p == 1 );
aitype = "allies";
break;
}
ai = getentarray( aitype, "team" );
for ( i = 0;i < ai.size;i++ )
{
if ( isdefined( ai[ i ].script_start ) )
if ( ai[ i ].script_start == startnum )
ai[ i ] thread delete_me();
}
}
}
kill_trigger( trigger )
{
if ( !isdefined( trigger ) )
return;
if ( ( isdefined( trigger.targetname ) ) && ( trigger.targetname != "flood_spawner" ) )
return;
// temporary
if ( level.script == "sniperescape" )
return;
trigger delete();
}
random_killspawner( trigger )
{
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" );
println( "killing killspawner: " + killspawner );
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 ] );
}
*/
}
// spawn maximum 16 grenades per team
spawn_grenade( origin, team )
{
// delete oldest grenade
if ( !isdefined( level.grenade_cache ) || !isdefined( level.grenade_cache[ team ] ) )
{
level.grenade_cache_index[ team ] = 0;
level.grenade_cache[ team ] = [];
}
index = level.grenade_cache_index[ team ];
grenade = level.grenade_cache[ team ][ index ];
if ( isdefined( grenade ) )
grenade delete();
grenade = spawn( "weapon_fraggrenade", origin );
level.grenade_cache[ team ][ index ] = grenade;
level.grenade_cache_index[ team ] = ( index + 1 ) % 16;
return grenade;
}
waittillDeathOrPainDeath()
{
self endon( "death" );
self waittill( "pain_death" );// pain that ends in death
}
drop_gear()
{
team = self.team;
waittillDeathOrPainDeath();
if ( !isdefined( self ) )
return;
if ( level.tire_explosion )
{
org = self.origin;
eye = self geteye();
// try to fix the delete ai during think error
waittillframeend;
for ( i = 0; i < 15; i++ )
{
thread random_tire( org, eye );
}
if ( isdefined( self ) )
{
//self hide();
self animscripts\shared::DropAllAIWeapons();
self delete();
}
return;
}
self.ignoreForFixedNodeSafeCheck = true;
if ( self.grenadeAmmo <= 0 )
return;
level.nextGrenadeDrop -- ;
if ( level.nextGrenadeDrop > 0 )
return;
level.nextGrenadeDrop = 2 + randomint( 2 );
max = 25;
min = 12;
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 )
{
if( level.cheattirecount > 90 )
return;
level.cheattirecount ++;
model = spawn( "script_model", (0,0,0) );
model.angles = ( 0, randomint( 360 ), 0 );
dif = randomfloat( 1 );
model.origin = start * dif + end * ( 1 - dif );
model setmodel( "com_junktire" );
vel = randomvector( 15000 );
vel = ( vel[ 0 ], vel[ 1 ], abs( vel[ 2 ] ) );
model physicslaunch( model.origin, vel );
wait ( randomintrange ( 8, 12 ) );
level.cheattirecount --;
model delete();
}
spawn_grenade_bag( org, angles, team )
{
grenade = spawn_grenade( org, team );
grenade setmodel( "grenade_bag" );
grenade.count = 3;
// wait( 0.2 );
// if ( isdefined( grenade ) )
grenade.angles = angles;
return grenade;
}
// 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();
}
// This is used to see if the classname for this character already has models stored for it
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 )
{
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 ) )
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;
if ( isdefined( level.spawnerCallbackThread ) ) // this looks like pre-spawnfunc functionality, should be depricated
self thread [[ level.spawnerCallbackThread ]]( spawn );
if ( isdefined( self.script_delete ) )
{
for ( i = 0;i < level._ai_delete[ self.script_delete ].size;i++ )
{
if ( level._ai_delete[ self.script_delete ][ i ] != self )
level._ai_delete[ self.script_delete ][ i ] delete();
}
}
spawn.spawn_funcs = self.spawn_functions;
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();
self thread death_achievements();
// 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();
}
if ( isdefined( self.script_forceColor ) )
{
// send all forcecolor through a centralized function
set_force_color( self.script_forceColor );
}
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" )
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();
portableMG42guy = issubstr( self.classname, "mgportable" ) || issubstr( self.classname, "30cal" );
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;
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();
#/
self thread maps\_gameskill::auto_adjust_enemy_death_detection();
}
// 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" && !portableMG42guy )
self.health = 150;
}
// player score stuff
// self thread player_score_think();
// Gives AI grenades
if ( isdefined( self.script_grenades ) )
{
self.grenadeAmmo = self.script_grenades;
}
else
self.grenadeAmmo = 3;
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;
}
// The AI will spawn and attack the player
if ( isdefined( self.script_playerseek ) )
{
self setgoalentity( level.player );
return;
}
// The AI will spawn and follow a patrol
if ( isdefined( self.script_patroller ) )
{
self thread maps\_patrol::patrol();
return;
}
// The AI will spawn and use his .radius as a goalradius, and his goalradius will get smaller over time.
if ( isdefined( self.script_delayed_playerseek ) )
{
if ( !isdefined( self.script_radius ) )
self.goalradius = 800;
self setgoalentity( level.player );
level thread delayed_player_seek_think( self );
return;
}
if ( portableMG42guy )
{
thread maps\_mgturret::portable_mg_behavior();
return;
}
if ( isdefined( self.used_an_mg42 ) )// This AI was called upon to use an MG42 so he's not going to run to his node.
return;
if ( override )
{
set_goalradius_based_on_settings();
self setgoalpos( self.origin );
return;
}
assertEx( self.goalradius == 4, "Changed the goalradius on guy with export " + self.export + " without waiting for spawn_failed. Note that this change will NOT show up by putting a breakpoint on the actors goalradius field because breakpoints don't properly handle the first frame an actor exists." );
set_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.pushable = 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.dropweapon = 1;
self.goalradius = level.default_goalradius;
self.goalheight = level.default_goalheight;
self.ignoresuppression = 0;
self pushplayer( false );
if ( isdefined( self.magic_bullet_shield ) && self.magic_bullet_shield )
{
stop_magic_bullet_shield();
}
self disable_replace_on_death();
self.maxsightdistsqrd = 8192 * 8192;
self.script_forceGrenade = 0;
self.walkdist = 16;
self unmake_hero();
self.pushable = true;
animscripts\init::set_anim_playback_rate();
// allies use fixednode by default
self.fixednode = self.team == "allies";
// 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" );
if ( isDefined( self.script_forcegoal ) )
return;
self.goalradius = level.default_goalradius;
}
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_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;
}
// thread maps\_mg_penetration::gunner_think( turret );
self useturret( turret );// dude should be near the mg42
// turret setmode( "auto_ai" );// auto, auto_ai, manual
// turret settargetentity( level.player );
// turret setmode( "manual" );// auto, auto_ai, manual
if ( ( isdefined( turret.target ) ) && ( turret.target != turret.targetname ) )
{
ents = getentarray( turret.target, "targetname" );
targets = [];
for ( i = 0; i < ents.size;i++ )
{
if ( ents[ i ].classname == "script_origin" )
targets[ targets.size ] = ents[ i ];
}
if ( isdefined( turret.script_autotarget ) )
{
turret thread autoTarget( targets );
}
else
if ( isdefined( turret.script_manualtarget ) )
{
turret setmode( "manual_ai" );
turret thread manualTarget( targets );
}
else
if ( targets.size > 0 )
{
if ( targets.size == 1 )
{
turret.manual_target = targets[ 0 ];
turret settargetentity( targets[ 0 ] );
// turret setmode( "manual_ai" );// auto, auto_ai, manual
self thread maps\_mgturret::manual_think( turret );
// if ( isdefined( self.script_mg42auto ) )
// println( "AI at origin ", self.origin, " has script_mg42auto" );
}
else
{
turret thread maps\_mgturret::mg42_suppressionFire( targets );
}
}
}
self thread maps\_mgturret::mg42_firing( turret );
turret notify( "startfiring" );
}
fallback_spawner_think( num, node )
{
self endon( "death" );
level.current_fallbackers[ num ] += self.count;
firstspawn = true;
while ( self.count > 0 )
{
self waittill( "spawned", spawn );
if ( firstspawn )
{
if ( getdvar( "fallback" ) == "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.current_fallbackers[ num ] -- ;
continue;
}
spawn thread fallback_ai_think( num, node, "is spawner" );
}
// level notify( ( "fallbacker_died" + num ) );
}
fallback_ai_think_death( ai, num )
{
ai waittill( "death" );
level.current_fallbackers[ num ] -- ;
level notify( ( "fallbacker_died" + num ) );
}
fallback_ai_think( num, node, spawner )
{
if ( ( !isdefined( self.fallback ) ) || ( !isdefined( self.fallback[ num ] ) ) )
self.fallback[ num ] = true;
else
return;
self.script_fallback = num;
if ( !isdefined( spawner ) )
level.current_fallbackers[ num ]++ ;
if ( ( isdefined( node ) ) && ( level.fallback_initiated[ num ] ) )
{
self thread fallback_ai( num, node );
/*
self notify( "stop_going_to_node" );
self setgoalnode( node );
if ( isdefined( node.radius ) )
self.goalradius = node.radius;
*/
}
level thread fallback_ai_think_death( self, num );
}
fallback_death( ai, num )
{
ai waittill( "death" );
level notify( ( "fallback_reached_goal" + num ) );
// ai notify( "fallback_notify" );
}
fallback_goal()
{
self waittill( "goal" );
self.ignoresuppression = false;
self notify( "fallback_notify" );
self notify( "stop_coverprint" );
}
fallback_ai( num, node )
{
self notify( "stop_going_to_node" );
self stopuseturret();
self.ignoresuppression = true;
self setgoalnode( node );
if ( node.radius != 0 )
self.goalradius = node.radius;
self endon( "death" );
level thread fallback_death( self, num );
self thread fallback_goal();
if ( getdvar( "fallback" ) == "1" )
self thread coverprint( node.origin );
self waittill( "fallback_notify" );
level notify( ( "fallback_reached_goal" + num ) );
}
coverprint( org )
{
self endon( "fallback_notify" );
self endon( "stop_coverprint" );
while ( 1 )
{
line( self.origin + ( 0, 0, 35 ), org, ( 0.2, 0.5, 0.8 ), 0.5 );
print3d( ( self.origin + ( 0, 0, 70 ) ), "Falling Back", ( 0.98, 0.4, 0.26 ), 0.85 );
maps\_spawner::waitframe();
}
}
newfallback_overmind( num, group )
{
fallback_nodes = undefined;
nodes = getallnodes();
for ( i = 0;i < nodes.size;i++ )
{
if ( ( isdefined( nodes[ i ].script_fallback ) ) && ( nodes[ i ].script_fallback == num ) )
fallback_nodes = maps\_utility::add_to_array( fallback_nodes, nodes[ i ] );
}
if ( !isdefined( fallback_nodes ) )
return;
level.current_fallbackers[ num ] = 0;
level.spawner_fallbackers[ num ] = 0;
level.fallback_initiated[ num ] = false;
spawners = getspawnerarray();
for ( i = 0;i < spawners.size;i++ )
{
if ( ( isdefined( spawners[ i ].script_fallback ) ) && ( spawners[ i ].script_fallback == num ) )
{
if ( spawners[ i ].count > 0 )
{
spawners[ i ] thread fallback_spawner_think( num, fallback_nodes[ randomint( fallback_nodes.size ) ] );
level.spawner_fallbackers[ num ]++ ;
}
}
}
ai = getaiarray();
for ( i = 0;i < ai.size;i++ )
{
if ( ( isdefined( ai[ i ].script_fallback ) ) && ( ai[ i ].script_fallback == num ) )
ai[ i ] thread fallback_ai_think( num );
}
if ( ( !level.current_fallbackers[ num ] ) && ( !level.spawner_fallbackers[ num ] ) )
return;
spawners = undefined;
ai = undefined;
thread fallback_wait( num, group );
level waittill( ( "fallbacker_trigger" + num ) );
if ( getdvar( "fallback" ) == "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;
first_half = fallback_ai.size * 0.4;
first_half = int( first_half );
level notify( "fallback initiated " + num );
fallback_text( fallback_ai, 0, first_half );
for ( i = 0;i < first_half;i++ )
fallback_ai[ i ] thread fallback_ai( num, fallback_nodes[ randomint( fallback_nodes.size ) ] );
for ( i = 0;i < first_half;i++ )
level waittill( ( "fallback_reached_goal" + num ) );
fallback_text( fallback_ai, first_half, fallback_ai.size );
for ( i = first_half;i < fallback_ai.size;i++ )
{
if ( isalive( fallback_ai[ i ] ) )
fallback_ai[ i ] thread fallback_ai( num, fallback_nodes[ randomint( fallback_nodes.size ) ] );
}
}
fallback_text( fallbackers, start, end )
{
if ( gettime() <= level._nextcoverprint )
return;
for ( i = start;i < end;i++ )
{
if ( !isalive( fallbackers[ i ] ) )
continue;
level._nextcoverprint = gettime() + 2500 + randomint( 2000 );
total = fallbackers.size;
temp = int( total * 0.4 );
if ( randomint( 100 ) > 50 )
{
if ( total - temp > 1 )
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_1";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_2";
else
msg = "dawnville_defensive_german_3";
}
else
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_4";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_5";
else
msg = "dawnville_defensive_german_1";
}
}
else
{
if ( temp > 1 )
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_2";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_3";
else
msg = "dawnville_defensive_german_4";
}
else
{
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_5";
else
if ( randomint( 100 ) > 66 )
msg = "dawnville_defensive_german_1";
else
msg = "dawnville_defensive_german_2";
}
}
fallbackers[ i ] animscripts\face::SaySpecificDialogue( undefined, msg, 1.0 );
return;
}
}
fallback_wait( num, group )
{
level endon( ( "fallbacker_trigger" + num ) );
if ( getdvar( "fallback" ) == "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 );
}
ai = undefined;
// if ( !total_fallbackers )
// return;
max_fallbackers = level.current_fallbackers[ num ];
deadfallbackers = 0;
while ( level.current_fallbackers[ num ] > max_fallbackers * 0.5 )
{
if ( getdvar( "fallback" ) == "1" )
println( "^cwaiting for " + level.current_fallbackers[ num ] + " to be less than " + ( max_fallbackers * 0.5 ) );
level waittill( ( "fallbacker_died" + num ) );
deadfallbackers++ ;
}
println( deadfallbackers, " fallbackers have died, time to retreat" );
level notify( ( "fallbacker_trigger" + num ) );
}
fallback_think( trigger )// for fallback trigger
{
if ( ( !isdefined( level.fallback ) ) || ( !isdefined( level.fallback[ trigger.script_fallback ] ) ) )
level thread newfallback_overmind( trigger.script_fallback, trigger.script_fallback_group );
trigger waittill( "trigger" );
level notify( ( "fallbacker_trigger" + trigger.script_fallback ) );
// level notify( ( "fallback" + trigger.script_fallback ) );
// Maybe throw in a thing to kill triggers with the same fallback? God my hands are cold.
kill_trigger( trigger );
}
arrive( node )
{
self waittill( "goal" );
if ( node.radius != 0 )
self.goalradius = node.radius;
else
self.goalradius = level.default_goalradius;
}
fallback_coverprint()
{
// self endon( "death" );
self endon( "fallback" );
self endon( "fallback_clear_goal" );
self endon( "fallback_clear_death" );
while ( 1 )
{
if ( isdefined( self.coverpoint ) )
line( self.origin + ( 0, 0, 35 ), self.coverpoint.origin, ( 0.2, 0.5, 0.8 ), 0.5 );
print3d( ( self.origin + ( 0, 0, 70 ) ), "Covering", ( 0.98, 0.4, 0.26 ), 0.85 );
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();
}
}
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 ] ) )
if ( targs[ i ].classname[ 7 ] != "l" )
{
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" );
if ( !spawn.size )
{
level waittill( "friendly_died" );
continue;
}
num = 0;
script_delay = isdefined( level.friendly_wave_trigger.script_delay );
while ( ( isdefined( level.friendly_wave_trigger ) ) && ( level.totalfriends < level.maxfriendlies ) )
{
if ( old_friendly_wave_trigger != level.friendly_wave_trigger )
{
script_delay = isdefined( level.friendly_wave_trigger.script_delay );
old_friendly_wave_trigger = level.friendly_wave_trigger;
assertex( isdefined( level.friendly_wave_trigger.target ), "Wave trigger must target spawner" );
spawn = getentarray( level.friendly_wave_trigger.target, "targetname" );
}
else if ( !script_delay )
num = randomint( spawn.size );
else if ( num == spawn.size )
num = 0;
spawn[ num ].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
spawned setgoalentity( level.player );
if ( script_delay )
{
if ( level.friendly_wave_trigger.script_delay == 0 )
waittillframeend;
else
wait level.friendly_wave_trigger.script_delay;
num++ ;
}
else
wait( randomfloat( 5 ) );
}
}
level waittill( "friendly_died" );
}
}
friendly_mgTurret( trigger )
{
/#
if ( !isdefined( trigger.target ) )
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 ) )
if ( other == level.player )
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;
}
if ( distance( level.player.origin, node.origin ) < 100 )
{
// println( "^a player too close" );
return false;
}
if ( isdefined( self.chainnode ) )
if ( distance( level.player.origin, self.chainnode.origin ) > 1100 )
{
// println( "^a too far from chain node" );
return false;
}
return true;
}
friendly_mg42_endtrigger( mg42, guy )
{
mg42 endon( "friendly_finished_using_mg42" );
self waittill( "trigger" );
println( "^a Told friendly to leave the MG42 now" );
// guy stopuseturret();
// badplace_cylinder( undefined, 3, level.player.origin, 150, 150, "allies" );
mg42 notify( "friendly_finished_using_mg42" );
}
friendly_mg42_stop_use()
{
if ( !isdefined( self.friendly_mg42 ) )
return;
self.friendly_mg42 notify( "friendly_finished_using_mg42" );
}
noFour()
{
self endon( "death" );
self waittill( "goal" );
self.goalradius = self.oldradius;
if ( self.goalradius < 32 )
self.goalradius = 400;
}
friendly_mg42_think( mg42, node )
{
self endon( "death" );
mg42 endon( "friendly_finished_using_mg42" );
// self endon( "death" );
level thread friendly_mg42_death_notify( self, mg42 );
// println( self.name + "^a is using an mg42" );
self.oldradius = self.goalradius;
self.goalradius = 28;
self thread noFour();
self setgoalnode( node );
self.ignoresuppression = true;
self waittill( "goal" );
self.goalradius = self.oldradius;
if ( self.goalradius < 32 )
self.goalradius = 400;
// println( "^3 my goal radius is ", self.goalradius );
self.ignoresuppression = false;
// Temporary fix waiting on new code command to see who the player is following.
// self setgoalentity( level.player );
self.goalradius = self.oldradius;
if ( distance( level.player.origin, node.origin ) < 32 )
{
mg42 notify( "friendly_finished_using_mg42" );
return;
}
self.friendly_mg42 = mg42;// For making him stop using the mg42 from another script
self thread friendly_mg42_wait_for_use( mg42 );
self thread friendly_mg42_cleanup( mg42 );
self useturret( mg42 );// dude should be near the mg42
// println( "^a Told AI to use mg42" );
if ( isdefined( mg42.target ) )
{
stoptrigger = getent( mg42.target, "targetname" );
if ( isdefined( stoptrigger ) )
stoptrigger thread friendly_mg42_endtrigger( mg42, self );
}
while ( 1 )
{
if ( distance( self.origin, node.origin ) < 32 )
self useturret( mg42 );// dude should be near the mg42
else
break;// a friendly is too far from mg42, stop using turret
if ( isdefined( self.chainnode ) )
{
if ( distance( self.origin, self.chainnode.origin ) > 1100 )
break;// friendly node is too far, stop using turret
}
wait( 1 );
}
mg42 notify( "friendly_finished_using_mg42" );
}
friendly_mg42_cleanup( mg42 )
{
self endon( "death" );
mg42 waittill( "friendly_finished_using_mg42" );
self friendly_mg42_doneUsingTurret();
}
friendly_mg42_doneUsingTurret()
{
self endon( "death" );
turret = self.friendly_mg42;
self.friendly_mg42 = undefined;
self stopuseturret();
self notify( "stopped_use_turret" );// special hook for decoytown guys - nate
self.useable = false;
self.goalradius = self.oldradius;
if ( !isdefined( turret ) )
return;
if ( !isdefined( turret.target ) )
return;
node = getnode( turret.target, "targetname" );
oldradius = self.goalradius;
self.goalradius = 8;
self setgoalnode( node );
wait( 2 );
self.goalradius = 384;
return;
self waittill( "goal" );
if ( isdefined( self.target ) )
{
node = getnode( self.target, "targetname" );
if ( isdefined( node.target ) )
node = getnode( node.target, "targetname" );
if ( isdefined( node ) )
self setgoalnode( node );
}
self.goalradius = oldradius;
}
tanksquish()
{
if ( isdefined( level.noTankSquish ) )
{
assertex( level.noTankSquish, "level.noTankSquish must be true or undefined" );
return;
}
if ( isdefined( level.levelHasVehicles ) && !level.levelHasVehicles )
return;
while ( 1 )
{
self waittill( "damage", amt, who, force, b, c, d, e );
if ( !isdefined( self ) )
{
// deleted?
return;
}
if ( isalive( self ) )
continue;
if ( !isalive( who ) )
return;
if ( !isdefined( who.vehicletype ) )
return;
force = vectorscale( force, 50000 );
force = ( force[ 0 ], force[ 1 ], abs( force[ 2 ] ) );
self startRagdoll();
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;
if ( self isTouching( level.player ) )
playerTriggered = true;
else
{
if ( !isalive( other ) )
continue;
if ( other == level.player )
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;
}
dist = distance( level.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;
}
if ( distance( level.player.origin, org ) < 700 )
{
return true;
}
return bulletTracePassed( level.player geteye(), ai geteye(), false, undefined );
}
waittillRestartOrDistance( dist )
{
self endon( "flood_begin" );
dist = dist * 0.75;// require the player to get a bit closer to force restart the spawner
while ( distance( level.player.origin, self.origin ) > dist )
wait( 1 );
}
flood_and_secure_spawn( spawner )
{
self thread flood_and_secure_spawn_goal();
self waittill( "death", other );
playerKill = isalive( other ) && other == level.player;
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 )
while ( level.player istouching( self ) )
wait( 0.05 );
if ( !objectiveIsAllowed() )
{
wait( 0.05 );
continue;
}
if ( isdefined( level.lastFriendlyTrigger ) && level.lastFriendlyTrigger == self )
continue;
level notify( "new_friendly_trigger" );
level.lastFriendlyTrigger = self;
array_thread( triggers, ::friendlyTrigger );
wait( 0.5 );
}
}
objectiveIsAllowed()
{
active = true;
if ( isdefined( self.script_objective_active ) )
{
active = false;
// objective must be active for this trigger to hit
for ( i = 0;i < level.active_objective.size;i++ )
{
if ( !issubstr( self.script_objective_active, level.active_objective[ i ] ) )
continue;
active = true;
break;
}
if ( !active )
return false;
}
if ( !isdefined( self.script_objective_inactive ) )
return( active );
// trigger only hits if this objective is inactive
inactive = 0;
for ( i = 0;i < level.inactive_objective.size;i++ )
{
if ( !issubstr( self.script_objective_inactive, level.inactive_objective[ i ] ) )
continue;
inactive++ ;
}
tokens = strtok( self.script_objective_inactive, " " );
return( inactive == tokens.size );
}
friendlyTrigger( node )
{
level endon( "new_friendly_trigger" );
self waittill( "trigger" );
node = getnode( self.target, "targetname" );
rejoin = !isdefined( self.script_baseOfFire ) || self.script_baseOfFire == 0;
setNewPlayerChain( node, rejoin );
}
waittillDeathOrEmpty()
{
self endon( "death" );
num = self.script_deathChain;
while ( self.count )
{
self waittill( "spawned", spawn );
spawn thread deathChainAINotify( num );
}
}
deathChainAINotify( num )
{
level.deathSpawner[ num ]++ ;
self waittill( "death" );
level.deathSpawner[ num ] -- ;
level notify( "spawner_expired" + num );
}
deathChainSpawnerLogic()
{
num = self.script_deathChain;
level.deathSpawner[ num ]++ ;
/#
level.deathSpawnerEnts[ num ][ level.deathSpawnerEnts[ num ].size ] = self;
#/
org = self.origin;
self waittillDeathOrEmpty();
/#
newDeathSpawners = [];
if ( isdefined( self ) )
{
for ( i = 0;i < level.deathSpawnerEnts[ num ].size;i++ )
{
if ( !isdefined( level.deathSpawnerEnts[ num ][ i ] ) )
continue;
if ( self == level.deathSpawnerEnts[ num ][ i ] )
continue;
newDeathSpawners[ newDeathSpawners.size ] = level.deathSpawnerEnts[ num ][ i ];
}
}
else
{
for ( i = 0;i < level.deathSpawnerEnts[ num ].size;i++ )
{
if ( !isdefined( level.deathSpawnerEnts[ num ][ i ] ) )
continue;
newDeathSpawners[ newDeathSpawners.size ] = level.deathSpawnerEnts[ num ][ i ];
}
}
level.deathSpawnerEnts[ num ] = newDeathSpawners;
#/
level notify( "spawner dot" + org );
level.deathSpawner[ num ] -- ;
level notify( "spawner_expired" + num );
}
friendlychain_onDeath()
{
/*
Enables a friendly chain when certain AI are cleared
trigger targetname friendly_chain_on_death
trigger is script_deathchain grouped with spawners
When the spawners have depleted and all their ai are dead:
the triggers become active.
When triggered they set the friendly chain to the chain they target
The triggers deactivate when a "friendlychain" targetnamed trigger is hit.
*/
triggers = getentarray( "friendly_chain_on_death", "targetname" );
spawners = getspawnerarray();
level.deathSpawner = [];
/#
// for debugging deathspawners
level.deathSpawnerEnts = [];
#/
for ( i = 0;i < spawners.size;i++ )
{
if ( !isdefined( spawners[ i ].script_deathchain ) )
continue;
num = spawners[ i ].script_deathchain;
if ( !isdefined( level.deathSpawner[ num ] ) )
{
level.deathSpawner[ num ] = 0;
/#
level.deathSpawnerEnts[ num ] = [];
#/
}
spawners[ i ] thread deathChainSpawnerLogic();
// level.deathSpawner[ num ]++ ;
}
for ( i = 0;i < triggers.size;i++ )
{
if ( !isdefined( triggers[ i ].script_deathchain ) )
{
println( "trigger at origin " + triggers[ i ] getorigin() + " has no script_deathchain" );
return;
}
triggers[ i ] thread friendlyChain_onDeathThink();
}
}
friendlyChain_onDeathThink()
{
while ( level.deathSpawner[ self.script_deathChain ] > 0 )
level waittill( "spawner_expired" + self.script_deathChain );
level endon( "start_chain" );
node = getnode( self.target, "targetname" );
for ( ;; )
{
self waittill( "trigger" );
setNewPlayerChain( node, true );
iprintlnbold( "Area secured, move up!" );
wait( 5 );// debounce
}
}
setNewPlayerChain( node, rejoin )
{
level.player set_friendly_chain_wrapper( node );
level notify( "new_escort_trigger" );// stops escorting guy from getting back on escort chain
level notify( "new_escort_debug" );
level notify( "start_chain", rejoin );// get the SMG guy back on the friendly chain
}
friendlyChains()
{
level.friendlySpawnOrg = [];
level.friendlySpawnTrigger = [];
array_thread( getentarray( "friendlychain", "targetname" ), ::friendlychain );
}
unsetFriendlySpawn()
{
newOrg = [];
newTrig = [];
for ( i = 0;i < level.friendlySpawnOrg.size;i++ )
{
newOrg[ newOrg.size ] = level.friendlySpawnOrg[ i ];
newTrig[ newTrig.size ] = level.friendlySpawnTrigger[ i ];
}
level.friendlySpawnOrg = newOrg;
level.friendlySpawnTrigger = newTrig;
if ( activeFriendlySpawn() )
return;
// If we've stepped back through all the spawners then turn off spawning
flag_Clear( "spawning_friendlies" );
}
getFriendlySpawnStart()
{
assert( level.friendlySpawnOrg.size > 0 );
return( level.friendlySpawnOrg[ level.friendlySpawnOrg.size - 1 ] );
}
activeFriendlySpawn()
{
return level.friendlySpawnOrg.size > 0;
}
getFriendlySpawnTrigger()
{
assert( level.friendlySpawnTrigger.size > 0 );
return( level.friendlySpawnTrigger[ level.friendlySpawnTrigger.size - 1 ] );
}
setFriendlySpawn( org, trigger )
{
level.friendlySpawnOrg[ level.friendlySpawnOrg.size ] = org.origin;
level.friendlySpawnTrigger[ level.friendlySpawnTrigger.size ] = trigger;
flag_set( "spawning_friendlies" );
}
delayedPlayerGoal()
{
self endon( "death" );
self endon( "leaveSquad" );
wait( 0.5 );
self setgoalentity( level.player );
}
spawnWaveStopTrigger( startTrigger )
{
self notify( "stopTrigger" );
self endon( "stopTrigger" );
self waittill( "trigger" );
if ( getFriendlySpawnTrigger() != startTrigger )
return;
unsetFriendlySpawn();
}
friendlySpawnWave_triggerThink( startTrigger )
{
org = getent( self.target, "targetname" );
// thread linedraw();
for ( ;; )
{
self waittill( "trigger" );
startTrigger notify( "friendly_wave_start", org );
if ( !isdefined( org.target ) )
continue;
}
}
goalVolumes()
{
volumes = getentarray( "info_volume", "classname" );
level.deathchain_goalVolume = [];
level.goalVolumes = [];
for ( i = 0; i < volumes.size; i++ )
{
volume = volumes[ i ];
if ( isdefined( volume.script_deathChain ) )
{
level.deathchain_goalVolume[ volume.script_deathChain ] = volume;
}
if ( isdefined( volume.script_goalvolume ) )
{
level.goalVolumes[ volume.script_goalVolume ] = volume;
}
}
}
debugPrint( msg, endonmsg, color )
{
// if ( !level.debug_corevillers )
if ( 1 )
return;
org = self getorigin();
height = 40 * sin( org[ 0 ] + org[ 1 ] ) - 40;
org = ( org[ 0 ], org[ 1 ], org[ 2 ] + height );
level endon( endonmsg );
self endon( "new_color" );
if ( !isdefined( color ) )
color = ( 0, 0.8, 0.6 );
num = 0;
for ( ;; )
{
num += 12;
scale = sin( num ) * 0.4;
if ( scale < 0 )
scale *= -1;
scale += 1;
print3d( org, msg, color, 1, scale );
wait( 0.05 );
}
}
aigroup_create( aigroup )
{
level._ai_group[ aigroup ] = spawnstruct();
level._ai_group[ aigroup ].aicount = 0;
level._ai_group[ aigroup ].spawnercount = 0;
level._ai_group[ aigroup ].ai = [];
level._ai_group[ aigroup ].spawners = [];
}
aigroup_spawnerthink( tracker )
{
self endon( "death" );
self.decremented = false;
tracker.spawnercount++ ;
self thread aigroup_spawnerdeath( tracker );
self thread aigroup_spawnerempty( tracker );
while ( self.count )
{
self waittill( "spawned", soldier );
if ( spawn_failed( soldier ) )
continue;
soldier thread aigroup_soldierthink( tracker );
}
waittillframeend;
if ( self.decremented )
return;
self.decremented = true;
tracker.spawnercount -- ;
}
aigroup_spawnerdeath( tracker )
{
self waittill( "death" );
if ( self.decremented )
return;
tracker.spawnercount -- ;
}
aigroup_spawnerempty( tracker )
{
self endon( "death" );
self waittill( "emptied spawner" );
waittillframeend;
if ( self.decremented )
return;
self.decremented = true;
tracker.spawnercount -- ;
}
aigroup_soldierthink( tracker )
{
tracker.aicount++ ;
tracker.ai[ tracker.ai.size ] = self;
if ( isdefined( self.script_deathflag_longdeath ) )
{
self waittillDeathOrPainDeath();
}
else
{
self waittill( "death" );
}
tracker.aicount -- ;
}
camper_trigger_think( trigger )
{
// wait( 0.05 );
tokens = strtok( trigger.script_linkto, " " );
spawners = [];
nodes = [];
for ( i = 0; i < tokens.size; i++ )
{
token = tokens[ i ];
ai = getent( token, "script_linkname" );
if ( isdefined( ai ) )
{
spawners = add_to_array( spawners, ai );
continue;
}
node = getnode( token, "script_linkname" );
if ( !isdefined( node ) )
{
println( "Warning: Trigger token number " + token + " did not exist." );
continue;
}
nodes = add_to_array( nodes, node );
}
assertEX( spawners.size, "camper_spawner without any spawners associated" );
assertEX( nodes.size, "camper_spawner without any nodes associated" );
assertEX( nodes.size >= spawners.size, "camper_spawner with less nodes than spawners" );
trigger waittill( "trigger" );
nodes = array_randomize( nodes );
for ( i = 0; i < nodes.size; i++ )
nodes[ i ].claimed = false;
j = 0;
for ( i = 0; i < spawners.size; i++ )
{
spawner = spawners[ i ];
if ( !isdefined( spawner ) )
continue;
if ( isdefined( spawner.script_spawn_here ) )
{
// these guys spawn where they're placed
continue;
}
while ( isdefined( nodes[ j ].script_noteworthy ) && nodes[ j ].script_noteworthy == "dont_spawn" )
j++ ;
spawner.origin = nodes[ j ].origin;
spawner.angles = nodes[ j ].angles;
spawner add_spawn_function( ::claim_a_node, nodes[ j ] );
j++ ;
}
array_thread( spawners, ::add_spawn_function, ::camper_guy );
array_thread( spawners, ::add_spawn_function, ::move_when_enemy_hides, nodes );
array_thread( spawners, ::spawn_ai );
}
camper_guy()
{
self.goalradius = 8;
self.fixednode = true;
}
move_when_enemy_hides( nodes )
{
self endon( "death" );
waitingForEnemyToDisappear = false;
while ( 1 )
{
// it is important that we check whether our enemy is defined before doing a cansee check on him.
if ( !isalive( self.enemy ) )
{
self waittill( "enemy" );
waitingForEnemyToDisappear = false;
continue;
}
if ( self.enemy == level.player )
{
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( level.player.origin );
wait( 1 );
}
return;
}
}
if ( waitingForEnemyToDisappear )
{
if ( self cansee( self.enemy ) )
{
wait .05;
continue;
}
waitingForEnemyToDisappear = false;
}
else
{
if ( self cansee( self.enemy ) )
{
// enemy is seen, wait until you cant see him
waitingForEnemyToDisappear = true;
}
wait .05;
continue;
}
// you cant see him, 2 / 3rds of the time move to a different node
if ( randomint( 3 ) > 0 )
{
node = find_unclaimed_node( nodes );
if ( isdefined( node ) )
{
self claim_a_node( node, self.claimed_node );
self waittill( "goal" );
}
}
}
}
claim_a_node( claimed_node, old_claimed_node )
{
self setgoalnode( claimed_node );
self.claimed_node = claimed_node;
claimed_node.claimed = true;
if ( isdefined( old_claimed_node ) )
old_claimed_node.claimed = false;
// self OrientMode( "face angle", claimed_node.angles[ 1 ] );
}
find_unclaimed_node( nodes )
{
for ( i = 0; i < nodes.size; i++ )
{
if ( nodes[ i ].claimed )
continue;
else
return nodes[ i ];
}
return undefined;
}
// flood_spawner
flood_trigger_think( trigger )
{
assertEX( isDefined( trigger.target ), "flood_spawner at " + trigger.origin + " without target" );
floodSpawners = getEntArray( trigger.target, "targetname" );
assertEX( floodSpawners.size, "flood_spawner at with target " + trigger.target + " without any targets" );
array_thread( floodSpawners, ::flood_spawner_init );
trigger waittill( "trigger" );
// reget the target array since targets may have been deletes, etc... between initialization and triggering
floodSpawners = getEntArray( trigger.target, "targetname" );
array_thread( floodSpawners, ::flood_spawner_think, trigger );
}
flood_spawner_init( spawner )
{
assertEX( ( isDefined( self.spawnflags ) && self.spawnflags & 1 ), "Spawner at origin" + self.origin + "/" + ( self getOrigin() ) + " is not a spawner!" );
}
trigger_requires_player( trigger )
{
if ( !isdefined( trigger ) )
return false;
return isDefined( trigger.script_requires_player );
}
two_stage_spawner_think( trigger )
{
trigger_target = getent( trigger.target, "targetname" );
assertEx( isdefined( trigger_target ), "Trigger with targetname two_stage_spawner that doesnt target anything." );
assertEx( issubstr( trigger_target.classname, "trigger" ), "Triggers with targetname two_stage_spawner must target a trigger" );
assertEx( isdefined( trigger_target.target ), "The second trigger of a two_stage_spawner must target at least one spawner" );
// wait until _spawner has initialized before adding spawn functions
waittillframeend;
spawners = getentarray( trigger_target.target, "targetname" );
for ( i = 0; i < spawners.size; i++ )
{
spawners[ i ].script_moveoverride = true;
spawners[ i ] add_spawn_function( ::wait_to_go, trigger_target );
}
trigger waittill( "trigger" );
spawners = getentarray( trigger_target.target, "targetname" );
array_thread( spawners, ::spawn_ai );
}
wait_to_go( trigger_target )
{
trigger_target endon( "death" );
self endon( "death" );
self.goalradius = 8;
trigger_target waittill( "trigger" );
self thread go_to_node();
}
flood_spawner_think( trigger )
{
self endon( "death" );
self notify( "stop current floodspawner" );
self endon( "stop current floodspawner" );
// pyramid spawner is a spawner that targets another spawner or spawners
// First the targetted spawners spawn, then when they die, the reinforcement spawns from
// the spawner this initial spawner
if ( is_pyramid_spawner() )
{
pyramid_spawn( trigger );
return;
}
requires_player = trigger_requires_player( trigger );
script_delay();
while ( self.count > 0 )
{
while ( requires_player && !level.player isTouching( trigger ) )
wait( 0.5 );
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;
}
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() )
wait( randomFloatRange( 5, 9 ) );
}
}
player_saw_kill( guy, attacker )
{
if ( isdefined( self.script_force_count ) )
if ( self.script_force_count )
return true;
if ( !isdefined( guy ) )
{
return false;
}
if ( isalive( attacker ) )
{
if ( attacker == level.player )
{
return true;
}
if ( distance( attacker.origin, level.player.origin ) < 200 )
{
// player was near the guy that killed the ai?
return true;
}
}
else
{
if ( isdefined( attacker ) )
{
if ( attacker.classname == "worldspawn" )
{
return false;
}
if ( distance( attacker.origin, level.player.origin ) < 200 )
{
// player was near the guy that killed the ai?
return true;
}
}
}
if ( distance( guy.origin, level.player.origin ) < 200 )
{
// player was near the guy that got killed?
return true;
}
// did the player see the guy die?
return bulletTracePassed( level.player geteye(), guy geteye(), false, undefined );
}
is_pyramid_spawner()
{
if ( !isdefined( self.target ) )
return false;
ent = getentarray( self.target, "targetname" );
if ( !ent.size )
return false;
return issubstr( ent[ 0 ].classname, "actor" );
}
pyramid_death_report( spawner )
{
spawner.spawn waittill( "death" );
self notify( "death_report" );
}
pyramid_spawn( trigger )
{
self endon( "death" );
requires_player = trigger_requires_player( trigger );
script_delay();
if ( requires_player )
{
while ( !level.player isTouching( trigger ) )
wait( 0.5 );
}
// first spawn all the guys we target. They decrement our count tho, so we spawn them in a random order in case
// our count is just 1( default )
spawners = getentarray( self.target, "targetname" );
/#
for ( i = 0; i < spawners.size; i++ )
assertEx( issubstr( spawners[ i ].classname, "actor" ), "Pyramid spawner targets non AI!" );
#/
// the spawners have to report their death to the head of the pyramid so it can kill itself when they're all gone
self.spawners = 0;
array_thread( spawners, ::pyramid_spawner_reports_death, self );
offset = randomint( spawners.size );
for ( i = 0; i < spawners.size; i++ )
{
if ( self.count <= 0 )
return;
offset++ ;
if ( offset >= spawners.size )
offset = 0;
spawner = spawners[ offset ];
// the count is local to self, not to the spawners that are targetted
spawner.count = 1;
soldier = spawner spawn_ai();
if ( spawn_failed( soldier ) )
{
// assertEx( 0, "Initial spawning from spawner at " + self.origin + " failed." );
wait( 2 );
continue;
}
self.count -- ;
spawner.spawn = soldier;
soldier thread reincrement_count_if_deleted( self );
soldier thread expand_goalradius( trigger );
thread pyramid_death_report( spawner );
}
culmulative_wait = 0.01;
while ( self.count > 0 )
{
self waittill( "death_report" );
script_wait();
wait( culmulative_wait );
culmulative_wait += 2.5;
offset = randomint( spawners.size );
for ( i = 0; i < spawners.size; i++ )
{
// cleanup in case any spawners were deleted
spawners = array_removeUndefined( spawners );
if ( !spawners.size )
{
if ( isdefined( self ) )
self delete();
return;
}
offset++ ;
if ( offset >= spawners.size )
offset = 0;
spawner = spawners[ offset ];
// find a spawner that has lost its AI
if ( isalive( spawner.spawn ) )
continue;
// spawn from self now, we're reinforcement
if ( isdefined( spawner.target ) )
{
self.target = spawner.target;
}
else
{
self.target = undefined;
}
soldier = self spawn_ai();
if ( spawn_failed( soldier ) )
{
wait( 2 );
continue;
}
assertEx( isdefined( spawner ), "Theoretically impossible." );
soldier thread reincrement_count_if_deleted( self );
soldier thread expand_goalradius( trigger );
spawner.spawn = soldier;
thread pyramid_death_report( spawner );
if ( self.count <= 0 )
return;
}
}
}
pyramid_spawner_reports_death( parent )
{
parent endon( "death" );
parent.spawners++ ;
self waittill( "death" );
parent.spawners -- ;
if ( !parent.spawners )
parent delete();
}
expand_goalradius( trigger )
{
if ( isDefined( self.script_forcegoal ) )
return;
// triggers with a script_radius of - 1 dont override the goalradius
// triggers with a script_radius of anything else set the goalradius to that size
radius = level.default_goalradius;
if ( isdefined( trigger ) )
{
if ( isdefined( trigger.script_radius ) )
{
if ( trigger.script_radius == -1 )
return;
radius = trigger.script_radius;
}
}
if ( isdefined( self.script_forcegoal ) )
return;
// expands the goalradius of the ai after they reach there initial goal.
self endon( "death" );
self waittill( "goal" );
self.goalradius = radius;
}
drop_health_timeout_thread()
{
self endon( "death" );
wait( 95 );
self notify( "timeout" );
}
drop_health_trigger_think()
{
self endon( "timeout" );
thread drop_health_timeout_thread();
self waittill( "trigger" );
change_player_health_packets( 1 );
}
traceShow( org )
{
for ( ;; )
{
line( org + ( 0, 0, 100 ), org, ( 0.2, 0.5, 0.8 ), 0.5 );
wait( 0.05 );
}
}
/*drophealth()
{
// wait until regular scripts have a change to set self.script_nohealth on the guy from script, after spawn_failed.
waittillframeend;
waittillframeend;
if ( !isalive( self ) )
return;
if ( isdefined( self.script_nohealth ) )
return;
self waittill( "death" );
if ( !isdefined( self ) )
return;
// drop health disabled once again
if ( 1 )
return;
// has enough time passed since the last health drop?
if ( gettime() < level.next_health_drop_time )
return;
// have enough guys died?
level.guys_to_die_before_next_health_drop -- ;
if ( level.guys_to_die_before_next_health_drop > 0 )
return;
level.guys_to_die_before_next_health_drop = randomintrange( 2, 5 );
level.next_health_drop_time = gettime() + 3500;// probably make this a _gameskill thing later
trace = bullettrace( self.origin + ( 0, 0, 50 ), self.origin + ( 0, 0, -220 ), true, self );
health = spawn( "script_model", self.origin + ( 0, 0, 10 ) );
health.origin = trace[ "position" ];
// health setmodel( "com_trashbag" );
trigger = spawn( "trigger_radius", self.origin + ( 0, 0, 10 ), 0, 10, 32 );
trigger.radius = 10;
trigger drop_health_trigger_think();
trigger delete();
health delete();
// health = spawn( "item_health", self.origin + ( 0, 0, 10 ) );
// health.angles = ( 0, randomint( 360 ), 0 );
/*
if ( isdefined( level._health_queue ) )
{
if ( isdefined( level._health_queue[ level._health_queue_num ] ) )
level._health_queue[ level._health_queue_num ] delete();
}
level._health_queue[ level._health_queue_num ] = health;
level._health_queue_num++ ;
if ( level._health_queue_num > level._health_queue_max )
level._health_queue_num = 0;
*/
//}
show_bad_path()
{
/#
if ( getdebugdvar( "debug_badpath" ) == "" )
setdvar( "debug_badpath", "" );
self endon( "death" );
last_bad_path_time = -5000;
bad_path_count = 0;
for ( ;; )
{
self waittill( "bad_path", badPathPos );
if ( !level.debug_badpath )
continue;
if ( gettime() - last_bad_path_time > 5000 )
{
bad_path_count = 0;
}
else
{
bad_path_count++;
}
last_bad_path_time = gettime();
if ( bad_path_count < 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;
self.is_the_player = self == level.player;
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 );
if ( ( isdefined( attacker ) ) && ( attacker == level.player ) )
level.player 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" );
strength = ( distance( level.player.origin, guy.origin ) * 2.0 );
if ( strength < 300 )
strength = 300;
if ( strength > 1000 )
strength = 1000;
vector = vectorNormalize( level.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 );
}
death_achievements()
{
self waittill( "death", attacker,type,weapon );
if( ! isdefined( self ) )
return;// deleted
achieve_enemy_killed_by_flash_banged( attacker );
thread achieve_carkilled_by_player( attacker );
thread achieve_three_of_kind( attacker, type );
thread achieve_four_of_kind( attacker, type );
}
achieve_three_of_kind( attacker, type )
{
if( self.team != "axis" )
return;
if( ! isdefined( attacker ) )
return;
if( attacker != level.player )
return;
if(type != "MOD_MELEE")
return;
if(!isdefined(level.achieve_three_of_kind))
level.achieve_three_of_kind = 1;
else
level.achieve_three_of_kind++;
if(level.achieve_three_of_kind == 3)
giveachievement_wrapper("THREE_OF_A_KIND");
level notify ("achieve_three_of_kind");
level endon ("achieve_three_of_kind");
wait 4;
level.achieve_three_of_kind = undefined;
}
achieve_four_of_kind( attacker, type )
{
if( self.team != "axis" )
return;
if( ! isdefined( attacker ) )
return;
if( attacker != level.player )
return;
if( type == "MOD_MELEE" || ! animscripts\utility::damageLocationIsAny( "head","neck", "helmet" ) )
{
level.achieve_four_of_kind = undefined;
return;
}
if(!isdefined(level.achieve_four_of_kind))
level.achieve_four_of_kind = 1;
else
level.achieve_four_of_kind++;
if(level.achieve_four_of_kind == 4)
giveachievement_wrapper("FOUR_OF_A_KIND");
level notify ("achieve_four_of_kind");
level endon ("achieve_four_of_kind");
wait 12;
level.achieve_four_of_kind = undefined;
}
achieve_enemy_killed_by_flash_banged( attacker )
{
if( self.team != "axis" )
return;
if( ! isdefined( attacker ) )
return;
if( level.flag["player_flashed"] && attacker == level.player )
giveachievement_wrapper("DAREDEVIL");
}
achieve_carkilled_by_player( attacker )
{
if( self.team != "axis" )
return;
if( ! isdefined( attacker ) )
return;
if( ! isdefined( attacker.destuctableInfo ) )
return;
if( ! isdefined( attacker.attacker ) )
return;
if( attacker.attacker != level.player )
return;
if( isdefined( level.achieve_car_killed_one ) )
{
giveachievement_wrapper( "ROADKILL" );
level.achieve_car_killed_one = undefined;
return;
}
level.achieve_car_killed_one = true;
wait .05;
return;
}