955 lines
39 KiB
Plaintext
955 lines
39 KiB
Plaintext
#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;
|
|
}
|