#using scripts\codescripts\struct; #using scripts\shared\callbacks_shared; #using scripts\shared\hud_shared; #using scripts\shared\util_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\lui_shared; #using scripts\mp\killstreaks\_qrdrone; #using scripts\mp\_util; #using scripts\mp\killstreaks\_ai_tank; #using scripts\mp\killstreaks\_killstreaks; #using scripts\mp\killstreaks\_killstreakrules; #using scripts\mp\killstreaks\_turret; // Manager for all weapons that can be remotely controlled with the tablet #namespace remote_weapons; function init() { level.remoteWeapons = []; level.remoteExitHint = &"MP_REMOTE_EXIT"; callback::on_spawned( &on_player_spawned ); } function on_player_spawned() { self endon("disconnect"); self AssignRemoteControlTrigger(); } function RemoveAndAssignNewRemoteControlTrigger( remoteControlTrigger ) // player = self { ArrayRemoveValue( self.activeRemoteControlTriggers, remoteControlTrigger ); self AssignRemoteControlTrigger( true ); } function AssignRemoteControlTrigger( force_new_assignment = false ) // player = self { if ( !isdefined( self.activeRemoteControlTriggers ) ) self.activeRemoteControlTriggers = []; ArrayRemoveValue( self.activeRemoteControlTriggers, undefined ); if ( ( !isdefined( self.remoteControlTrigger ) || force_new_assignment ) && self.activeRemoteControlTriggers.size > 0 ) { self.remoteControlTrigger = self.activeRemoteControlTriggers[ self.activeRemoteControlTriggers.size - 1 ]; } if ( isdefined( self.remoteControlTrigger ) ) { self.remoteControlTrigger.origin = self.origin; self.remoteControlTrigger LinkTo( self ); } } function RegisterRemoteWeapon( weaponName, hintString, useCallback, endUseCallback, hideCompassOnUse = true ) { assert( isdefined( level.remoteWeapons ) ); level.remoteWeapons[ weaponName ] = SpawnStruct(); level.remoteWeapons[ weaponName ].hintString = hintString; level.remoteWeapons[ weaponName ].useCallback = useCallback; level.remoteWeapons[ weaponName ].endUseCallback = endUseCallback; level.remoteWeapons[ weaponName ].hideCompassOnUse = hideCompassOnUse; } function UseRemoteWeapon( weapon, weaponName, immediate, allowManualDeactivation = true, always_allow_ride = false ) { player = self; assert( IsPlayer( player ) ); weapon.remoteOwner = player; weapon.initTime = GetTime(); weapon.remoteName = weaponName; weapon.remoteWeaponAllowManualDeactivation = allowManualDeactivation; weapon thread WatchRemoveRemoteControlledWeapon(); if( !immediate ) { weapon CreateRemoteWeaponTrigger(); } else { weapon thread WatchOwnerDisconnect(); weapon UseRemoteControlWeapon( allowManualDeactivation, always_allow_ride ); } } function WatchForHack() { weapon = self; weapon endon( "death" ); weapon waittill( "killstreak_hacked", hacker ); if ( isdefined( weapon.remoteWeaponAllowManualDeactivation ) && weapon.remoteWeaponAllowManualDeactivation == true ) { weapon thread WatchRemoteControlDeactivate(); } weapon.remoteOwner = hacker; } function WatchRemoveRemoteControlledWeapon() { weapon = self; weapon endon( "remote_weapon_end" ); weapon util::waittill_any( "death", "remote_weapon_shutdown" ); weapon EndRemoteControlWeaponUse( false ); while( isdefined( weapon ) ) { {wait(.05);}; // Wait until any death animations and any other waits are finished } //Check for queued remote weapon } function CreateRemoteWeaponTrigger() { weapon = self; player = weapon.remoteOwner; if ( isdefined( weapon.useTrigger ) ) { weapon.useTrigger delete(); } weapon.useTrigger = spawn( "trigger_radius_use", player.origin, 32, 32 ); weapon.useTrigger EnableLinkTo(); weapon.useTrigger LinkTo( player ); weapon.useTrigger SetHintLowPriority( true ); weapon.useTrigger SetCursorHint( "HINT_NOICON" ); weapon.useTrigger SetHintString( level.remoteWeapons[ weapon.remoteName ].hintString ); weapon.useTrigger SetTeamForTrigger( player.team ); weapon.useTrigger.team = player.team; player ClientClaimTrigger( weapon.useTrigger ); player.remoteControlTrigger = weapon.useTrigger; player.activeRemoteControlTriggers[ player.activeRemoteControlTriggers.size ] = weapon.useTrigger; weapon.useTrigger.ClaimedBy = player; weapon thread WatchWeaponDeath(); weapon thread WatchOwnerDisconnect(); weapon thread WatchRemoteTriggerUse(); weapon thread WatchRemoteTriggerDisable(); } function WatchWeaponDeath() { weapon = self; weapon.useTrigger endon( "death" ); weapon util::waittill_any( "death", "remote_weapon_end" ); if ( isdefined( weapon.remoteOwner ) ) { weapon.remoteOwner RemoveAndAssignNewRemoteControlTrigger( weapon.useTrigger ); } weapon.useTrigger delete(); } function WatchOwnerDisconnect() { weapon = self; weapon endon( "remote_weapon_end" ); weapon endon( "remote_weapon_shutdown" ); if( isdefined( weapon.useTrigger ) ) weapon.useTrigger endon( "death" ); weapon.remoteOwner util::waittill_any( "joined_team", "disconnect", "joined_spectators" ); EndRemoteControlWeaponUse( false ); if( isdefined( weapon ) && isdefined( weapon.useTrigger ) ) weapon.useTrigger delete(); } function WatchRemoteTriggerDisable() { weapon = self; weapon endon( "remote_weapon_end" ); weapon endon( "remote_weapon_shutdown" ); weapon.useTrigger endon( "death" ); while( true ) { weapon.useTrigger TriggerEnable( !( weapon.remoteOwner IsWallRunning() ) ); wait( 0.1 ); } } function AllowRemoteStart() { player = self; if( player useButtonPressed() && !player.throwingGrenade && !player meleeButtonPressed() && !player util::isUsingRemote() && !( isdefined( player.carryObject ) && ( isdefined( player.carryObject.disallowRemoteControl ) && player.carryObject.disallowRemoteControl ) ) ) { return true; } else { return false; } } function WatchRemoteTriggerUse() { weapon = self; weapon endon( "death" ); weapon endon( "remote_weapon_end" ); if( weapon.remoteOwner util::is_bot() ) { return; } while( true ) { weapon.useTrigger waittill( "trigger", player ); if( weapon.remoteOwner IsUsingOffhand() || weapon.remoteOwner IsWallRunning() ) { continue; } if( isdefined( weapon.hackertrigger ) && isdefined( weapon.hackertrigger.progressbar ) ) { if( weapon.remoteName == "killstreak_remote_turret" ) { weapon.remoteOwner iPrintLnBold( &"KILLSTREAK_AUTO_TURRET_NOT_AVAILABLE" ); } continue; } if( weapon.remoteOwner AllowRemoteStart() ) { UseRemoteControlWeapon(); } } } function UseRemoteControlWeapon( allowManualDeactivation = true, always_allow_ride = false ) { self endon( "death" ); weapon = self; assert( isdefined( weapon.remoteOwner ) ); weapon.control_initiated = true; weapon.endRemoteControlWeapon = false; weapon.remoteOwner endon( "disconnect" ); weapon.remoteOwner endon( "joined_team" ); weapon.remoteOwner DisableOffhandWeapons(); weapon.remoteOwner DisableWeaponCycling(); weapon.remoteOwner.dofutz = false; if( !isdefined( weapon.disableRemoteWeaponSwitch ) ) { remoteWeapon = GetWeapon( "killstreak_remote" ); weapon.remoteOwner giveWeapon( remoteWeapon ); weapon.remoteOwner switchToWeapon( remoteWeapon ); if ( always_allow_ride ) { weapon.remoteOwner util::waittill_any( "weapon_change", "death" ); } else { weapon.remoteOwner waittill( "weapon_change", newWeapon ); } } if( isdefined( newweapon ) ) { if( newweapon != remoteWeapon ) { weapon.remoteOwner killstreaks::clear_using_remote( true, true ); return; } } weapon.remoteOwner thread killstreaks::watch_for_remove_remote_weapon(); weapon.remoteOwner util::setUsingRemote( weapon.remoteName ); weapon.remoteOwner util::freeze_player_controls( true ); result = weapon.remoteOwner killstreaks::init_ride_killstreak( weapon.remoteName, always_allow_ride ); if( result != "success" ) { if( result != "disconnect" ) { weapon.remoteOwner killstreaks::clear_using_remote(); weapon thread ResetControlInitiatedUponOwnerRespawn(); } } else { weapon.controlled = true; weapon.killCamEnt = self; weapon notify("remote_start"); // shuts down the AI scripts for this weapon if ( allowManualDeactivation ) { weapon thread watchRemoteControlDeactivate(); } weapon.remoteOwner thread [[level.remoteWeapons[ weapon.remoteName ].useCallback]]( weapon ); if ( level.remoteWeapons[ weapon.remoteName ].hideCompassOnUse ) { weapon.remoteOwner killstreaks::hide_compass(); } } weapon.remoteOwner util::freeze_player_controls( false ); } function ResetControlInitiatedUponOwnerRespawn() { self endon( "death" ); self.remoteOwner waittill( "spawned" ); self.control_initiated = false; } function WatchRemoteControlDeactivate() { self notify("WatchRemoteControlDeactivate_remoteWeapons"); self endon ("WatchRemoteControlDeactivate_remoteWeapons"); weapon = self; weapon endon( "remote_weapon_end" ); weapon endon( "death" ); weapon.remoteOwner endon( "disconnect" ); while( true ) { timeUsed = 0; while( weapon.remoteOwner UseButtonPressed() ) { timeUsed += 0.05; if ( timeUsed > 0.25 ) { weapon thread EndRemoteControlWeaponUse( true ); return; } {wait(.05);}; } {wait(.05);}; } } function EndRemoteControlWeaponUse( exitRequestedByOwner ) { weapon = self; if( !isdefined( weapon ) || ( isdefined( weapon.endRemoteControlWeapon ) && weapon.endRemoteControlWeapon ) ) return; weapon.endRemoteControlWeapon = true; remote_controlled = ( isdefined( weapon.control_initiated ) && weapon.control_initiated ) || ( isdefined( weapon.controlled ) && weapon.controlled ); while ( isdefined( weapon ) && ( weapon.forceWaitRemoteControl === true ) && ( remote_controlled == false ) ) { remote_controlled = ( ( isdefined( weapon.control_initiated ) && weapon.control_initiated ) || ( isdefined( weapon.controlled ) && weapon.controlled ) ); {wait(.05);}; } if ( !isdefined( weapon ) ) return; if( isdefined( weapon.remoteOwner ) && remote_controlled ) { if( isdefined( weapon.remoteWeaponShutdownDelay ) ) //when changing teams or disconnect, we can skip this { wait( weapon.remoteWeaponShutdownDelay ); } player = weapon.remoteOwner; if( player.dofutz === true ) { player clientfield::set_to_player( "static_postfx", 1 ); wait 1; if( isdefined( player ) ) { player clientfield::set_to_player( "static_postfx", 0 ); player.dofutz = false; } } else if ( !exitRequestedByOwner && weapon.watch_remote_weapon_death === true && !IsAlive( weapon ) ) { wait ( isdefined( weapon.watch_remote_weapon_death_duration ) ? weapon.watch_remote_weapon_death_duration : 1.0 ); } if( isdefined( player ) ) { player thread FadeToBlackAndBackIn(); player waittill( "fade2black" ); // we the prev call to be blocking, in the same time we dont want to be left with black screen if smth external kills this thread. if( remote_controlled ) player unlink(); player killstreaks::clear_using_remote( true ); // must come before the end use callback, its notify clears all the AI threads on the remote weapons cleared_killstreak_delay = 1; player EnableUsability(); // there are cases where the typical path to enable usability is not called, (because of some endon) } } if( isdefined( weapon ) && isdefined( weapon.remoteName ) ) { self [[ level.remoteWeapons[ weapon.remoteName ].endUseCallback]]( weapon, exitRequestedByOwner ); } if( isdefined( weapon )) { weapon.killCamEnt = weapon; if( isdefined( weapon.remoteOwner ) ) { if ( remote_controlled ) { weapon.remoteOwner unlink(); if( !( isdefined( cleared_killstreak_delay ) && cleared_killstreak_delay ) ) weapon.remoteOwner killstreaks::reset_killstreak_delay_killcam(); weapon.remoteOwner util::clientNotify( "nofutz" ); } if( isdefined( level.gameEnded ) && level.gameEnded ) { weapon.remoteOwner util::freeze_player_controls( true ); } } } if( isdefined( weapon ) ) { weapon.control_initiated = false; weapon.controlled = false; if( isdefined( weapon.remoteOwner ) ) weapon.remoteOwner killstreaks::unhide_compass(); if( !exitRequestedByOwner || ( isdefined( weapon.one_remote_use ) && weapon.one_remote_use ) ) weapon notify( "remote_weapon_end" ); } } function FadeToBlackAndBackIn() { self endon( "disconnect" ); // self endon( "death" ); // never end on death as it may never fade back in lui::screen_fade_out( 0.1 ); self qrdrone::destroyHud(); {wait(.05);}; self notify( "fade2black" ); lui::screen_fade_in( .2 ); } function stunStaticFX( duration ) { self endon( "remove_remote_weapon" ); self.fullscreen_static.alpha = 0.65; wait ( duration - 0.5 ); time = duration - 0.5; while ( time < duration ) { {wait(.05);}; time += 0.05; self.fullscreen_static.alpha -= 0.05; } self.fullscreen_static.alpha = 0.15; } function destroyRemoteHUD() { self UseServerVisionset( false ); self SetInfraredVision( false ); if ( isdefined(self.fullscreen_static) ) { self.fullscreen_static destroy(); } if ( isdefined( self.hud_prompt_exit )) { self.hud_prompt_exit destroy(); } } function set_static( val ) { owner = self.owner; if( isdefined( owner ) && owner.usingvehicle && isdefined( owner.viewlockedentity ) && ( owner.viewlockedentity == self ) ) { owner clientfield::set_to_player( "static_postfx", val ); } } function do_static_fx() { self endon( "death" ); self set_static( 1 ); wait 2; self set_static( 0 ); self notify( "static_fx_done" ); }