#include common_scripts\utility; #include maps\mp\_utility; #include maps\mp\gametypes\_damage; #include maps\mp\gametypes\_gamelogic; #include maps\mp\agents\_agent_utility; #include maps\mp\alien\_utility; //======================================================================= // main // This is functions is called directly from native code on game startup // The particular gametype's main() is called from native code afterward //======================================================================= main() { if( IsDefined( level.createFX_enabled ) && level.createFX_enabled ) return; setup_callbacks(); // Enable badplaces in destructibles level.badplace_cylinder_func = ::badplace_cylinder; level.badplace_delete_func = ::badplace_delete; level thread maps\mp\agents\_agent_common::init(); level.spitter_last_cloud_time = 0; } //======================================================= // initAliens //======================================================= setup_callbacks() { if ( !IsDefined( level.agent_funcs ) ) level.agent_funcs = []; level.agent_funcs["alien"] = []; level.agent_funcs["alien"]["spawn"] = ::alienAgentSpawn; level.agent_funcs["alien"]["think"] = ::alienAgentThink; level.agent_funcs["alien"]["on_killed"] = maps\mp\alien\_death::onAlienAgentKilled; level.agent_funcs["alien"]["on_damaged"] = maps\mp\alien\_damage::onAlienAgentDamaged; level.agent_funcs["alien"]["on_damaged_finished"] = maps\mp\agents\alien\_alien_think::onDamageFinish; level.alien_funcs["goon"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach; level.alien_funcs["minion"]["approach"] = maps\mp\agents\alien\_alien_minion::minion_approach; level.alien_funcs["spitter"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach; level.alien_funcs["elite"]["approach"] = maps\mp\agents\alien\_alien_elite::elite_approach; level.alien_funcs["brute"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach; level.alien_funcs["locust"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach; level.alien_funcs["leper"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach; level.alien_funcs["goon"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat; level.alien_funcs["minion"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat; level.alien_funcs["spitter"]["combat"] = maps\mp\agents\alien\_alien_spitter::spitter_combat; level.alien_funcs["elite"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat; level.alien_funcs["brute"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat; level.alien_funcs["locust"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat; level.alien_funcs["leper"]["combat"] = maps\mp\agents\alien\_alien_leper::leper_combat; level.alien_funcs["goon"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath; level.alien_funcs["minion"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath; level.alien_funcs["spitter"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath; level.alien_funcs["elite"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath; level.alien_funcs["brute"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath; level.alien_funcs["locust"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath; level.alien_funcs["leper"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath; level.used_nodes = []; level.used_nodes_list_size = 20; level.used_nodes_list_index = 0; level.alien_jump_melee_speed = 1.05; level.alien_jump_melee_gravity = 900; } //======================================================= // alienAgentThink //======================================================= alienAgentThink() { } //======================================================= // alienAgentSpawn //======================================================= alienAgentSpawn( spawnOrigin, spawnAngles, alienType, introVignetteAnim ) { if ( !isDefined( alienType ) ) alienType = "wave goon"; alien_type = remove_spawn_type( alienType ); if ( !isDefined( spawnOrigin ) || !isDefined( spawnAngles ) ) { spawnPoint = self [[level.getSpawnPoint]](); spawnOrigin = spawnpoint.origin; spawnAngles = spawnpoint.angles; } self set_alien_model( alien_type ); // escape sequence, move aliens closer to players post spawn if ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) && self.agentteam == "axis" ) { if ( !flag_exist( "nuke_went_off" ) || !flag( "nuke_went_off" ) ) { self.noTriggerHurt = true; port_failed = false; tokens = strtok( alienType, " " ); type = tokens[ 0 ]; if ( tokens.size > 1 ) type = tokens[ 1 ]; prof_begin( "port_to_player_loc" ); if ( alien_type == "spitter" && IsDefined( level.escape_spitter_target_node ) ) { spawnOrigin = self maps\mp\alien\_spawnlogic::port_to_escape_spitter_location(); } else { port_to_data = self maps\mp\alien\_spawnlogic::port_to_player_loc( type ); if ( !isdefined( port_to_data ) ) { port_failed = true; /# if ( GetDvarInt( "alien_debug_escape" ) > 0 ) IPrintLnBold( "^1Failed to port alien" ); #/ } else { spawnOrigin = port_to_data[ 0 ]; spawnAngles = port_to_data[ 1 ]; } } prof_end( "port_to_player_loc" ); if ( !port_failed ) { spawnOrigin = GetGroundPosition( spawnOrigin, 16 ); spawnOrigin -= ( 0, 0, 90 ); introVignetteAnim = level.cycle_data.spawn_node_info[ "queen_test" ].vignetteInfo[ type ]; } } } self spawn_alien_agent( spawnOrigin, spawnAngles, alien_type ); level notify( "spawned_agent", self ); self set_alien_attributes( alienType ); self set_code_fields( alien_type ); self set_script_fields( spawnOrigin ); self set_threat_bias_group( alien_type ); self type_specific_init(); self setup_watcher(); self misc_setup(); if ( isDefined ( introVignetteAnim ) ) self doIntroVignetteAnim( introVignetteAnim ); if ( isdefined( self.noTriggerHurt ) ) self.noTriggerHurt = undefined; self maps\mp\alien\_ffotd::onSpawnAlien(); //self ScrAgentUseModelCollisionBounds(); self thread maps\mp\agents\alien\_alien_think::main(); } set_code_fields( alien_type ) { self.allowJump = true; self.allowladders = 1; self.moveMode = self get_default_movemode(); self.radius = 15; self.height = 72; self.turnrate = 0.3; self.sharpTurnNotifyDist = 48; self.traverseSoonNotifyDist = level.alienAnimData.jumpLaunchArrival_maxMoveDelta; self.stopSoonNotifyDist = level.alienAnimData.stopSoon_NotifyDist; self.jumpCost = level.alien_types[ alien_type ].attributes[ "jump_cost" ]; // escape mode = jumps more if ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) ) self.jumpCost = max( 0.85, self.jumpCost * 0.66 ); self.traverseCost = level.alien_types[ alien_type ].attributes[ "traverse_cost" ]; self.runCost = level.alien_types[ alien_type ].attributes[ "run_cost" ]; if ( IsDefined( level.alien_types[ alien_type ].attributes[ "wall_run_cost" ] ) ) self ScrAgentSetWallRunCost( level.alien_types[ alien_type ].attributes[ "wall_run_cost" ] ); } get_default_movemode() { alien_type = self get_alien_type(); switch( alien_type ) { case "minion": return "walk"; default: return "run"; } } set_threat_bias_group( alien_type ) { if ( !can_attack_drill( alien_type ) ) { self SetThreatBiasGroup( "dontattackdrill" ); return; } self SetThreatBiasGroup( "other_aliens" ); } can_attack_drill( alien_type ) { if ( IsDefined( level.dlc_alien_can_attack_drill_override_func ) ) { canAttackDrill = [[level.dlc_alien_can_attack_drill_override_func]]( alien_type ); if ( IsDefined( canAttackDrill ) ) return canAttackDrill; } switch( alien_type ) { case "elite": case "minion": case "locust": case "gargoyle": case "mammoth": return false; default: return true; } } set_script_fields( spawnOrigin ) { self.species = "alien"; self.enableStop = true; self activateAgent(); self.spawnTime = GetTime(); self.attacking_player = false; self.spawnOrigin = spawnOrigin; self.recentDamages = []; self.damageListIndex = 0; self.swipeChance = 0.5; self.leapEndPos = undefined; self.trajectoryActive = false; self.melee_in_move_back = false; self.melee_in_posture = false; } remove_spawn_type( alienType ) { // if spawn type is passed in, it is the first token delimited by a space, second token is alien type spawnTypeConfig = strtok( alienType, " " ); if ( isdefined( spawnTypeConfig ) && spawnTypeConfig.size == 2 ) return spawnTypeConfig[ 1 ]; else return alienType; } set_alien_model( alien_type ) { // During kraken fight if ( isDefined( level.get_alien_model_func ) ) { alien_model = [[level.get_alien_model_func]]( alien_type ); } else { alien_model = level.alien_types[ alien_type ].attributes[ "model" ]; } self SetModel( alien_model ); self show(); self MotionBlurHQEnable(); } spawn_alien_agent( spawnOrigin, spawnAngles, alien_type ) { // the self.OnEnterAnimState field needs to be set before SpawnAgent self.OnEnterAnimState = maps\mp\agents\alien\_alien_think::onEnterAnimState; anim_class = get_anim_class( alien_type ); self SpawnAgent( spawnOrigin, spawnAngles, anim_class, 15, 50 ); } get_anim_class( alien_type ) { return level.alien_types[ alien_type ].attributes[ "animclass" ]; } set_alien_attributes( alienType ) { self maps\mp\alien\_spawnlogic::assign_alien_attributes( alienType ); } type_specific_init() { switch ( maps\mp\alien\_utility::get_alien_type() ) { case "elite": maps\mp\agents\alien\_alien_elite::elite_init(); break; case "minion": maps\mp\agents\alien\_alien_minion::minion_init(); break; case "spitter": maps\mp\agents\alien\_alien_spitter::spitter_init(); break; case "leper": maps\mp\agents\alien\_alien_leper::leper_init(); break; default: // Check for level specific override if( isDefined( level.dlc_alien_init_override_func )) { [[level.dlc_alien_init_override_func]](); } break; } } misc_setup() { self ScrAgentSetClipMode( "agent" ); self TakeAllWeapons(); //self maps\mp\agents\alien\_alien_anim_utils::calculateAnimData(); } setup_watcher() { self thread maps\mp\agents\alien\_alien_think::watch_for_scripted(); self thread maps\mp\agents\alien\_alien_think::watch_for_badpath(); self thread maps\mp\agents\alien\_alien_think::watch_for_insolid(); self thread maps\mp\_flashgrenades::MonitorFlash(); self thread maps\mp\agents\alien\_alien_think::MonitorFlash(); /# if ( GetDvarInt( "scr_aliendebugvelocity" ) == 1 ) self thread maps\mp\alien\_debug::alienDebugVelocity(); #/ } doIntroVignetteAnim( vignetteAnimInfo ) { CONST_ANIM_STATE_INDEX = 0; CONST_ANIM_INDEX_ARRAY_INDEX = 1; CONST_LABEL_INDEX = 2; CONST_END_NOTETRACK_INDEX = 3; CONST_FX_INDEX = 4; CONST_SCRIPTABLE_TARGETNAME_INDEX = 5; CONST_SCRIPTABLE_STATE_INDEX = 6; CONST_SPAWN_NODE_ID_INDEX = 7; self ScrAgentSetScripted( true ); self ScrAgentSetPhysicsMode( "noclip" ); self ScrAgentSetAnimMode( "anim deltas" ); vignetteAnimInfo = StrTok( vignetteAnimInfo, ";" ); self.vignetteAnimInfo = []; self.vignetteAnimInfo["FX"] = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_FX_INDEX] ); self.vignetteAnimInfo["scriptableName"] = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SCRIPTABLE_TARGETNAME_INDEX] ), "," ); self.vignetteAnimInfo["scriptableState"] = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SCRIPTABLE_STATE_INDEX] ), "," ); self.vignetteAnimInfo["spawnNodeID"] = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SPAWN_NODE_ID_INDEX] ); animState = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_ANIM_STATE_INDEX] ); indexArray = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_ANIM_INDEX_ARRAY_INDEX] ), "," ); animIndex = int( indexArray [ randomInt ( indexArray.size ) ] ); animLabel = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_LABEL_INDEX] ); endNotetrack = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_END_NOTETRACK_INDEX] ); animEntry = self GetAnimEntry( animState, animIndex ); if ( shouldDoGroundLerp( animEntry ) ) doLerpToEndOnGround( animState, animIndex ); if ( willPlayScriptables( animEntry ) ) resetAllScriptables( self.vignetteAnimInfo["scriptableName"], self.origin ); result = maps\mp\agents\alien\_alien_traverse::needFlexibleHeightSupport( animEntry ); if( result.need_support ) doSpawnVignetteWithFlexibleHeight( animState, animIndex, animLabel, animEntry, result.start_notetrack, result.end_notetrack, ::vignetteNotetrackHandler ); else maps\mp\agents\_scriptedAgents::PlayAnimNUntilNotetrack( animState, animIndex, animLabel, endNotetrack, ::vignetteNotetrackHandler ); self ScrAgentSetScripted( false ); } shouldDoGroundLerp( animEntry ) { return !( AnimHasNotetrack ( animEntry, "skip_ground_lerp" ) ); } willPlayScriptables( animEntry ) { return ( AnimHasNotetrack( animEntry, "play_scriptable" ) && can_play_scriptable( self.vignetteAnimInfo["spawnNodeID"], self.vignetteAnimInfo["scriptableName"] ) ); } doSpawnVignetteWithFlexibleHeight( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, notetrackHandlerFunc ) { maps\mp\agents\_scriptedAgents::PlayAnimNUntilNotetrack( animState, animIndex, animLabel, startNotetrack, notetrackHandlerFunc ); ground_pos = getEndLocOnGround( animEntry ); maps\mp\agents\alien\_alien_traverse::doTraversalWithFlexibleHeight_internal( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, ground_pos , 1, ::vignetteNotetrackHandler ); } getEndLocOnGround( animEntry ) { DROP_TO_GROUND_UP_DIST = 32; DROP_TO_GROUND_DOWN_DIST = -300; AnimEndLoc = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( animEntry, self.origin, self.angles, GetAnimLength( animEntry ) ); return drop_to_ground( AnimEndLoc, DROP_TO_GROUND_UP_DIST, DROP_TO_GROUND_DOWN_DIST ); } replaceNoneWithEmptyString( string ) { if( string == "NONE" ) return ""; return string; } vignetteNotetrackHandler( note, animState, animIndex, animTime ) { switch ( note ) { case "alien_drone_spawn_underground": case "play_fx": if ( !is_empty_string( self.vignetteAnimInfo["FX"] ) ) playSpawnVignetteFX( self.vignetteAnimInfo["FX"] ); break; case "play_scriptable": if ( can_play_scriptable( self.vignetteAnimInfo["spawnNodeID"], self.vignetteAnimInfo["scriptableName"] ) ) { playAnimOnAllScriptables( self.vignetteAnimInfo["scriptableName"], self.origin, self.vignetteAnimInfo["scriptableState"] ); if ( is_one_off_scriptable( self.vignetteAnimInfo["spawnNodeID"] ) ) inactivate_scriptable_for_node( self.vignetteAnimInfo["spawnNodeID"] ); } break; case "play_earthquake": Earthquake( 0.5, 1.5, self.origin, 800 ); break; case "delete_spawn_clip": if ( isDefined( self.intro_clips ) ) delete_items( self.intro_clips ); break; case "frontal_cone_knock_player_back": frontal_cone_knock_player_back(); break; case "apply_physics": self ScrAgentSetPhysicsMode( "gravity" ); break; default: break; } } can_play_scriptable( node_id, scriptable_name_list ) { return ( ( is_scriptable_status( node_id, "always_on" ) || is_scriptable_status( node_id, "one_off" ) ) && scriptable_name_list.size > 0 ); } is_scriptable_status( node_id, state ) { return ( level.cycle_data.spawn_node_info[node_id].scriptableStatus == state ); } is_one_off_scriptable( node_id ) { return is_scriptable_status( node_id, "one_off" ); } inactivate_scriptable_for_node( node_id ) { level.cycle_data.spawn_node_info[node_id].scriptableStatus = "inactive"; } delete_items( item_array ) { foreach( item in item_array ) { if ( isDefined( item ) ) item delete(); } } frontal_cone_knock_player_back() { KNOCK_BACK_ACTIVATION_DIST_SQ = 22500; // 150 * 150 KNOCK_BACK_FORCE_MAGNITUDE = 650; FRONT_CONE_LIMIT = 0.2588; //cos( 70 ) self_forward = anglesToForward( self.angles); foreach ( player in level.players) { self_to_player = vectorNormalize ( player.origin - self.origin ); if( VectorDot( self_to_player, self_forward ) > FRONT_CONE_LIMIT && distanceSquared( player.origin , self.origin ) <= KNOCK_BACK_ACTIVATION_DIST_SQ ) { player SetVelocity( VectorNormalize( player.origin - self.origin ) * KNOCK_BACK_FORCE_MAGNITUDE ); player DoDamage( ( player.health / 10 ) , self.origin ); } } } resetAllScriptables( scriptable_name_list, position ) { for( i = 0; i < scriptable_name_list.size; i++ ) maps\mp\agents\alien\_alien_anim_utils::resetScriptable( scriptable_name_list[i], position ); } playAnimOnAllScriptables( scriptable_name_list, position, scriptable_state_list ) { /# AssertEx( scriptable_name_list.size == scriptable_state_list.size, "The scriptable name lists and state lists have mismatch with their size near position ( " + position + " )." ); #/ for( i = 0; i < scriptable_name_list.size; i++ ) maps\mp\agents\alien\_alien_anim_utils::playAnimOnScriptable( scriptable_name_list[i], position, int( scriptable_state_list[i] ) ); } is_empty_string( string ) { return ( string == "" ); } playSpawnVignetteFX( effect_key ) { effect_id = level._effect[effect_key]; AssertEx( isDefined( effect_id ), "'" + effect_key + "' is not a valid key for the spawn vignette FX. Load the effect in alien type specific script." ); ground_position = GetGroundPosition( self.origin + ( 0, 0, 100 ), 16 ); // play fx on ground surface PlayFX( effect_id, ground_position, (0,0,1) ); } doLerpToEndOnGround( animState, animIndex ) { VERTICAL_DELTA_BUFFER = 2; anime = self GetAnimEntry( animState, animIndex ); lerp_time = maps\mp\agents\alien\_alien_anim_utils::getLerpTime( anime ); lerp_target_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( anime, self.origin, self.angles, lerp_time ); z_delta = getVerticalDeltaToEndGroud( anime ); lerp_target_pos += ( 0, 0, z_delta + VERTICAL_DELTA_BUFFER ); thread maps\mp\agents\alien\_alien_anim_utils::doLerp( lerp_target_pos, lerp_time ); } getVerticalDeltaToEndGroud( anime ) { GET_GROUND_DROP_HEIGHT = 100; AI_PHYSICS_TRACE_RADIUS = 32; AI_PHYSICS_TRACE_HEIGHT = 72; anime_delta = GetMoveDelta( anime, 0, 1 ); anime_delta = RotateVector( anime_delta, self.angles ); anime_height = anime_delta[2]; anime_end_pos = self.origin + anime_delta; trace_start_pos = anime_end_pos + ( 0, 0, GET_GROUND_DROP_HEIGHT ); trace_end_pos = anime_end_pos - ( 0, 0, GET_GROUND_DROP_HEIGHT ); ground_pos = self AIPhysicsTrace( trace_start_pos, trace_end_pos, AI_PHYSICS_TRACE_RADIUS, AI_PHYSICS_TRACE_HEIGHT ); self_to_ground_height = ( ground_pos - self.origin )[2]; return ( self_to_ground_height - anime_height ); }