#include maps\mp\_utility; #include common_scripts\utility; #include maps\mp\alien\_utility; #include maps\mp\gametypes\_hud_util; #include maps\mp\agents\_agent_utility; #include maps\mp\alien\_perk_utility; CONST_DRILL_HEALTH = 150; // health: of planted bomb to withstand alien attack CONST_DRILL_HEALTH_HARDCORE = 1250; // bomb health in hardcore mode CONST_DRILL_HEALTH_HARDCORE_SOLO= 2000; // bomb health in hardcore mode CONST_HEALTH_INVULNERABLE = 20000; CONST_DRILL_THREATBIAS_MIN = -3000;// min threat - starting threat, threat while players are in range CONST_DRILL_THREATBIAS_MAX = -1000; // max threat CONST_DRILL_THREAT_RADIUS_MIN = 1000; // radius before threat is increased based on distance CONST_DRILL_THREAT_RADIUS_MAX = 2500; // radius where threat increased is maxed CONST_DRILL_MODEL = "mp_laser_drill"; CONST_DRILL_MODEL_OBJ = "mp_laser_drill"; CONST_DRILL_OUTLINE_COLOR_INDEX = 3; // drill overheat constants CONST_DRILL_OVERHEAT_TIMER = 45; // seconds CONST_DRILL_OVERHEAT_TIMER_DECREASE = 5; CONST_DRILL_OVERHEAT_TIMER_MIN = 25; CONST_DRILL_OVERHEAT_TIMER_WARN_1 = 20; CONST_DRILL_OVERHEAT_TIMER_WARN_2 = 10; init_drill() { // state flags flag_init( "drill_detonated" ); flag_init( "drill_destroyed" ); flag_init ( "drill_drilling" ); level.drill_use_trig = getent( "drill_pickup_trig", "targetname" ); // needs to exist in map, and needs to be placed in an unreachable location if ( isdefined( level.drill_use_trig ) ) level.drill_use_trig.original_origin = level.drill_use_trig.origin; level.drill_id = 0; level.drill_marker_id = 1; // carried drill level.drill = undefined; level.drill_carrier = undefined; // inits init_fx(); // fx init_drill_drop_loc(); // drill start location // drill device loop thread drill_think(); //drill gets dropped out of playable area level thread drill_out_of_playable(); } drill_out_of_playable() { level endon( "game_ended" ); out_of_playable_areas = getentarray( "trigger_hurt","classname" ); while ( 1 ) { if ( !isDefined ( level.drill ) ) { wait ( .5 ); continue; } foreach ( area in out_of_playable_areas ) { if ( !isDefined ( area.script_noteworthy ) || area.script_noteworthy != "out_of_playable" ) continue; if ( level.drill istouching ( area ) ) { level.drill delete(); //AssertEx ( isDefined ( level.last_drill_pickup_origin ) && isDefined ( level.last_drill_pickup_angles ),"Last drill pickup spot was not defined" ); playfx( level._effect[ "alien_teleport" ] , level.last_drill_pickup_origin ); playfx ( level._effect[ "alien_teleport_dist" ], level.last_drill_pickup_origin ); drop_drill ( level.last_drill_pickup_origin, level.last_drill_pickup_angles ); foreach ( player in level.players ) { player setLowerMessage( "drill_overboard",&"ALIEN_COLLECTIBLES_DRILL_OUTOFPLAY",4 ); } } } wait( 0.1 ); } } //======================================================= // Inits //======================================================= init_drill_drop_loc() { level.drill_locs = []; level.drill_locs = getstructarray( "bomb_drop_loc", "targetname" ); } init_fx() { // fx level._effect[ "drill_laser_contact" ] = loadfx( "vfx/gameplay/alien/vfx_alien_drill_laser_contact" ); level._effect[ "drill_laser" ] = loadfx( "vfx/gameplay/alien/vfx_alien_drill_laser" ); level._effect[ "stronghold_explode_med" ] = loadfx( "vfx/gameplay/mp/killstreaks/vfx_sentry_gun_explosion" ); level._effect[ "stronghold_explode_large" ] = loadfx( "fx/explosions/aerial_explosion" ); level._effect[ "alien_hive_explode" ] = loadfx( "fx/explosions/alien_hive_explosion" ); level.spawnGlowModel["friendly"] = "mil_emergency_flare_mp"; level.spawnGlow["friendly"] = loadfx( "fx/misc/flare_ambient_green" ); } //======================================================= // Drill object loop //======================================================= // loop to respawn drill object drill_think() { level endon( "game_ended" ); //wait 1; while ( !isdefined( level.players ) || level.players.size < 1 ) wait 0.05; level.drill_health_hardcore = CONST_DRILL_HEALTH_HARDCORE; if ( isPlayingSolo() ) level.drill_health_hardcore = CONST_DRILL_HEALTH_HARDCORE_SOLO; level thread drill_threat_think(); // drill initial drop location //drop_loc = ( 2801, -117, 595 ); // default for "mp_alien_town" drop_loc = ( 2822.27, -196, 524.068 ); // default to match drill animation in "mp_alien_town" drop_loc_struct = getstruct( "drill_loc", "targetname" ); if ( isdefined( drop_loc_struct ) ) drop_loc = drop_loc_struct.origin; //drop_angles = ( 0, 0, 0 ); drop_angles = ( 1.287, 0.995, -103.877 ); //default to match drill animation in "mp_alien_town" if ( isdefined( drop_loc_struct ) && isdefined( drop_loc_struct.angles ) ) drop_angles = drop_loc_struct.angles; /# drop_loc = maps\mp\alien\_debug::adjust_drill_loc( drop_loc ); #/ level waittill( "spawn_intro_drill" ,spawnpos,spawnang ); // overrides of drill origin and angles, ex: intro animation end frame drop_to_ground = true; if ( isdefined( level.initial_drill_origin ) && isdefined( level.initial_drill_angles ) ) { drop_loc = level.initial_drill_origin; drop_angles = level.initial_drill_angles; drop_to_ground = false; } if ( isDefined( spawnpos ) && isDefined( spawnang ) ) { drop_loc = spawnpos; drop_angles = spawnang; drop_to_ground = false; } // TODO: remove after new model is in, and carry object is setup, // currently is to remove the vest model dropped on ground when dropping drill marker = undefined; while ( true ) { spawn_drill_raw( CONST_DRILL_MODEL_OBJ, drop_loc, drop_angles, marker, drop_to_ground ); drop_to_ground = true; level waittill( "new_drill", drop_loc, drop_angles, marker ); assertex( isdefineD( drop_loc ), "Drill dropped at invalid position" ); wait 0.05; } } // drop drill spawns new drill drop_drill( pos, angles, marker ) { level notify( "new_drill", pos, angles, marker ); } // spawns the drill and listens for pickup spawn_drill_raw( model, pos, angles, marker, drop_to_ground ) { if ( !isdefined( drop_to_ground ) ) drop_to_ground = true; level.drill_carrier = undefined; // means no one is carrying drill as drill is spawned // remove previous version, if ( isdefined( level.drill ) ) { level.drill delete(); level.drill = undefined; } // drill object level.drill = spawn( "script_model", pos ); level.drill setmodel( model ); level.drill set_drill_icon(); level.drill.state = "idle"; // idle meaning it can be picked up/planted if ( drop_to_ground ) level.drill thread angles_to_ground( pos, angles, ( 0, 0, -4 ) ); else level.drill.angles = angles; // wait until intro sequence is complete if ( flag_exist( "intro_sequence_complete" ) && !flag( "intro_sequence_complete" ) ) flag_wait( "intro_sequence_complete" ); if ( alien_mode_has( "outline" ) ) maps\mp\alien\_outline_proto::add_to_drill_preplant_watch_list( level.drill ); //using marker to kill the dropped alien drill model. //TODO: remove "marker" when we get a proper drill model and the tags are correct for makeusable if ( !is_true( level.automatic_drill ) ) level.drill thread drill_pickup_listener( marker ); level notify( "drill_spawned" ); } enable_alt_drill_pickup( drill ) { assert( isdefined( level.drill_use_trig ) ); level.drill_use_trig.origin = drill.origin + ( 0, 0, 24 ); } disable_alt_drill_pickup() { assert( isdefined( level.drill_use_trig ) ); level.drill_use_trig.origin = level.drill_use_trig.original_origin; } // listen for pickup, updates icon from drill to carrier drill_pickup_listener( marker ) { // self is level.drill, the script model self endon( "death" ); level endon( "game_ended" ); level endon( "new_drill" ); if ( isdefined( level.drill_use_trig ) ) use_trig = level.drill_use_trig; else use_trig= self; if ( !is_true( level.prevent_drill_pickup ) ) { // ======= SETUP FOR PICKUP ======= if ( isdefined( level.drill_use_trig ) ) { level.drill_use_trig enable_alt_drill_pickup( self ); } else { use_trig MakeUsable(); } } use_trig SetCursorHint( "HINT_ACTIVATE" ); use_trig SetHintString( &"ALIEN_COLLECTIBLES_PICKUP_BOMB" ); // ======= WAIT FOR PICKUP ======= while( true ) { use_trig waittill( "trigger", owner ); if ( owner is_holding_deployable() ) { owner setLowerMessage( "cant_buy", &"ALIEN_COLLECTIBLES_PLAYER_HOLDING", 3 ); continue; } if ( owner GetStance() == "prone" || owner GetStance() == "crouch" ) { owner setLowerMessage( "change_stance", &"ALIENS_PATCH_CHANGE_STANCE", 3 ); continue; } if ( is_true ( owner.picking_up_item ) ) continue; if ( is_true ( owner.isCarrying ) ) continue; owner.has_special_weapon = true; owner _disableUsability(); owner thread delayed_enable_usability(); if( isPlayer( owner ) ) break; } // ======= PICKED UP ======= // tell the world who picked up the drill if ( alien_mode_has( "outline" ) ) maps\mp\alien\_outline_proto::remove_from_drill_preplant_watch_list( level.drill ); // move alternate drill pickup trigger far away if ( isdefined( level.drill_use_trig ) ) level.drill_use_trig disable_alt_drill_pickup(); level notify( "drill_pickedup", owner ); self PlaySound( "extinction_item_pickup" ); level.drill_carrier = owner; level.last_drill_pickup_origin = drop_to_ground( self.origin, 16, -32 ); // trace to ground, to prevent cascading offset level.last_drill_pickup_angles = self.angles; level.drill_carrier set_drill_icon( true ); self.state = "carried"; // player setup owner thread drop_drill_on_death(); owner thread drop_drill_on_disconnect(); owner.lastweapon = owner GetCurrentWeapon(); owner _giveWeapon( "alienbomb_mp" ); owner SwitchToWeapon( "alienbomb_mp" ); owner DisableWeaponSwitch(); owner _disableOffhandWeapons(); // ======= CLEAN UP ======= // clean up drill marker model after pickup if (IsDefined (marker) ) marker delete(); owner notify( "kill_spendhint" ); owner notify( "dpad_cancel" ); // removed from the world, also ends threads running on the pickupable object self delete(); } delayed_enable_usability() { self endon( "death" ); self endon( "disconnect" ); wait ( 1 ); self _enableUsability(); } // drop drill when player so others can pick it up drop_drill_on_death() { // self is player level endon( "game_ended" ); level endon( "new_drill" ); // new drill was requested, old drill will be removed level endon( "drill_planted" ); // drill left hand of owner level endon( "drill_dropping" ); // drill left hand of owner // only one instance of this per player self notify( "watching_drop_drill_on_death" ); self endon( "watching_drop_drill_on_death" ); // either death or last stand mode self waittill_either( "death", "last_stand" ); self Takeweapon( "alienbomb_mp" ); self EnableWeaponSwitch(); self SwitchToWeapon( self.lastWeapon ); self _enableOffhandWeapons(); level.drill_carrier = undefined; assert( isdefined( self.last_death_pos ) ); //make sure the last_death_pos is actually on the ground groundpos = GetGroundPosition( self.last_death_pos + (0,0,4 ),8 ); angles = self.angles; if ( is_true ( self.kill_trigger_event_processed ) ) //player killed by kill trigger, don't drop the drill here { drill_spot = getClosest ( self.origin, level.killTriggerSpawnLocs ); groundpos = GetGroundPosition ( drill_spot.origin + ( 0,0,4 ),8 ); if ( !isDefined( drill_spot.angles ) ) drill_spot.angles = ( 0,0,0 ); angles = drill_spot.angles; } drop_drill( groundpos, angles ); } //drop the drill when the owner disconnects while holding it drop_drill_on_disconnect() { level endon( "drill_dropping" ); level endon( "game_ended" ); self endon( "death" ); self endon ( "last_stand" ); self waittill( "disconnect" ); playfx( level._effect[ "alien_teleport" ] , level.last_drill_pickup_origin ); playfx ( level._effect[ "alien_teleport_dist" ], level.last_drill_pickup_origin ); drop_drill ( level.last_drill_pickup_origin, level.last_drill_pickup_angles ); foreach ( player in level.players ) { if ( !isAlive ( player ) ) continue; if ( player == self ) continue; player setLowerMessage( "drill_overboard",&"ALIEN_COLLECTIBLES_DRILL_OUTOFPLAY",4 ); } } // teleport the drill if not near blocker CONST_DRILL_TELEPORT_RANGE = 1250; teleport_drill( pos ) { wait 5; // padding, so hive destruction fx can finish // only if drill is not carried and out of set range if ( isdefined( level.drill ) && !isdefined( level.drill_carrier ) && ( distance( pos, level.drill.origin ) > CONST_DRILL_TELEPORT_RANGE ) ) { pos = drop_to_ground( pos, 16, -64 ); // in case the struct is floating level.drill angles_to_ground( pos, level.drill.angles, ( 0, 0, -4 ) ); // spawn drill oriented to ground normal level.drill set_drill_icon(); enable_alt_drill_pickup( level.drill ); } } // loops until drilling is complete, handles being offline drilling( pos, owner ) { if ( isDefined( level.set_drill_state_drilling_override ) ) { self thread [[level.set_drill_state_drilling_override]]( pos,owner ); return; } // self is hive location struct drill is drilling self endon( "stop_listening" ); self endon( "drill_complete" ); // =======[STATE: PLANT]======= // self thread set_drill_state_plant( pos, owner ); level.drill endon( "death" ); level.drill.owner = owner; level.encounter_name = self.target; level.drill.start_time = gettime(); // initial start time, in case drill is killed before it started flag_set( "drill_drilling" ); // wait till unfold animation completes level.drill waittill_any_timeout( 5, "drill_finished_plant_anim" ); // set timing parameters ( ex. depth, layers etc. ) self init_drilling_parameters(); // =======[STATE: RUN]======= // level.drill.start_time = gettime(); // updated start time self thread set_drill_state_run( owner ); self maps\mp\alien\_hive::hive_play_drill_planted_animations(); // wait till offline level.drill waittill( "offline", attacker, damage ); // =======[STATE: OFF]======= // self thread set_drill_state_offline(); flag_set( "drill_destroyed" ); wait 2; maps\mp\gametypes\aliens::AlienEndGame( "axis", maps\mp\alien\_hud::get_end_game_string_index( "drill_destroyed" ) ); return; } set_drill_attack_setup() { synchDirections = []; synchDirections["brute"][0] = set_attack_sync_direction( ( 0, 1, 0 ), "alien_drill_attack_drill_F_enter", "alien_drill_attack_drill_F_loop", "alien_drill_attack_drill_F_exit", "attack_drill_front", "attack_drill" ); synchDirections["brute"][1] = set_attack_sync_direction( ( -1, 0, 0 ), "alien_drill_attack_drill_R_enter", "alien_drill_attack_drill_R_loop", "alien_drill_attack_drill_R_exit", "attack_drill_right", "attack_drill" ); synchDirections["brute"][2] = set_attack_sync_direction( ( 1, 0, 0 ), "alien_drill_attack_drill_L_enter", "alien_drill_attack_drill_L_loop", "alien_drill_attack_drill_L_exit", "attack_drill_left", "attack_drill" ); synchDirections["goon"][0] = set_attack_sync_direction( ( 0, 1, 0 ), "alien_goon_drill_attack_drill_F_enter", "alien_goon_drill_attack_drill_F_loop", "alien_goon_drill_attack_drill_F_exit", "attack_drill_front", "attack_drill" ); synchDirections["goon"][1] = set_attack_sync_direction( ( -1, 0, 0 ), "alien_goon_drill_attack_drill_R_enter", "alien_goon_drill_attack_drill_R_loop", "alien_goon_drill_attack_drill_R_exit", "attack_drill_right", "attack_drill" ); synchDirections["goon"][2] = set_attack_sync_direction( ( 1, 0, 0 ), "alien_goon_drill_attack_drill_L_enter", "alien_goon_drill_attack_drill_L_loop", "alien_goon_drill_attack_drill_L_exit", "attack_drill_left", "attack_drill" ); endNotifies[0] = "offline"; endNotifies[1] = "death"; endNotifies[2] = "drill_complete"; endNotifies[3] = "destroyed"; self set_synch_attack_setup( synchDirections, true, endNotifies, undefined, ::drill_synch_attack_play_anim, ::drill_synch_attack_play_anim, ::drill_synch_attack_exit, "drill" ); } drill_synch_attack_play_anim( anim_name ) { level.drill ScriptModelClearAnim(); level.drill ScriptModelPlayAnim( anim_name ); } drill_synch_attack_exit( anim_name, anim_length ) { if ( IsDefined( anim_name ) ) { level.drill ScriptModelClearAnim(); level.drill ScriptModelPlayAnim( anim_name ); wait anim_length; } if ( IsAlive( level.drill ) && !flag( "drill_detonated" ) ) { level.drill ScriptModelClearAnim(); level.drill ScriptModelPlayAnim( "alien_drill_loop" ); } } use_alternate_drill() { return true; } watch_to_repair( hive_struct ) { self endon( "drill_complete" ); self endon( "death" ); hive_struct endon( "hive_dying" ); wait 5.0; self MakeUnUsable(); CONST_DRILL_REPAIR_PAYMENT = 100; CONST_DRILL_HEALTH_REPAIRED = 1000; CONST_DRILL_REPAIR_BASE_TIME = 4000; // Time in ms for solo mode to repair drill CONST_DRILL_PER_PLAYER_ADDITIONAL_TIME = 2000; //10 seconds total for 4 players while ( 1 ) { self MakeUnusable(); while ( 1 ) { drill_health = ( self.health - CONST_HEALTH_INVULNERABLE ) / level.drill_health_hardcore; if ( drill_health < 0.75 ) break; wait ( 1 ); } self MakeUsable(); if ( isDefined( level.drill_repair ) ) self SetHintString( level.drill_repair ); else self SetHintString( &"ALIEN_COLLECTIBLES_DRILL_REPAIR" ); self waittill( "trigger", player ); if ( is_true ( player.iscarrying ) ) continue; self SetHintString( "" ); player_count = level.players.size; player.isRepairing = true; level notify ("dlc_vo_notify","drill_repair", player); use_time = int( CONST_DRILL_REPAIR_BASE_TIME * player perk_GetDrillTimeScalar() * player.drillSpeedModifier ); if( player_count > 1 ) use_time = int( ( CONST_DRILL_REPAIR_BASE_TIME + (( player_count - 1 ) * CONST_DRILL_PER_PLAYER_ADDITIONAL_TIME ) ) * player perk_GetDrillTimeScalar() * player.drillSpeedModifier ); result = self useHoldThink( player, use_time ); if( !result ) { player.isRepairing = false; continue; } if ( isdefined( level.drill_sfx_lp ) ) { if ( isdefined( level.drill_overheat_lp_02 )) level.drill_overheat_lp_02 StopLoopSound(); if ( !hive_struct is_door() && !hive_struct is_door_hive() && level.script != "mp_alien_dlc3" ) { if( level.script == "mp_alien_last" ) level.drill_sfx_lp PlayLoopSound( "alien_conduit_on_lp" ); else level.drill_sfx_lp PlayLoopSound( "alien_laser_drill_lp" ); } } level notify( "dlc_vo_notify","drill_repaired" , player); level notify( "drill_repaired"); player.isRepairing = false; // black box print hive_struct thread drill_reset_BBPrint( player ); player maps\mp\alien\_persistence::give_player_currency( CONST_DRILL_REPAIR_PAYMENT ); self.health = level.drill_health_hardcore + CONST_HEALTH_INVULNERABLE; level.drill_last_health = level.drill_health_hardcore + CONST_HEALTH_INVULNERABLE; update_drill_health_HUD(); player.isRepairing = false; // EoG tracking: drill restarts player maps\mp\alien\_persistence::eog_player_update_stat( "drillrestarts", 1 ); wait 1.0; } } // [!] function not to be used to reset drills not yet planted set_drill_state_plant( pos, owner ) { // self is hive location struct // reset drill if ( isdefined( level.drill ) ) { level.drill delete(); level.drill = undefined; } // TODO: Replace with animated drill model level.drill = spawn( "script_model", pos ); level.drill setmodel( CONST_DRILL_MODEL ); level.drill.state = "planted"; level.drill.angles = self.angles; // Only allow synced attack when drilling a hive if( !is_door()) level.drill set_drill_attack_setup(); if ( isDefined( level.drill_attack_setup_override ) ) //for overriding the default sync animations if necessary level.drill [[level.drill_attack_setup_override ]](); //level.drill thread angles_to_ground( pos, self.angles, ( 0, 0, 0 ) ); health = CONST_DRILL_HEALTH; if ( use_alternate_drill() ) { health = level.drill_health_hardcore; level.drill thread watch_to_repair( self ); } level.drill.maxhealth = CONST_HEALTH_INVULNERABLE + health; level.drill.health = int( CONST_HEALTH_INVULNERABLE + ( health * owner perk_GetDrillHealthScalar() ) ); level.drill thread watch_drill_health_for_challenge(); if ( alien_mode_has( "outline" ) ) maps\mp\alien\_outline_proto::add_to_outline_drill_watch_list ( level.drill, 0 ); thread sfx_drill_plant(); // init depth marker self.depth_marker = gettime(); level thread maps\mp\alien\_music_and_dialog::playVOForBombPlant(owner ); // remove drill icon destroy_drill_icon(); // Normal drill enter anim if( !is_door() && !is_door_hive() ) { level.drill ScriptModelPlayAnim( "alien_drill_enter" ); wait 4; } else { //small wait here since there isn't a seperate unfolding/folding animation for the door drilling wait .5; } level.drill notify( "drill_finished_plant_anim" ); } watch_drill_health_for_challenge() { self endon( "drill_complete" ); self endon( "death" ); while ( 1 ) { drill_health = ( level.drill.health - CONST_HEALTH_INVULNERABLE ) / level.drill_health_hardcore; if ( drill_health < 0.5 ) { maps\mp\alien\_challenge::update_challenge ( "no_stuck_drill" , false); break; } wait ( 1 ); } } drill_threat_think() { // self is stronghold_loc struct level endon( "game_ended" ); interval = 1; // seconds while ( 1 ) { if ( !isdefined( level.drill ) || !IsSentient( level.drill ) || !isAlive( level.drill ) ) { wait interval; continue; } if ( use_alternate_drill() ) { self.drill.threatbias = -1000; wait interval; continue; } total_dist = 0; players_available = 0; foreach ( player in level.players ) { if ( isdefined( player ) && isalive( player ) ) { players_available++; total_dist += distance2D( player.origin, level.drill.origin ); } } // not enough players if ( players_available == 0 ) { // reset level.drill.threatbias = int( CONST_DRILL_THREATBIAS_MIN ); wait interval; continue; } average_dist = total_dist / max( 1, players_available ); if ( average_dist < CONST_DRILL_THREAT_RADIUS_MIN ) { level.drill.threatbias = int( CONST_DRILL_THREATBIAS_MIN ); } else if ( average_dist > CONST_DRILL_THREAT_RADIUS_MAX ) { level.drill.threatbias = int( CONST_DRILL_THREATBIAS_MAX ); } else { radius_value_range = CONST_DRILL_THREAT_RADIUS_MAX - CONST_DRILL_THREAT_RADIUS_MIN; threat_value_range = CONST_DRILL_THREATBIAS_MAX - CONST_DRILL_THREATBIAS_MIN; dist_ratio = ( average_dist - CONST_DRILL_THREAT_RADIUS_MIN ) / radius_value_range; threat_delta = dist_ratio * threat_value_range; level.drill.threatbias = int( CONST_DRILL_THREATBIAS_MIN + threat_delta ); } wait interval; } } set_drill_state_run( owner ) { if ( isDefined( level.set_drill_state_run_override ) ) { self thread [[level.set_drill_state_run_override]]( owner ); return; } // self is stronghold_loc struct self endon( "death" ); self endon( "stop_listening" ); level.drill.state = "online"; level.drill notify( "online" ); // setup for attackable level.drill setCanDamage( true ); level.drill MakeUnUsable(); level.drill SetHintString( "" ); health = CONST_DRILL_HEALTH; if ( use_alternate_drill() ) { health = level.drill_health_hardcore; } // reset attributes level.drill.maxhealth = CONST_HEALTH_INVULNERABLE + health; level.drill.health = int( CONST_HEALTH_INVULNERABLE + ( health * level.drill.owner perk_GetDrillHealthScalar() ) ); level.drill.threatbias = CONST_DRILL_THREATBIAS_MIN; level.drill MakeEntitySentient( "allies" ); level.drill SetThreatBiasGroup( "drill" ); update_drill_health_HUD(); // tell aliens drill is up for attack! foreach ( agent in level.agentArray ) { if ( isdefined( agent.wave_spawned ) && agent.wave_spawned ) agent GetEnemyInfo( level.drill ); } // play fx on drill when armed laser_fx_tag_angles = level.drill GetTagAngles( "tag_laser" ); laser_fx_dir_forward = AnglesToForward( laser_fx_tag_angles ); laser_fx_dir_up = AnglesToUp( laser_fx_tag_angles ); laser_offset_dir = VectorCross(laser_fx_dir_forward,(0,0,1)); fx_loc = level.drill GetTagOrigin( "tag_laser_end" ) - ( 0, 0, 16 ) + (laser_offset_dir * 4 * -1) + (laser_fx_dir_forward * 1.0 * -1 ); laser_fx_loc = level.drill GetTagOrigin( "tag_laser" ) - ( 0, 0, 8 ) ; level.drill.fxEnt = SpawnFx( level._effect[ "drill_laser_contact" ], fx_loc ); level.drill.fxLaserEnt = SpawnFx( level._effect[ "drill_laser" ], laser_fx_loc, laser_fx_dir_forward, laser_fx_dir_up ); // play loop sfx door = ( self is_door() || self is_door_hive() ); thread sfx_drill_on(door); // Play door drilling anim if( is_door()) { level notify( "drill_start_door_fx" ); level.drill ScriptModelPlayAnim( "alien_drill_open_door" ); // Door drilling anim } else if ( isDefined( level.custom_hive_logic ) ) { level [[level.custom_hive_logic]](); } // Play drilling loop anim else { TriggerFx( level.drill.fxEnt ); TriggerFx( level.drill.fxLaserEnt ); level.drill ScriptModelPlayAnim( "alien_drill_loop" ); } // friendly fire and offline catch self thread handle_bomb_damage(); // update time marker, to track running time, so it can be subtracted from total when it runs again self.depth_marker = gettime(); // thread to watch for end of drilling self thread monitor_drill_complete( self.depth ); self thread maps\mp\alien\_hive::hive_pain_monitor(); // set waypoint to defend location self thread maps\mp\alien\_hive::set_hive_icon( "waypoint_alien_defend" ); // remove drill icon destroy_drill_icon(); maps\mp\alien\_hud::turn_on_drill_meter_HUD( self.depth ); level thread watch_drill_depth_for_vo( self.depth ); } watch_drill_depth_for_vo ( time ) { level endon( "drill_detonated"); level endon( "game_ended" ); wait ( time/2 ); level thread maps\mp\alien\_music_and_dialog::playVOForDrillHalfway(); } monitor_drill_complete( depth ) { // self is stronghold_loc struct self endon( "death" ); self endon( "stop_listening" ); level.drill endon( "offline" ); while ( self.layers.size > 0 ) { layer_depth = self.layers[ self.layers.size - 1 ]; is_last_layer = self.layers.size == 1; remaining_depth_to_layer = depth - layer_depth; if ( is_last_layer ) self childthread maps\mp\alien\_music_and_dialog::playMusicBeforeReachLayer( remaining_depth_to_layer ); msg = "remaining_depth_to_layer is negative, "; msg = msg + "[depth=" + depth + "][layer_depth="+ layer_depth +"][layer index="+ (self.layers.size - 1) +"]"; msg = msg + "[hive.origin=" + self.origin + "]"; assertex( remaining_depth_to_layer >= 0, msg ); self waittill_any_timeout( remaining_depth_to_layer, "force_drill_complete" ); // layer is reached self.layer_completed++; SetOmnvar( "ui_alien_drill_layer_completed", self.layer_completed ); self.layers = array_remove( self.layers, layer_depth ); depth = layer_depth; reach_layer_earthquake(); if ( !self is_door() ) { reach_layer_spawn_event( is_last_layer ); } } self notify( "drill_complete" ); level.drill notify( "drill_complete" ); level.encounter_name = undefined; flag_clear( "drill_drilling" ); flag_set( "drill_detonated" ); //clear any repair progress bars leftover on the players foreach ( player in level.players ) { //ignore players who are in the process of reviving if ( !isAlive ( player ) || is_true ( player.isReviving ) || is_true( player.being_revived ) ) continue; player SetClientOmnvar( "ui_securing_progress",0 ); player SetClientOmnvar( "ui_securing",0); } SetOmnvar( "ui_alien_drill_state", 0 ); } reach_layer_earthquake() { earthquake_intensity = 0.4; warn_delay = 1.75; if ( self is_door() ) { earthquake_intensity = 0.15; } thread maps\mp\alien\_hive::warn_all_players( warn_delay, earthquake_intensity ); } reach_layer_spawn_event( is_last_layer ) { if ( is_last_layer ) return; notify_msg = "reached_layer_" + self.layer_completed; //TODO: re-enable when table tweaked maps\mp\alien\_spawn_director::activate_spawn_event( notify_msg ); /# if ( GetDvarInt( "alien_debug_director" ) > 0 ) IPrintLnBold( "activate_spawn_event: " + notify_msg ); #/ } init_drilling_parameters() { if ( self is_door() ) { //layers_info = level.cycle_data.cycle_drill_layers[level.current_cycle_num + 1]; self.depth = 30;//layers_info[ layers_info.size - 1 ]; // last entry in the layers self.total_depth = self.depth; // .depth changes as we drill, total_depth never changes self.layer_completed = 0; self.layers[0] = 0; // Send the table line info to LUA SetOmnvar( "ui_alien_drill_layers_table_line", ( 599 + level.current_cycle_num + 1 ) ); SetOmnvar( "ui_alien_drill_layer_completed", self.layer_completed ); } else { // init depths and layers layers_info = level.cycle_data.cycle_drill_layers[level.current_cycle_num + 1]; self.depth = layers_info[ layers_info.size - 1 ]; // last entry in the layers self.total_depth = self.depth; // .depth changes as we drill, total_depth never changes self.layer_completed = 0; self.layers[0] = 0; for ( i = 0; i <= layers_info.size - 2; i++ ) self.layers[ self.layers.size ] = layers_info[ i ]; // Send the table line info to LUA SetOmnvar( "ui_alien_drill_layers_table_line", ( 599 + level.current_cycle_num + 1 ) ); SetOmnvar( "ui_alien_drill_layer_completed", self.layer_completed ); } } set_drill_state_offline() { // self is stronghold_loc struct self endon( "death" ); self endon( "stop_listening" ); level.drill.state = "offline"; //level.drill notify( "offline" ); // already notified by damage monitor // delete flare fx if ( Isdefined( level.drill.fxEnt) ) level.drill.fxEnt Delete(); if ( Isdefined( level.drill.fxLaserEnt) ) level.drill.fxLaserEnt Delete(); if( is_door()) { level notify( "drill_stop_door_fx" ); } // stop sfx loop thread sfx_drill_offline(); // mark time depth_delta = ( gettime() - self.depth_marker )/1000; self.depth = max( 0, self.depth - depth_delta ); // plays non operate anim level.drill ScriptModelPlayAnim( "alien_drill_operate_end" ); wait 1.4; level.drill ScriptModelPlayAnim( "alien_drill_nonoperate" ); level.drill MakeUsable(); level.drill SetCursorHint( "HINT_ACTIVATE" ); level.drill SetHintString( &"ALIEN_COLLECTIBLES_PLANT_BOMB" ); level.drill setCanDamage( false ); level.drill FreeEntitySentient(); // rid the defend icon self maps\mp\alien\_hive::destroy_hive_icon(); // set drill icon to signal player for reactivation level.drill set_drill_icon(); // Make drill meter stop SetOmnvar( "ui_alien_drill_state", 2 ); } handle_bomb_damage() { // self is stronghold_loc struct self endon( "death" ); self endon( "stop_listening" ); level endon( "hives_cleared" ); level.drill endon( "death" ); level.drill endon( "offline" ); // no need to monitor damages if offline //setting this to 0 since it conflicts with the delay in the vo script itself DRILL_HEALTH_VO_SPAM_DELAY = 0; last_damage_time = GetTime(); level.drill_last_health = level.drill.health; repair_hint1_given = false; repair_hint2_given = false; repair_hint3_given = false; while ( 1 ) { level.drill waittill( "damage", amount, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon ); if ( isDefined ( attacker ) && isAi( attacker ) ) { level.drill_last_health = level.drill_last_health - amount; } else if ( isDefined ( weapon ) && weapon == "alien_minion_explosion" ) { level.drill_last_health = level.drill_last_health - amount; } else { level.drill.health = level.drill_last_health; continue; } if ( IsDefined( level.level_drill_damage_adjust_function )) [[ level.level_drill_damage_adjust_function ]]( amount, attacker, weapon ); level.drill.health = level.drill_last_health; /# if ( GetDvarInt( "scr_debugdrilldamage", 0 ) == 1 ) { if ( !isdefined( meansOfDeath ) ) meansOfDeath = "MOD_NONE"; if ( !isDefined( weapon ) ) weapon = "weapon_none"; if ( !isDefined( attacker ) ) atkr = "no_atkr"; else if ( isDefined( attacker ) && isDefined( attacker.model ) ) atkr = attacker.model; else atkr = "no_model"; println( "Drill damaged. MOD: " + meansOfDeath + " Wpn: " + weapon + " atkr: " + atkr + " dmg: " + amount + " remain: " + level.drill.health ); } #/ //PlayFxOnTag( level._effect[ "bomb_impact" ], level.drill, "tag_origin" ); maps\mp\alien\_gamescore::update_team_encounter_performance( maps\mp\alien\_gamescore::get_drill_score_component_name(), "drill_damage_taken", amount ); maps\mp\alien\_alien_matchdata::inc_drill_heli_damages( amount ); // Play alien hit sound for DLC4 conduits [IWSIX-186485] //if( IsDefined( attacker.team ) && attacker.team == "allies" ) if( level.script == "mp_alien_last" ) { if( isDefined( attacker ) && (( !IsDefined( attacker.team ) || attacker.team == "axis" ))) { //level notify("dlc_vo_notify", "conduit_attack"); self PlaySound("scn_dscnt_alien_pod_hit"); } } if ( level.drill.health < CONST_HEALTH_INVULNERABLE ) { maps\mp\alien\_hud::update_drill_health( 0 ); // offline level.drill notify( "offline", attacker, amount ); // this call ends this thread! aka return; } else { if ( !isdefined( self.icon ) ) { continue; } // color scales to red when health is low health_ratio = ( level.drill.health - CONST_HEALTH_INVULNERABLE ) / CONST_DRILL_HEALTH; health_ratio = max( 0, min( 1, health_ratio ) ); health_ratio_sqr= health_ratio * health_ratio; // shows red earlier green = health_ratio_sqr; blue = green; self.icon.color = ( 1, green, blue ); if ( use_alternate_drill() ) { hardcore_ratio = ( level.drill.health - CONST_HEALTH_INVULNERABLE ) / level.drill_health_hardcore; update_drill_health_HUD(); //maps\mp\alien\_hud::update_drill_health( int( hardcore_ratio * 100 ) ); if ( hardcore_ratio <= 0.75 && !repair_hint1_given ) { if ( isDefined( level.drill_repair_hint ) ) IPrintLnBold ( level.drill_repair_hint ); else iprintlnbold( &"ALIEN_COLLECTIBLES_DRILL_REPAIR_HINT" ); repair_hint1_given = true; } else if ( hardcore_ratio <= 0.5 && !repair_hint2_given ) { if ( isDefined( level.drill_repair_hint ) ) IPrintLnBold ( level.drill_repair_hint ); else iprintlnbold( &"ALIEN_COLLECTIBLES_DRILL_REPAIR_HINT" ); repair_hint2_given = true; } else if ( hardcore_ratio <= 0.25 && !repair_hint3_given) { if ( isDefined( level.drill_repair_hint_urgent ) ) IPrintLnBold ( level.drill_repair_hint_urgent ); else IPrintLnbold( &"ALIEN_COLLECTIBLES_REACT_DRILL" ); repair_hint3_given = true; } if ( hardcore_ratio <= 0.25 ) self thread sfx_overheat(); if ( hardcore_ratio < 0.5 && ( GetTime() - last_damage_time > DRILL_HEALTH_VO_SPAM_DELAY ) ) level thread maps\mp\alien\_music_and_dialog::playVOforDrillHot(); else if ( GetTime() - last_damage_time > DRILL_HEALTH_VO_SPAM_DELAY ) level thread maps\mp\alien\_music_and_dialog::playVOForDrillDamaged(); last_damage_time = GetTime(); } } } } sfx_overheat() { if ( !is_door() && !is_door_hive() && level.script != "mp_alien_dlc3") level.drill_sfx_lp StopLoopSound( "alien_laser_drill_lp" ); if (!IsDefined(level.drill_overheat_lp_02)) { level.drill_overheat_lp_02 = Spawn( "script_origin", level.drill.origin ); level.drill_overheat_lp_02 LinkTo(level.drill); // Turn off the normal loop, and play the damaged loop if( level.script == "mp_alien_last" ) { level.drill_sfx_lp StopLoopSound( "alien_conduit_on_lp" ); level.drill_overheat_lp_02 PlayLoopSound( "alien_conduit_damaged_lp" ); return; } } // FIXME: These should probably be moved up into the above if, but I dont want to modify previous levels if ( level.script == "mp_alien_dlc3" ) level.drill_overheat_lp_02 PlayLoopSound( "alien_drill_scanner_overheat_lp" ); else level.drill_overheat_lp_02 PlayLoopSound( "alien_laser_drill_overheat_lp" ); } drill_detonate() { if ( isDefined( level.drill_detonate_override ) ) { self thread [[level.drill_detonate_override]](); return; } // rid hive icon if active self maps\mp\alien\_hive::destroy_hive_icon(); self MakeUnusable(); self SetHintString( "" ); // to stop monitoring of drill layers if ( alien_mode_has( "outline" ) ) maps\mp\alien\_outline_proto::remove_from_outline_drill_watch_list ( level.drill ); if ( !is_door() && !is_door_hive() ) //only play the death FX for normal hives { // Stop SFX thread sfx_drill_off(false); // final kill sequence self thread kill_sequence(); } // delete flare fx if ( Isdefined( level.drill.fxEnt) ) level.drill.fxEnt Delete(); // delete laser fx if ( Isdefined( level.drill.fxLaserEnt) ) level.drill.fxLaserEnt Delete(); if( is_door()) { level notify( "drill_stop_door_fx" ); } // plays non operate anim level.drill ScriptModelClearAnim(); if( !is_door()) { level.drill ScriptModelPlayAnim( "alien_drill_end" ); wait 3.8; } if ( !isdefined( self.last_hive ) || !self.last_hive ) { org = level.drill.origin + ( 0, 0, 8 ); // this is the make sure drill trace starts above the ground drop_drill( org, self.angles - ( 0, 90, 0 ) ); // rotation offset because static model is not the same orientation as the animated model... why? } if ( is_door() || is_door_hive() ) { self delaythread( 3,::open_door ); } else self thread maps\mp\alien\_hive::delete_removables(); self thread remove_spawner(); self thread fx_ents_playfx(); self maps\mp\alien\_hive::show_dead_hive_model(); self thread do_radius_damage(); // To clean up any items stuck on top of hive // drill self destruct if last hive if ( isdefined( self.last_hive ) && self.last_hive ) { flag_set( "hives_cleared" ); level thread detonate_drill_when_nuke_goes_off( self ); } flag_clear( "drill_detonated" ); wait 8; level thread maps\mp\alien\_music_and_dialog::playVOForBombDetonate( self ); } do_radius_damage() { HIVE_EXPLODE_DAMAGE_RADIUS = 300; foreach ( scriptable in self.scriptables ) { RadiusDamage( scriptable.origin, HIVE_EXPLODE_DAMAGE_RADIUS, 0, 0, scriptable ); waitframe(); } } detonate_drill_when_nuke_goes_off( hive ) { if ( isdefined( level.drill ) ) { level.drill setCanDamage( false ); level.drill FreeEntitySentient(); level.drill MakeUnusable(); } level waittill( "nuke_went_off" ); wait 1.5; // padding // drill destroyed fx destroyed_fx = level._effect[ "stronghold_explode_med" ]; fx_loc = hive.origin; if ( isdefined( level.drill ) ) fx_loc = level.drill.origin; playfx( destroyed_fx, fx_loc ); if ( isdefined( level.drill ) ) { level.drill_carrier = undefined; level.drill delete(); } } kill_sequence() { PlayFX( level._effect[ "stronghold_explode_large" ], self.origin ); if (!is_door()) { self thread maps\mp\alien\_hive::sfx_destroy_hive(); } if ( isAlive( level.drill ) ) { foreach ( scriptable in self.scriptables ) { scriptable thread maps\mp\alien\_hive::hive_explode(1); waitframe(); } } } createUseEnt() { useEnt = Spawn( "script_origin", self.origin ); useEnt.curProgress = 0; useEnt.useTime = 0; useEnt.useRate = 1; useEnt.inUse = false; useEnt thread deleteUseEnt( self ); return useEnt; } deleteUseEnt( owner ) { self endon ( "death" ); owner waittill( "death" ); self delete(); } cancel_repair_on_hive_death( player ) { player endon( "disconnect" ); self notify ( "cancel_repair_on_hive_death" ); self endon( "cancel_repair_on_hive_death" ); level endon ( "drill_repaired" ); self waittill( "drill_complete" ); if ( isAlive( player ) ) { player notify( "drill_repair_weapon_management" ); if ( player.disabledWeapon > 0 ) player _enableWeapon(); if ( is_true ( player.hasprogressbar ) ) player.hasprogressbar = false; player.isRepairing = false; } } DRILL_USE_DISTANCE = 18496; // 136*136 useHoldThink( player, useTime ) { // if ( IsPlayer( player ) ) // player playerLinkTo( self ); // else // player LinkTo( self ); // player playerLinkedOffsetEnable(); self thread cancel_repair_on_hive_death( player ); self.curProgress = 0; self.inUse = true; self.useRate = 1; if ( IsDefined( useTime ) ) self.useTime = useTime; else self.useTime = 3000; //player _disableWeapon(); if( !player maps\mp\alien\_perk_utility::has_perk( "perk_rigger", [ 0,1,2,3,4 ] ) ) player disable_weapon_timeout( ( useTime + 0.05 ), "drill_repair_weapon_management" ); player thread personalUseBar( self ); player.hasprogressbar = true; result = useHoldThinkLoop( player, self, DRILL_USE_DISTANCE ); assert ( IsDefined( result ) ); if ( isAlive( player ) ) { player.hasprogressbar = false; if( !player maps\mp\alien\_perk_utility::has_perk( "perk_rigger", [ 0,1,2,3,4 ] ) ) player enable_weapon_wrapper( "drill_repair_weapon_management" ); } if ( !IsDefined( self ) ) return false; self.inUse = false; self.curProgress = 0; return ( result ); } personalUseBar( object ) { UI_DRILL_REPAIR = 2; if ( level.script == "mp_alien_last" ) UI_DRILL_REPAIR = 7; //indexes defined & explained in AlienCapturingHud.lua self endon( "disconnect" ); self SetClientOmnvar( "ui_securing",UI_DRILL_REPAIR ); lastRate = -1; while ( isReallyAlive( self ) && IsDefined( object ) && object.inUse && !level.gameEnded ) { if ( lastRate != object.useRate ) { if( object.curProgress > object.useTime) object.curProgress = object.useTime; } lastRate = object.useRate; self SetClientOmnvar( "ui_securing_progress",object.curProgress / object.useTime ); wait ( 0.05 ); } self SetClientOmnvar( "ui_securing_progress",0 ); self SetClientOmnvar( "ui_securing",0); } useHoldThinkLoop( player,ent, dist_check ) { while( !level.gameEnded && IsDefined( self ) && isReallyAlive( player ) && player useButtonPressed() && ( !isdefined( player.lastStand ) || !player.lastStand ) && self.curProgress < self.useTime ) { drill_health = ( self.health - CONST_HEALTH_INVULNERABLE ) / level.drill_health_hardcore; if ( drill_health <= 0 ) return false; if ( isDefined ( ent ) && isDefined ( dist_check) ) { if ( distancesquared ( player.origin,ent.origin ) > dist_check ) { return false; } } self.curProgress += (50 * self.useRate); self.useRate = 1; if ( self.curProgress >= self.useTime ) return ( isReallyAlive( player ) ); wait 0.05; } return false; } // ================= utilities ================== draw_line( from, to, color, frames ) { //level endon( "helicopter_done" ); if( isdefined( frames ) ) { for( i=0; i 45 ) { self.angles = ( self.angles[0], self.angles[1], 0 ); } if ( abs ( self.angles[0] ) > 45 ) { self.angles = ( 0, self.angles[1], self.angles[2] ); } self.origin = pos + offset; } //============================================================================== // Bomb viewmodel carry and replace weapons when bomb is planted or dropped //============================================================================== // watchBomb() { level endon( "game_ended" ); self endon( "death" ); self endon( "disconnect" ); // remove bomb on player spawn, no way they can spawn with one by default currently if ( self hasweapon( "alienbomb_mp" ) ) { self TakeWeapon( "alienbomb_mp" ); self EnableWeaponSwitch(); } while ( 1 ) { self waittill( "grenade_fire", alienbomb, weapname ); if ( weapname == "alienbomb" || weapname == "alienbomb_mp" ) { alienbomb.owner = self; alienbomb SetOtherEnt(self); alienbomb.team = self.team; alienbomb thread watchBombStuck( self ); } } } //Watcher for the dropped bomb to stick then spawns a new script_model bomb at the location of the dropped bomb watchBombStuck( owner ) { // self == alienbomb projectile level endon( "game_ended" ); //self endon( "death" ); // if deleted or died owner endon( "death" ); owner endon( "disconnect" ); self Hide(); self waittill_any_timeout( .05, "missile_stuck" ); //try to put it on the ground //secTrace = bulletTrace( owner.origin + (0,0,8 ) , owner.origin - (0, 0, 16 ), false, self , true ,false, true ); secTrace = self AIPhysicsTrace ( owner.origin + ( 0,0,8 ),owner.origin - ( 0,0,12 ),undefined,undefined,true,true ); if( secTrace["fraction"] == 1 ) { owner TakeWeapon( "alienbomb_mp" ); owner GiveWeapon( "alienbomb_mp" ); // there's nothing under us so don't place the drill up in the air owner SetWeaponAmmoStock( "alienbomb_mp", owner GetWeaponAmmoStock( "alienbomb_mp" ) + 1 ); owner SwitchToWeapon( "alienbomb_mp" ); self delete(); return; } else { self.origin = secTrace["position"]; parent = secTrace["entity"]; } self.angles *= (0,1,1); //self.origin = self.origin; level notify( "drill_dropping" ); // auto plant if dropped close to target foreach ( destroy_loc in level.stronghold_hive_locs ) { if ( destroy_loc maps\mp\alien\_hive::is_blocker_hive() ) continue; if ( !destroy_loc maps\mp\alien\_hive::dependent_hives_removed() ) continue; if ( distance( destroy_loc.origin, self.origin ) < 80 ) { // auto plant destroy_loc notify( "trigger", owner ); Earthquake( .25,.5,self.origin,128 ); owner TakeWeapon( "alienbomb_mp" ); if ( !owner has_special_weapon() ) owner EnableWeaponSwitch(); owner restore_last_weapon(); owner _enableOffhandWeapons(); self delete(); return; } } if ( isDefined( level.watch_bomb_stuck_override ) ) //for checking when the drill is dropped near *other* things that may not be a hive struct ( like the platform bot in Beacon ) { if ( [[level.watch_bomb_stuck_override]]( owner ) ) return; } drop_drill( self.origin, self.angles, self ); Earthquake( .25,.5,self.origin,128 ); owner TakeWeapon( "alienbomb_mp" ); if ( !owner has_special_weapon() ) owner EnableWeaponSwitch(); owner restore_last_weapon(); owner _enableOffhandWeapons(); level thread maps\mp\alien\_outline_proto::update_drill_outline(); self delete(); } restore_last_weapon() { if ( self.lastweapon != "aliendeployable_crate_marker_mp" ) self SwitchToWeapon( self.lastweapon ); else self SwitchToWeapon ( self GetWeaponsListPrimaries()[0] ); } //======================================================= // HUD - Icons //======================================================= // player bomb carry init on player spawn - No longer used TODO : Remove this player_carry_bomb_init() { // self is player if ( !isdefined( self.carryIcon ) ) { if ( level.splitscreen ) { self.carryIcon = createIcon( "hud_suitcase_bomb", 33, 33 ); self.carryIcon setPoint( "BOTTOM RIGHT", "BOTTOM RIGHT", -50, -78 ); } else { self.carryIcon = createIcon( "hud_suitcase_bomb", 50, 50 ); self.carryIcon setPoint( "BOTTOM RIGHT", "BOTTOM RIGHT", -50, -65 ); } self.carryIcon.hidewheninmenu = true; self thread hideCarryIconOnGameEnd(); } self.carryIcon.alpha = 0; } hideCarryIconOnGameEnd() { self endon( "disconnect" ); level waittill( "game_ended" ); if ( isDefined( self.carryIcon ) ) self.carryIcon.alpha = 0; } set_drill_icon( link ) { level notify( "new_bomb_icon" ); // destroy in case we used to link destroy_drill_icon( self ); if ( !isDefined ( link ) || !link ) { level.drill_icon = NewHudElem(); level.drill_icon SetShader( "waypoint_alien_drill", 14, 14 ); level.drill_icon.color = ( 1, 1, 1 ); level.drill_icon SetWayPoint( true, true ); level.drill_icon.sort = 1; level.drill_icon.foreground = true; level.drill_icon.alpha = 0.5; level.drill_icon.x = self.origin[ 0 ]; level.drill_icon.y = self.origin[ 1 ]; level.drill_icon.z = self.origin[ 2 ] + 72; } else { self maps\mp\_entityheadIcons::setHeadIcon( self.team, "waypoint_alien_drill", (0,0,72), 4, 4, undefined, undefined, undefined, true, undefined, false ); } } destroy_drill_icon( ent ) { if ( isdefined( level.drill_icon ) ) level.drill_icon Destroy(); if ( !isDefined ( ent ) ) return; remove_headicons_from_players(); } remove_headicons_from_players() { foreach ( player in level.players ) { if ( isDefined ( player.entityHeadIcons ) ) { foreach ( key, headIcon in player.entityHeadIcons ) //remove any head icons { // TODO: remove and fix properly after ship if( !isDefined(headIcon) ) //needed for FFA host migration (when host has active head icons) continue; headIcon destroy(); } } } } //======================================================= // Helpers //======================================================= remove_spawner() { if ( IsDefined( self.script_linkto ) ) maps\mp\alien\_spawn_director::remove_spawn_location( self.script_linkto ); } fx_ents_playfx() { // self is stronghold_loc script model assert( isdefined( self.fx_ents ) ); foreach ( fx_ent in self.fx_ents ) { playfx( level._effect[ "stronghold_explode_med" ], fx_ent.origin ); fx_ent delete(); } } //======================================================= // Audio //======================================================= sfx_drill_plant() { drill = get_drill_entity(); //wait 0.1; drill PlaySound( "alien_laser_drill_plant" ); } sfx_drill_on(door) { wait 0.1; drill = get_drill_entity(); if (!IsDefined(level.drill_sfx_lp)) { level.drill_sfx_lp = Spawn( "script_origin", drill.origin ); level.drill_sfx_lp LinkTo(drill); } if (!IsDefined(level.drill_sfx_dist_lp)) { level.drill_sfx_dist_lp = Spawn( "script_origin", drill.origin ); level.drill_sfx_dist_lp LinkTo( drill ); } wait 0.1; if (door) { wait 3.76; //IPrintLnBold("Drilling a door"); if ( isDefined ( level.drill_sfx_lp ) ) level.drill_sfx_lp PlayLoopSound("alien_laser_drill_door_lp"); if ( isDefined ( level.drill_sfx_dist_lp ) ) level.drill_sfx_dist_lp PlayLoopSound("alien_laser_drill_door_dist_lp"); } else { //IPrintLnBold("Drilling a hive"); if ( isDefined ( level.drill_sfx_lp ) ) level.drill_sfx_lp PlayLoopSound("alien_laser_drill_lp"); if ( isDefined ( level.drill_sfx_dist_lp ) ) level.drill_sfx_dist_lp PlayLoopSound("alien_laser_drill_dist_lp"); } } sfx_drill_off(door) { drill = get_drill_entity(); coord = drill.origin; if (!door) drill PlaySound("alien_laser_drill_stop"); else playSoundAtPos( coord, "alien_laser_drill_stop"); if (IsDefined(level.drill_sfx_lp)) level.drill_sfx_lp delete(); if (IsDefined(level.drill_sfx_dist_lp)) level.drill_sfx_dist_lp delete(); if (IsDefined(level.drill_overheat_lp)) level.drill_overheat_lp delete(); if (IsDefined(level.drill_overheat_lp_02)) level.drill_overheat_lp_02 delete(); if (door) { wait 2.7; playSoundAtPos( coord, "alien_laser_drill_door_open"); } } sfx_drill_offline() { drill = get_drill_entity(); if ( level.script == "mp_alien_dlc3" ) level.drill PlaySound("alien_drill_scanner_shutdown"); else drill PlaySound("alien_laser_drill_shutdown"); if (IsDefined(level.drill_sfx_lp)) level.drill_sfx_lp delete(); if (IsDefined(level.drill_sfx_dist_lp)) level.drill_sfx_dist_lp delete(); if (IsDefined(level.drill_overheat_lp_02)) level.drill_overheat_lp_02 delete(); } // =============================================================================== // Black box prints for drill // =============================================================================== drill_plant_BBprint( player ) { // self is hive struct self drill_generic_BBPrint( "aliendrillplant", player ); } drill_reset_BBPrint( player ) { // self is hive struct self drill_generic_BBPrint( "aliendrillreset", player ); } drill_generic_BBPrint( BBprint_string, player ) { // self is hive struct cyclenum = level.current_cycle_num; hivename = "unknown hive"; if ( isdefined( self.target ) ) hivename = self.target; playtime = gettime() - level.startTime; planter = "unknown player"; if ( isdefined( player.name ) ) planter = player.name; playernum = level.players.size; planterperk0 = player maps\mp\alien\_persistence::get_selected_perk_0(); planterperk0level = player maps\mp\alien\_persistence::get_perk_0_level(); planterperk1 = player maps\mp\alien\_persistence::get_selected_perk_1(); planterperk1level = player maps\mp\alien\_persistence::get_perk_1_level(); healthratio = -1; if ( isdefined( level.drill ) && isdefined( level.drill.health ) && isdefined( level.drill_health_hardcore ) ) healthratio = ( level.drill.health - CONST_HEALTH_INVULNERABLE ) / level.drill_health_hardcore; /# if ( GetDvarInt( "alien_bbprint_debug" ) > 0 ) { IPrintLnBold( "^8bbprint: " + BBprint_string + "\n" + " cyclenum=" + cyclenum + " hivename=" + hivename + " playtime=" + playtime + " drillhealth=" + healthratio + " repairer=" + planter + " repairerperk0=" + planterperk0 + " repairerperk1=" + planterperk1 + " repairerperk0level=" + planterperk0level + " repairerperk1level=" + planterperk1level + " playernum=" + playernum ); } #/ bbprint( BBprint_string, "cyclenum %i hivename %s playtime %f drillhealth %f repairer %s repairerperk0 %s repairerperk1 %s repairerperk0level %s repairerperk1level %s playernum %i ", cyclenum, hivename, playtime, healthratio, planter, planterperk0, planterperk1, planterperk0level, planterperk1level, playernum ); } check_for_player_near_hive_with_drill() { /# if ( alien_mode_has( "nogame" ) ) return; #/ if ( is_true ( level.automatic_drill ) ) //break out of this if not needed return; self endon( "disconnect" ); check_distance = 80*80; while ( 1 ) { while ( !flag( "drill_drilling" ) ) { if ( ( isDefined ( self.inlaststand ) && self.inlaststand ) || flag ( "drill_drilling" ) || isDefined ( self.usingRemote ) || is_true ( self.isCarrying ) ) { wait ( .05 ); continue; } foreach ( destroy_loc in level.stronghold_hive_locs ) { if ( destroy_loc maps\mp\alien\_hive::is_blocker_hive() ) continue; if ( !destroy_loc maps\mp\alien\_hive::dependent_hives_removed() ) continue; if ( distancesquared( destroy_loc.origin, self.origin ) < check_distance ) { if ( ( !isDefined ( level.drill_carrier ) ) || isDefined ( level.drill_carrier ) && level.drill_carrier != self ) { self setLowerMessage( "need_drill",&"ALIEN_COLLECTIBLES_NEED_DRILL",undefined,10 ); while( player_should_see_drill_hint( destroy_loc,check_distance,true ) ) { wait ( .05 ); } self clearLowerMessage ( "need_drill" ); } else { self setLowerMessage( "plant_drill",&"ALIEN_COLLECTIBLES_PLANT_BOMB",undefined,10 ); while( player_should_see_drill_hint( destroy_loc,check_distance,false ) ) { wait ( .05 ); } self clearLowerMessage( "plant_drill" ); } } } wait ( .05 ); } flag_waitopen( "drill_drilling" ); } } player_should_see_drill_hint( destroy_loc,check_distance, ignore_carrying_check ) { if ( distancesquared( destroy_loc.origin, self.origin ) > check_distance ) return false; if ( flag ( "drill_drilling" ) ) return false; if ( self.inlaststand ) return false; if ( isDefined ( self.usingRemote ) ) return false; if ( is_true ( ignore_carrying_check ) ) return true; else if ( is_true ( self.isCarrying ) ) return false; return true; } get_drill_entity() { if ( isDefined( level.drill.vehicle ) ) return level.drill.vehicle; else return level.drill; } open_door() { level notify( "door_opening" , self.target ); foreach ( ent in self.removeables ) { if ( isdefined( ent ) ) { if( ent.classname == "script_model" ) { ent thread slide_open(); } else { if( ent.classname == "script_brushmodel" ) { ent ConnectPaths(); } ent delete(); } } } } slide_open() { if ( !isDefined( self.script_angles ) ) self delete(); else self moveto ( self.origin + self.script_angles, 1 ); } wait_for_drill_plant() { // self is hive location struct, where we allow planting of drill self endon( "stop_listening" ); //self MakeUsable(); //self SetCursorHint( "HINT_NOICON" ); //self SetHintString( "" ); while( true ) { self waittill( "trigger", player ); if ( !is_true ( level.automatic_drill ) && ( !isdefined( level.drill_carrier ) || level.drill_carrier != player ) ) { player setLowerMessage( "no_bomb", &"ALIEN_COLLECTIBLES_NO_BOMB", 5 ); wait 0.05; continue; } if( isPlayer( player ) ) { if( !is_true ( level.automatic_drill ) ) { // clear lower message hint to plant bomb player clearLowerMessage( "go_plant" ); // remove the bomb and give the player back his last weapon player TakeWeapon( "alienbomb_mp" ); if ( !player has_special_weapon() ) { player EnableWeaponSwitch(); } if(!isdefined(level.non_player_drill_plant_check) || ![[level.non_player_drill_plant_check]]()) { player SwitchToWeapon( player.lastweapon ); } self MakeUnusable(); self SetHintString( "" ); maps\mp\alien\_drill::remove_headicons_from_players(); } earthquake_intensity = 0.4; warn_delay = 1.75; thread maps\mp\alien\_hive::warn_all_players( warn_delay, earthquake_intensity ); // EoG tracking: drill plants player maps\mp\alien\_persistence::eog_player_update_stat( "drillplants", 1 ); level notify( "drill_planted", player, self ); return player; } } } update_drill_health_HUD() { drill_health = int( ( level.drill.health - CONST_HEALTH_INVULNERABLE ) / level.drill_health_hardcore * 100 ); maps\mp\alien\_hud::update_drill_health( drill_health ); }