#using scripts\codescripts\struct; #using scripts\shared\ai_shared; #using scripts\shared\ai_puppeteer_shared; #using scripts\shared\callbacks_shared; #using scripts\shared\challenges_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\flagsys_shared; #using scripts\shared\killstreaks_shared; #using scripts\shared\objpoints_shared; #using scripts\shared\scoreevents_shared; #using scripts\shared\ai\archetype_utility; #using scripts\shared\ai\systems\gib; #using scripts\shared\entityheadicons_shared; #using scripts\shared\util_shared; #using scripts\shared\vehicleriders_shared; #using scripts\shared\weapons\_heatseekingmissile; #using scripts\shared\ai\systems\blackboard; #using scripts\mp\_challenges; #using scripts\mp\gametypes\_globallogic_audio; #using scripts\mp\killstreaks\_killstreak_bundles; #using scripts\mp\killstreaks\_killstreak_detect; #using scripts\mp\killstreaks\_killstreak_hacking; #using scripts\mp\killstreaks\_killstreakrules; #using scripts\mp\killstreaks\_killstreaks; #using scripts\mp\killstreaks\_supplydrop; #namespace combat_robot; // Tweaks to how the combat robot's body is thrown after exploding // Scales the initial velocity // Time in seconds the combat robot will shutdown before exploding. // The combat robot will give up chasing an enemy if they haven't attacked them for this long. // The combat robot will ignore unattackable enemies for this long. #precache( "string", "KILLSTREAK_COMBAT_ROBOT_ESCORT_HINT" ); #precache( "string", "KILLSTREAK_COMBAT_ROBOT_GUARD_HINT" ); #precache( "string", "KILLSTREAK_COMBAT_ROBOT_INBOUND" ); #precache( "string", "KILLSTREAK_COMBAT_ROBOT_NOT_AVAILABLE" ); #precache( "string", "KILLSTREAK_COMBAT_ROBOT_HACKED" ); #precache( "string", "KILLSTREAK_COMBAT_ROBOT_PATROL_FAIL" ); #precache( "string", "KILLSTREAK_DESTROYED_COMBAT_ROBOT" ); #precache( "triggerstring", "KILLSTREAK_COMBAT_ROBOT_ESCORT_HINT" ); #precache( "triggerstring", "KILLSTREAK_COMBAT_ROBOT_GUARD_HINT" ); #precache( "material", "t7_hud_ks_c54i_drop" ); function init() { killstreaks::register( "combat_robot", "combat_robot_marker", "killstreak_" + "combat_robot", "combat_robot" + "_used", &ActivateCombatRobot, undefined, true ); killstreaks::register_alt_weapon( "combat_robot", "lmg_light_robot" ); killstreaks::register_strings( "combat_robot", &"KILLSTREAK_COMBAT_ROBOT_EARNED", &"KILLSTREAK_COMBAT_ROBOT_NOT_AVAILABLE", &"KILLSTREAK_COMBAT_ROBOT_INBOUND", undefined, &"KILLSTREAK_COMBAT_ROBOT_HACKED" ); killstreaks::register_dialog( "combat_robot", "mpl_killstreak_combat_robot", "combatRobotDialogBundle", "combatRobotPilotDialogBundle", "friendlyCombatRobot", "enemyCombatRobot", "enemyCombatRobotMultiple", "friendlyCombatRobotHacked", "enemyCombatRobotHacked", "requestCombatRobot", "threatCombatRobot" ); // TODO: Move to killstreak data level.killstreaks["inventory_combat_robot"].threatOnKill = true; level.killstreaks["combat_robot"].threatOnKill = true; level thread _CleanupRobotCorpses(); } function private _CalculateProjectedGuardPosition( player ) { // Find the closest navmesh position projected out from the reticle. //forwardVector = VectorScale( AnglesToForward( player GetPlayerAngles() ), 4000 ); //guardPoint = BulletTrace( player GetEye(), player GetEye() + forwardVector, true, player ); //return GetClosestPointOnNavMesh( guardPoint["position"], 48 ); return GetClosestPointOnNavMesh( player.origin, 48 ); } function private _CalculateRobotSpawnPosition( player ) { desiredSpawnPosition = AnglesToForward( player.angles ) * 72 + player.origin; return GetClosestPointOnNavMesh( desiredSpawnPosition, 48 ); } function private _CleanupRobotCorpses() { corpseDeleteTime = 15000; while ( true ) { deleteCorpses = []; foreach ( corpse in GetCorpseArray() ) { if ( IsDefined( corpse.birthtime ) && IsDefined( corpse.archetype ) && corpse.archetype == "robot" && ( corpse.birthtime + corpseDeleteTime ) < GetTime() ) { deleteCorpses[ deleteCorpses.size ] = corpse; } } for ( index = 0; index < deleteCorpses.size; index++ ) { deleteCorpses[ index ] Delete(); } wait ( corpseDeleteTime / 1000 ) / 2; } } function ConfigureTeamPost( player, isHacked ) { robot = self; robot.properName = ""; // Prevent the robot from being damaged based on a hurt trigger when being called in. robot.ignoreTriggerDamage = true; robot.empShutdownTime = ( 750 ); robot.minWalkDistance = 60; robot.superSprintDistance = 180; robot.robotRusherMinRadius = 64; robot.robotRusherMaxRadius = 120; robot.allowPushActors = false; robot.chargeMeleeDistance = 0; // Disable charge melee, more effective to shoot. robot.fovcosine = 0; // 360 degree field of view robot.fovcosinebusy = 0; // 360 degree field of view even when busy robot.MaxSightDistSqrd = ( (2000) * (2000) ); Blackboard::SetBlackBoardAttribute( robot, "_robot_mode", "combat" ); // Disable head gibbing. robot.gib_state = (0 | ( 8 & ( ( 1 << 9 ) - 1 ) )); robot clientfield::set( "gib_state", robot.gib_state ); _ConfigureRobotTeam( robot, player, isHacked ); robot ai::set_behavior_attribute( "can_become_crawler", false ); robot ai::set_behavior_attribute( "can_be_meleed", false ); robot ai::set_behavior_attribute( "can_initiateaivsaimelee", false ); robot ai::set_behavior_attribute( "supports_super_sprint", true ); } function private _ConfigureRobotTeam( robot, player, isHacked ) { if ( isHacked ) { lightsState = 3; } else { lightsState = 0; } robot ai::set_behavior_attribute( "robot_lights", lightsState ); robot thread WatchCombatRobotOwnerDisconnect( player ); if ( !isdefined( robot.objective ) ) { robot.objective = GetEquipmentHeadObjective( GetWeapon( "combat_robot_marker" ) ); } robot thread _WatchModeSwap( robot, player ); robot thread _Underwater( robot ); } function private _CreateGuardMarker( robot, position ) { owner = robot.owner; guardMarker = spawn( "script_model", ( 0, 0, 0 ) ); guardMarker.origin = position; guardMarker entityheadicons::setEntityHeadIcon( owner.pers["team"], owner, undefined, &"airdrop_combatrobot" ); return guardMarker; } function private _DestroyGuardMarker( robot ) { if ( isdefined( robot.guardMarker ) ) { robot.guardMarker delete(); } } function private _Underwater( robot ) { robot endon( "death" ); while ( true ) { if ( ( robot.origin[2] + 72 / 2.0 ) <= GetWaterHeight( robot.origin ) ) { robot ASMSetAnimationRate( 0.85 ); } else { robot ASMSetAnimationRate( 1.0 ); } wait 0.1; } } function private _Escort( robot ) { robot endon( "death" ); robot.escorting = true; robot.guarding = false; _DestroyGuardMarker( robot ); while ( robot.escorting ) { attackingEnemy = false; if ( IsDefined( robot.enemy ) && IsAlive( robot.enemy ) ) { if ( ( robot LastKnownTime( robot.enemy ) + 10000 ) >= GetTime() ) { robot ai::set_behavior_attribute( "move_mode", "rusher" ); attackingEnemy = true; } else { robot ClearEnemy(); } } if ( !attackingEnemy && IsDefined( robot.owner ) && IsAlive( robot.owner ) ) { lookAheadTime = 1.0; predicitedPosition = robot.owner.origin + VectorScale( robot.owner GetVelocity(), lookAheadTime ); robot ai::set_behavior_attribute( "escort_position", predicitedPosition ); robot ai::set_behavior_attribute( "move_mode", "escort" ); } wait 1; } } function private _IgnoreUnattackableEnemy( robot, enemy ) { robot endon( "death" ); robot SetIgnoreEnt( enemy, true ); wait 5000 / 1000; robot SetIgnoreEnt( enemy, false ); } function private _GuardPosition( robot, position ) { robot endon( "death" ); robot.goalradius = 1000; robot SetGoal( position ); robot.escorting = false; robot.guarding = true; _DestroyGuardMarker( robot ); robot.guardMarker = _CreateGuardMarker( robot, position ); while ( robot.guarding ) { attackingEnemy = false; if ( IsDefined( robot.enemy ) && IsAlive( robot.enemy ) ) { if ( ( robot LastKnownTime( robot.enemy ) + 10000 ) >= GetTime() ) { // Robot still within goalradius, continue pursuit. robot ai::set_behavior_attribute( "move_mode", "rusher" ); attackingEnemy = true; } else { robot ClearEnemy(); } } if ( !attackingEnemy ) { robot ai::set_behavior_attribute( "move_mode", "guard" ); } wait 1; } } function _WatchModeSwap( robot, player ) { robot endon( "death" ); nextSwitchTime = GetTime(); while ( true ) { {wait(.05);}; if( !isdefined( robot.useTrigger ) ) continue; robot.useTrigger waittill( "trigger" ); if ( nextSwitchTime <= GetTime() && IsAlive( player ) ) { if ( ( isdefined( robot.guarding ) && robot.guarding ) ) { robot.guarding = false; robot.escorting = true; player playsoundtoplayer( "uin_mp_combat_bot_escort", player ); robot thread _Escort( robot ); if( isdefined( robot.useTrigger ) ) robot.useTrigger SetHintString( &"KILLSTREAK_COMBAT_ROBOT_GUARD_HINT" ); if( isdefined( robot.markerFXHandle ) ) robot.markerFXHandle delete(); } else { navGuardPosition = _CalculateProjectedGuardPosition( player ); if ( IsDefined( navGuardPosition ) ) { robot.guarding = true; robot.escorting = false; player playsoundtoplayer( "uin_mp_combat_bot_guard", player ); robot thread _GuardPosition( robot, navGuardPosition ); if( isdefined( robot.useTrigger ) ) robot.useTrigger SetHintString( &"KILLSTREAK_COMBAT_ROBOT_ESCORT_HINT" ); if( isdefined( robot.markerFXHandle ) ) robot.markerFXHandle delete(); params = level.killstreakBundle["combat_robot"]; if( isdefined( params.ksCombatRobotPatrolFX ) ) { point = player.origin; if( !isdefined( point ) ) point = navGuardPosition; robot.markerFXHandle = SpawnFx( params.ksCombatRobotPatrolFX, point + ( 0, 0, 3 ), ( 0, 0, 1 ), ( 1, 0, 0 ) ); robot.markerFXHandle.team = player.team; TriggerFX( robot.markerFXHandle ); robot.markerFXHandle SetInvisibleToAll(); robot.markerFXHandle SetVisibleToPlayer( player ); } } else { player iPrintLnBold( &"KILLSTREAK_COMBAT_ROBOT_PATROL_FAIL" ); } } robot notify("bhtn_action_notify", "modeSwap"); nextSwitchTime = GetTime() + 1000; } } } function ActivateCombatRobot( killstreak ) { player = self; team = self.team; if( !self supplydrop::isSupplyDropGrenadeAllowed( killstreak ) ) { return false; } killstreak_id = self killstreakrules::killstreakStart( killstreak, team, false, false ); if ( killstreak_id == -1 ) { return false; } context = SpawnStruct(); context.prolog = &Prolog; context.epilog = &Epilog; context.hasFlares = 1; context.radius = level.killstreakCoreBundle.ksAirdropRobotRadius; context.dist_from_boundary = 18; context.max_dist_from_location = 4; context.perform_physics_trace = true; context.drop_from_goal_distance2d = 96; // combat robot doesn't need this value to be strict (note: drop ship related) context.isLocationGood = &supplydrop::IsLocationGood; context.objective = &"airdrop_combatrobot"; context.killstreakRef = killstreak; context.validLocationSound = level.killstreakCoreBundle.ksValidCombatRobotLocationSound; context.vehiclename = "combat_robot_dropship"; context.killstreak_id = killstreak_id; context.tracemask = (1 << 0) | (1 << 2); // This offset is specific to the exit vtol animation of the combat rider. context.dropOffset = (0, -120, 0); result = self supplydrop::useSupplyDropMarker( killstreak_id, context ); if ( !isdefined(result) || !result ) { killstreakrules::killstreakStop( killstreak, team, killstreak_id ); return false; } self killstreaks::play_killstreak_start_dialog( "combat_robot", self.team, killstreak_id ); self killstreakrules::displayKillstreakStartTeamMessageToAll( "combat_robot" ); self AddWeaponStat( GetWeapon( "combat_robot_marker" ), "used", 1 ); return result; } function DropKillThread() { robot = self; robot endon( "death" ); robot endon( "combat_robot_land" ); while( true ) { robot supplydrop::is_touching_crate(); robot supplydrop::is_clone_touching_crate(); {wait(.05);}; } } function WatchHelicopterDeath( context ) { helicopter = self; helicopter waittill( "death" ); callback::callback( #"on_vehicle_killed" ); if( isdefined( context.marker ) ) { context.marker delete(); context.marker = undefined; if( isdefined( context.markerFXHandle ) ) { context.markerFXHandle delete(); context.markerFXHandle = undefined; } supplydrop::DelDropLocation( context.killstreak_id ); } } function Prolog( context ) { helicopter = self; player = helicopter.owner; spawnPosition = ( 0,0,0 ); spawnAngles = ( 0,0,0 ); combatRobot = SpawnActor( "spawner_bo3_robot_grunt_assault_mp", spawnPosition, spawnAngles, "", true ); combatRobot.missileTrackDamage = 0; combatRobot killstreaks::configure_team( "combat_robot", context.killstreak_id, player, "small_vehicle", undefined, &ConfigureTeamPost ); combatRobot killstreak_hacking::enable_hacking( "combat_robot", undefined, &HackedCallbackPost ); combatRobot thread _Escort( combatRobot ); combatRobot thread WatchCombatRobotHelicopterHacked( helicopter ); combatRobot thread WatchCombatRobotShutdown(); combatRobot thread WatchCombatRobotDeath(); combatRobot thread killstreaks::WaitForTimeout( "combat_robot", ( 90000 ), &OnCombatRobotTimeout, "combat_robot_shutdown" ); combatRobot thread sndWatchCombatRobotVoxNotifies(); //combatRobot thread debugThread(); helicopter thread WatchHelicopterDeath( context ); helicopter.unloadTimeout = 6; killstreak_detect::killstreakTargetSet( combatRobot, ( 0, 0, 50 ) ); combatRobot.maxhealth = combatRobot.health; tableHealth = killstreak_bundles::get_max_health( "combat_robot" ); if ( isdefined( tableHealth ) ) { combatRobot.maxhealth = tableHealth; } combatRobot.health = combatRobot.maxhealth; combatRobot.treat_owner_damage_as_friendly_fire = true; combatRobot.ignore_team_kills = true; combatRobot.remoteMissileDamage = combatRobot.maxhealth + 1; combatRobot.rocketDamage = combatRobot.maxhealth / 2 + 1; combatRobot thread heatseekingmissile::MissileTarget_ProximityDetonateIncomingMissile("death"); combatRobot clientfield::set( "enemyvehicle", 1 ); combatRobot.soundmod = "drone_land"; AiUtility::AddAIOverrideDamageCallback( combatRobot, &combatRobotDamageOverride ); combatRobot.vehicle = helicopter; combatRobot.vehicle.ignore_seat_check = true; combatRobot vehicle::get_in( helicopter , "driver", true ); combatRobot.overrideDropPosition = player.markerPosition; combatRobot thread WatchCombatRobotLanding(); combatRobot thread sndWatchExit(); combatRobot thread sndWatchLanding(); combatRobot thread sndWatchActivate(); foreach( player in level.players ) { combatRobot respectNotTargetedByRobotPerk( player ); } callback::on_spawned( &respectNotTargetedByRobotPerk, combatRobot ); context.robot = combatRobot; } function respectNotTargetedByRobotPerk( player ) { combatRobot = self; combatRobot setignoreent( player, player hasperk( "specialty_nottargetedbyrobot" ) ); } function Epilog( context ) { helicopter = self; context.robot thread DropKillThread(); context.robot.startTime = GetTime() + ( 750 ); // set killcam to start a time offset from when drop ship arrives thread CleanupThread( context ); /# debug_delay_robot_deploy(); #/ helicopter WaitThenSetDeleteAfterDestructionWaitTime( 0.8, (isdefined(self.unloadTimeout)?self.unloadTimeout:0) + 0.1 ); helicopter vehicle::unload( "all", undefined, true, 0.8 ); // removes robot as rider so that it doesn't } /# function debug_delay_robot_deploy() { seconds_to_wait = GetDvarInt( "scr_combat_robot_wait_to_deploy", 0 ); // 14 seconds is good to shoot down deploy ship given 3 flares and 2 rockets to kill and sustained ammo while ( seconds_to_wait > 0 ) { IPrintLnBold( "Remaining time: " + seconds_to_wait ); wait 1; seconds_to_wait--; if ( seconds_to_wait == 0 ) IPrintLnBold( "Jump!!" ); } } #/ function WaitThenSetDeleteAfterDestructionWaitTime( set_wait_time, delete_after_destruction_wait_time ) { wait set_wait_time; if ( isdefined( self ) ) { self.delete_after_destruction_wait_time = delete_after_destruction_wait_time; } } function HackedCallbackPost( hacker ) { robot = self; robot ClearEnemy(); robot SetupCombatRobotHintTrigger( hacker ); } function WatchCombatRobotHelicopterHacked( helicopter ) { robot = self; robot endon( "death" ); robot endon( "killstreak_hacked" ); robot endon( "combat_robot_land" ); helicopter endon( "death" ); helicopter waittill( "killstreak_hacked", hacker ); if( robot flagsys::get( "in_vehicle" ) == false ) return; robot [[ robot.killstreak_hackedCallback ]]( hacker ); } function CleanupThread( context ) { robot = context.robot; while( isdefined( robot ) && isdefined( context.marker ) && ( robot flagsys::get( "in_vehicle" ) ) ) { wait 1; } if( isdefined( context.marker ) ) { context.marker delete(); context.marker = undefined; if( isdefined( context.markerFXHandle ) ) { context.markerFXHandle delete(); context.markerFXHandle = undefined; } supplydrop::DelDropLocation( context.killstreak_id ); } } function WatchCombatRobotDeath() { combatRobot = self; combatRobot endon( "combat_robot_shutdown" ); callback::remove_on_spawned( &respectNotTargetedByRobotPerk, combatRobot ); combatRobot waittill( "death", attacker, damageFromUnderneath, weapon ); // combatRobot waittill( "death", attacker, damageFromUnderneath, weapon, point, dir, modType ); attacker = self [[ level.figure_out_attacker ]]( attacker ); if ( isdefined( attacker ) && IsPlayer( attacker ) && ( !isdefined( combatRobot.owner ) || combatRobot.owner util::IsEnemyPlayer( attacker ) ) ) { attacker challenges::destroyScoreStreak( weapon, false, true ); attacker challenges::destroyNonAirScoreStreak_PostStatsLock( weapon ); scoreevents::processScoreEvent( "destroyed_combat_robot", attacker, combatRobot.owner, weapon ); LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_COMBAT_ROBOT", attacker.entnum ); } combatRobot killstreaks::play_destroyed_dialog_on_owner( "combat_robot", combatRobot.killstreak_id ); combatRobot notify( "combat_robot_shutdown" ); } function WatchCombatRobotLanding() { robot = self; robot endon( "death" ); robot endon( "combat_robot_shutdown" ); // wait for landing while( robot flagsys::get( "in_vehicle" ) ) { wait 1; } robot notify( "combat_robot_land" ); robot.ignoreTriggerDamage = false; // only check if on nav mesh after finishing traversals while ( isdefined( robot.traverseStartNode ) ) { robot waittill( "traverse_end" ); } v_on_navmesh = GetClosestPointOnNavMesh( robot.origin, 50, 20 ); ///#sphere( robot.origin, 5, ( 1, 0, 0 ), 1, true, 10, 200 );#/ if ( isdefined ( v_on_navmesh ) ) { player = robot.owner; robot SetupCombatRobotHintTrigger( player ); } else { robot notify( "combat_robot_shutdown" ); } } function SetupCombatRobotHintTrigger( player ) { robot = self; if ( isdefined( robot.useTrigger ) ) { robot.useTrigger delete(); } robot.useTrigger = spawn( "trigger_radius_use", player.origin, 32, 32 ); robot.useTrigger EnableLinkTo(); robot.useTrigger LinkTo( player ); robot.useTrigger SetHintLowPriority( true ); robot.useTrigger SetCursorHint( "HINT_NOICON" ); robot.useTrigger SetHintString( &"KILLSTREAK_COMBAT_ROBOT_GUARD_HINT" ); robot.useTrigger SetTeamForTrigger( player.team ); robot.useTrigger.team = player.team; player ClientClaimTrigger( robot.useTrigger ); player.remoteControlTrigger = robot.useTrigger; robot.useTrigger.ClaimedBy = player; } function WatchCombatRobotOwnerDisconnect( player ) { combatRobot = self; combatRobot notify( "WatchCombatRobotOwnerDisconnect_singleton" ); combatRobot endon( "WatchCombatRobotOwnerDisconnect_singleton" ); combatRobot endon( "combat_robot_shutdown" ); player util::waittill_any( "joined_team", "disconnect", "joined_spectators" ); combatRobot notify( "combat_robot_shutdown" ); } function private _corpseWatcher() { archetype = self.archetype; self waittill("actor_corpse", corpse); corpse clientfield::set("arch_actor_fire_fx", 3); } function private _explodeRobot( combatRobot ) { combatRobot clientfield::set("arch_actor_fire_fx", 1); clientfield::set( "robot_mind_control_explosion", 1 ); combatRobot thread _corpseWatcher(); if ( RandomInt( 100 ) >= 50 ) GibServerUtils::GibLeftArm( combatRobot ); else GibServerUtils::GibRightArm( combatRobot ); GibServerUtils::GibLegs( combatRobot ); GibServerUtils::GibHead( combatRobot ); velocity = combatRobot GetVelocity() * ( 1 / 8 ); combatRobot StartRagdoll(); combatRobot LaunchRagdoll( ( velocity[0] + RandomFloatRange( -20, 20 ), velocity[1] + RandomFloatRange( -20, 20 ), RandomFloatRange( 60, 80 ) ), "j_mainroot" ); } function OnCombatRobotTimeout() { combatRobot = self; combatRobot killstreaks::play_pilot_dialog_on_owner( "timeout", "combat_robot" ); combatRobot ai::set_behavior_attribute( "shutdown", true ); wait RandomFloatRange( 3.0, 4.5 ); _explodeRobot( combatRobot ); params = level.killstreakBundle["combat_robot"]; if( isdefined( params.ksExplosionFX ) ) { PlayFXOnTag( params.ksExplosionFX, combatRobot, "tag_origin" ); } Target_Remove( combatRobot ); if(!isdefined(params.ksExplosionOuterRadius))params.ksExplosionOuterRadius=200; if(!isdefined(params.ksExplosionInnerRadius))params.ksExplosionInnerRadius=1; if(!isdefined(params.ksExplosionOuterDamage))params.ksExplosionOuterDamage=25; if(!isdefined(params.ksExplosionInnerDamage))params.ksExplosionInnerDamage=350; if(!isdefined(params.ksExplosionMagnitude))params.ksExplosionMagnitude=1; PhysicsExplosionSphere( combatRobot.origin, params.ksExplosionOuterRadius, params.ksExplosionInnerRadius, params.ksExplosionMagnitude, params.ksExplosionOuterDamage, params.ksExplosionInnerDamage ); if( isdefined( combatRobot.owner ) ) { RadiusDamage( combatRobot.origin, params.ksExplosionOuterRadius, params.ksExplosionInnerDamage, params.ksExplosionOuterDamage, combatRobot.owner, "MOD_EXPLOSIVE", GetWeapon( "combat_robot_marker" ) ); if( isdefined( params.ksExplosionRumble ) ) combatRobot.owner PlayRumbleOnEntity( params.ksExplosionRumble ); } wait( 0.2 ); combatRobot notify( "combat_robot_shutdown" ); } function WatchCombatRobotShutdown() { combatRobot = self; combatRobotTeam = combatRobot.originalteam; combatRobotKillstreakId = combatRobot.killstreak_id; combatRobot waittill( "combat_robot_shutdown" ); combatRobot playsound ("evt_combat_bot_mech_fail_explode"); if( isdefined( combatRobot.useTrigger ) ) combatRobot.useTrigger delete(); if( isdefined( combatRobot.markerFXHandle ) ) combatRobot.markerFXHandle delete(); _DestroyGuardMarker( combatRobot ); killstreakrules::killstreakStop( "combat_robot", combatRobotTeam, combatRobotKillstreakId ); if( isdefined( combatRobot ) ) { if( Target_IsTarget( combatRobot ) ) Target_Remove( combatRobot ); if( !level.gameEnded ) // kill and do damage do nothing after game end { if( combatRobot flagsys::get( "in_vehicle" ) ) combatRobot Unlink(); combatRobot Kill(); } } } function sndWatchCombatRobotVoxNotifies() { combatRobot = self; combatRobot endon( "combat_robot_shutdown" ); combatRobot endon( "death" ); combatRobot PlaySoundOnTag( "vox_robot_chatter", "j_head" ); while( 1 ) { soundAlias = undefined; combatRobot waittill("bhtn_action_notify", notify_string); switch( notify_string ) { case "charge": case "attack_melee": case "attack_kill": case "modeSwap": soundAlias = "vox_robot_chatter"; break; } if( isdefined( soundAlias ) ) { combatRobot PlaySoundOnTag( soundAlias, "j_head" ); wait(1.2); } } } function sndWatchExit() { combatRobot = self; combatRobot endon( "combat_robot_shutdown" ); combatRobot endon( "death" ); combatRobot waittill( "exiting_vehicle" ); combatRobot playsound( "veh_vtol_supply_robot_launch" ); } function sndWatchLanding() { combatRobot = self; combatRobot endon( "combat_robot_shutdown" ); combatRobot endon( "death" ); combatRobot waittill( "falling", falltime ); wait_time = falltime - .5; if ( wait_time > 0 ) wait( wait_time ); combatRobot playsound( "veh_vtol_supply_robot_land" ); } function sndWatchActivate() { combatRobot = self; combatRobot endon( "combat_robot_shutdown" ); combatRobot endon( "death" ); combatRobot waittill( "landing" ); wait(.1); combatRobot playsound( "veh_vtol_supply_robot_activate" ); } function combatRobotDamageOverride( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, psOffsetTime, boneIndex, modelIndex ) { combatRobot = self; if( combatRobot flagsys::get( "in_vehicle" ) && ( sMeansOfDeath == "MOD_TRIGGER_HURT" ) ) // the dropship goes through hurt triggers sometimes iDamage = 0; else iDamage = killstreaks::OnDamagePerWeapon( "combat_robot", eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, self.maxhealth, undefined, self.maxhealth*0.4, undefined, 0, undefined, true, 1.0 ); combatRobot.missileTrackDamage += iDamage; if ( iDamage > 0 && isdefined( eAttacker ) ) { if ( isPlayer( eAttacker) ) { if ( isdefined( combatRobot.owner ) ) { challenges::combat_robot_damage( eAttacker, combatRobot.Owner ); } } } return iDamage; }