#using scripts\codescripts\struct; #using scripts\mp\_util; #using scripts\mp\gametypes\_hostmigration; #using scripts\mp\killstreaks\_airsupport; #using scripts\mp\killstreaks\_emp; #using scripts\mp\killstreaks\_killstreakrules; #using scripts\mp\killstreaks\_killstreak_bundles; #using scripts\mp\killstreaks\_killstreak_hacking; #using scripts\mp\killstreaks\_killstreaks; #using scripts\mp\killstreaks\_placeables; #using scripts\mp\teams\_teams; #using scripts\mp\teams\_teams; #using scripts\shared\callbacks_shared; #using scripts\shared\challenges_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\hostmigration_shared; #using scripts\shared\killstreaks_shared; #using scripts\shared\killstreaks_shared; #using scripts\shared\popups_shared; #using scripts\shared\turret_shared; #using scripts\shared\util_shared; #using scripts\shared\vehicle_shared; #using scripts\shared\weapons\_weaponobjects; #using scripts\shared\scoreevents_shared; #using_animtree ( "mp_emp_power_core" ); #precache( "string", "KILLSTREAK_EARNED_EMP" ); #precache( "string", "KILLSTREAK_EMP_NOT_AVAILABLE" ); #precache( "string", "KILLSTREAK_EMP_INBOUND" ); #precache( "string", "KILLSTREAK_EMP_HACKED" ); #precache( "string", "KILLSTREAK_DESTROYED_EMP" ); #precache( "triggerstring", "KILLSTREAK_EMP_PLACE_TURRET_HINT" ); #precache( "triggerstring", "KILLSTREAK_EMP_INVALID_TURRET_LOCATION" ); #precache( "triggerstring", "KILLSTREAK_EMP_TURRET_PICKUP" ); #precache( "string", "mpl_killstreak_emp_activate" ); //#precache( "fx", "killstreaks/fx_emp_core" ); #precache( "fx", "killstreaks/fx_emp_exp_death" ); #namespace emp; function init() { bundle = struct::get_script_bundle( "killstreak", "killstreak_emp" ); level.empKillstreakBundle = bundle; level.ActivePlayerEMPs = []; level.ActiveEMPs = []; foreach( team in level.teams ) { level.ActiveEMPs[ team ] = false; } level.enemyEMPActiveFunc = &EnemyEMPActive; level thread EMPTracker(); killstreaks::register( "emp", "emp", "killstreak_emp", "emp_used", &ActivateEMP ); killstreaks::register_strings( "emp", &"KILLSTREAK_EARNED_EMP", &"KILLSTREAK_EMP_NOT_AVAILABLE", &"KILLSTREAK_EMP_INBOUND", undefined, &"KILLSTREAK_EMP_HACKED", false ); killstreaks::register_dialog( "emp", "mpl_killstreak_emp_activate", "empDialogBundle", undefined, "friendlyEmp", "enemyEmp", "enemyEmpMultiple", "friendlyEmpHacked", "enemyEmpHacked", "requestEmp", "threatEmp" ); clientfield::register( "scriptmover", "emp_turret_init", 1, 1, "int" ); // re-export model in close position to save this clientfield clientfield::register( "vehicle", "emp_turret_deploy", 1, 1, "int" ); spinAnim = %o_turret_emp_core_spin; deployAnim = %o_turret_emp_core_deploy; callback::on_spawned( &OnPlayerSpawned ); callback::on_connect( &OnPlayerConnect ); vehicle::add_main_callback( "emp_turret", &InitTurretVehicle ); } function InitTurretVehicle() { turretVehicle = self; turretVehicle killstreaks::setup_health( "emp" ); turretVehicle.damageTaken = 0; turretVehicle.health = turretVehicle.maxhealth; turretVehicle clientfield::set( "enemyvehicle", 1 ); turretVehicle.soundmod = "drone_land"; // TODO: update this to the correct value turretVehicle.overrideVehicleDamage = &OnTurretDamage; turretVehicle.overrideVehicleDeath = &OnTurretDeath; Target_Set( turretVehicle, ( 0, 0, 36 ) ); } function OnPlayerSpawned() { self endon( "disconnect" ); self UpdateEMP(); } function OnPlayerConnect() { self.entNum = self getEntityNumber(); level.ActivePlayerEMPs[ self.entNum ] = false; } function ActivateEMP() { player = self; killstreakId = player killstreakrules::killstreakStart( "emp", player.team, false, false ); if( killstreakId == (-1) ) { return false; } bundle = level.empKillstreakBundle; empBase = player placeables::SpawnPlaceable( "emp", killstreakId, &OnPlaceEMP, &OnCancelPlacement, undefined, &OnShutdown, undefined, undefined, "wpn_t7_turret_emp_core", "wpn_t7_turret_emp_core_yellow", "wpn_t7_turret_emp_core_red", true, "", undefined, undefined, 0, bundle.ksPlaceableHint, bundle.ksPlaceableInvalidLocationHint ); empBase thread util::ghost_wait_show_to_player( player ); empBase.otherModel thread util::ghost_wait_show_to_others( player ); empBase clientfield::set( "emp_turret_init", 1 ); empBase.otherModel clientfield::set( "emp_turret_init", 1 ); event = empBase util::waittill_any_return( "placed", "cancelled", "death", "disconnect" ); if( event != "placed" ) { return false; } return true; } function OnPlaceEMP( emp ) { player = self; assert( IsPlayer( player ) ); assert( !isdefined( emp.vehicle ) ); emp.vehicle = SpawnVehicle( "emp_turret", emp.origin, emp.angles ); emp.vehicle thread util::ghost_wait_show( 0.05 ); emp.vehicle.killstreakType = emp.killstreakType; // need to do this for enable_hacking emp.vehicle.owner = player; emp.vehicle SetOwner( player ); emp.vehicle.ownerEntNum = player.entNum; emp.vehicle.parentStruct = emp; player.EMPTime = GetTime(); player killstreaks::play_killstreak_start_dialog( "emp", player.pers["team"], emp.killstreakId ); player AddWeaponStat( GetWeapon( "emp" ), "used", 1 ); level thread popups::DisplayKillstreakTeamMessageToAll( "emp", player ); emp.vehicle killstreaks::configure_team( "emp", emp.killstreakId, player ); emp.vehicle killstreak_hacking::enable_hacking( "emp", &HackedCallbackPre, &HackedCallbackPost ); emp thread killstreaks::WaitForTimeout( "emp", ( 60 * 1000 ), &on_timeout, "death" ); if ( IsSentient( emp.vehicle ) == false ) emp.vehicle MakeSentient(); // so other sentients will consider this as a potential enemy emp.vehicle vehicle::disconnect_paths( 0, false ); // perform deploy on separate thread because of the wait delays // always complete OnPlace() in same frame for killstreak accounting; otherwise exploits can happen player thread DeployEmpTurret( emp ); } function DeployEmpTurret( emp ) { player = self; player endon( "disconnect" ); player endon( "joined_team" ); player endon( "joined_spectators" ); emp endon( "death" ); // deploy emp emp.vehicle UseAnimTree( #animtree ); emp.vehicle SetAnim( %o_turret_emp_core_deploy, 1.0 ); length = GetAnimLength( %o_turret_emp_core_deploy ); emp.vehicle clientfield::set( "emp_turret_deploy", 1 ); wait length * 0.75; // fire emp pulse emp.vehicle thread PlayEMPFx(); emp.vehicle playsound( "mpl_emp_turret_activate" ); emp.vehicle SetAnim( %o_turret_emp_core_spin, 1.0 ); // Jam Enemies and destroy other scorestreaks! player thread EMP_JamEnemies( emp, false ); wait length * 0.25; emp.vehicle ClearAnim( %o_turret_emp_core_deploy, 0 ); // stop deploy anim } function HackedCallbackPre( hacker ) { emp_vehicle = self; emp_vehicle clientfield::set( "enemyvehicle", 2 ); emp_vehicle.parentStruct killstreaks::configure_team( "emp", emp_vehicle.parentStruct.killstreakId, hacker, undefined, undefined, undefined, true ); } function HackedCallbackPost( hacker ) { emp_vehicle = self; hacker thread EMP_JamEnemies( emp_vehicle.parentStruct, true ); } function DoneEMPFx( fxTagOrigin ) { PlayFx( "killstreaks/fx_emp_exp_death", fxTagOrigin ); playsoundatposition( "mpl_emp_turret_deactivate", fxTagOrigin ); } function PlayEMPFx() { emp_vehicle = self; emp_vehicle playloopsound( "mpl_emp_turret_loop_close" ); {wait(.05);}; // workaround for a bug where the fx would not play on subsequent deployment of power cores } function on_timeout() { emp = self; if ( isdefined( emp.vehicle ) ) { fxTagOrigin = emp.vehicle GetTagorigin( "tag_fx" ); DoneEMPFx( fxTagOrigin ); } ShutdownEMP( emp ); } function OnCancelPlacement( emp ) { StopEMP( emp.team, emp.ownerEntNum, emp.originalTeam, emp.killstreakId ); } function OnTurretDamage( eInflictor, attacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal ) { empDamage = 0; // emp power core is not affected by emp damage iDamage = self killstreaks::OnDamagePerWeapon( "emp", attacker, iDamage, iDFlags, sMeansOfDeath, weapon, self.maxhealth, undefined, self.maxhealth*0.4, undefined, empDamage, undefined, true, 1.0 ); self.damageTaken += iDamage; // turret death if ( self.damageTaken > self.maxHealth && !isdefined( self.will_die ) ) { self.will_die = true; self thread OnDeathAfterFrameEnd( attacker, weapon ); } return iDamage; } function OnTurretDeath( inflictor, attacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime ) { // currently, OnTurretDeath is not getting called, so we call OnDeath directly from OnTurretDamage self OnDeath( attacker, weapon ); } function OnDeathAfterFrameEnd( attacker, weapon ) { waittillframeend; if ( isdefined( self ) ) { self OnDeath( attacker, weapon ); } } function OnDeath( attacker, weapon ) { emp_vehicle = self; fxTagOrigin = self GetTagorigin( "tag_fx" ); DoneEMPFx( fxTagOrigin ); if ( isdefined( attacker ) && IsPlayer( attacker ) && ( !isdefined( emp_vehicle.owner ) || emp_vehicle.owner util::IsEnemyPlayer( attacker ) ) ) { attacker challenges::destroyScoreStreak( weapon, false, true, false ); attacker challenges::destroyNonAirScoreStreak_PostStatsLock( weapon ); attacker AddPlayerStat( "destroy_turret", 1 ); attacker AddWeaponStat( weapon, "destroy_turret", 1 ); scoreevents::processScoreEvent( "destroyed_emp", attacker, emp_vehicle.owner, weapon ); LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_EMP", attacker.entnum ); } if ( isdefined( attacker ) && isdefined( emp_vehicle.owner ) && ( attacker != emp_vehicle.owner ) ) emp_vehicle killstreaks::play_destroyed_dialog_on_owner( "emp", emp_vehicle.parentStruct.killstreakId ); ShutdownEMP( emp_vehicle.parentStruct ); } function OnShutdown( emp ) { ShutdownEMP( emp ); } function ShutdownEMP( emp ) { if (!isdefined( emp ) ) return; if ( isdefined( emp.already_shutdown ) ) return; emp.already_shutdown = true; if ( isdefined( emp.vehicle ) ) emp.vehicle clientfield::set( "emp_turret_deploy", 0 ); StopEMP( emp.team, emp.OwnerEntNum, emp.originalTeam, emp.killstreakId ); if ( isdefined( emp.otherModel ) ) { emp.otherModel delete(); } if ( isdefined( emp.vehicle ) ) { emp.vehicle delete(); } emp delete(); } function StopEMP( currentTeam, currentOwnerEntNum, originalTeam, killstreakID ) { StopEMPEffect( currentTeam, currentOwnerEntNum ); StopEMPRule( originalTeam, killstreakID ); } function StopEMPEffect( team, ownerEntNum ) { level.ActiveEMPs[ team ] = false; level.ActivePlayerEMPs[ ownerEntNum ] = false; level notify ( "emp_updated" ); } function StopEMPRule( killstreakOriginalTeam, killstreakId ) { killstreakrules::killstreakStop( "emp", killstreakOriginalTeam, killstreakId ); } function HasActiveEMP() { return ( level.ActivePlayerEMPs[ self.entNum ] ); } function TeamHasActiveEMP( team ) { return ( level.ActiveEMPs[ team ] > 0 ); } function EnemyEMPActive() { if( level.teamBased ) { foreach( team in level.teams ) { if( ( team != self.team ) && TeamHasActiveEMP( team ) ) { return true; } } } else { enemies = self teams::GetEnemyPlayers(); foreach( player in enemies ) { if( player HasActiveEMP() ) { return true; } } } return false; } function EnemyEMPOwner() { enemies = self teams::GetEnemyPlayers(); foreach( player in enemies ) { if( player HasActiveEMP() ) { return player; } } return undefined; } function EMP_JamEnemies( empEnt, hacked ) { level endon ( "game_ended" ); self endon( "killstreak_hacked" ); if( level.teamBased ) { if ( hacked ) { level.ActiveEMPs[ empEnt.OriginalTeam ] = false; } level.ActiveEMPs[ self.team ] = true; } if( hacked ) { level.ActivePlayerEMPs[ empEnt.originalOwnerEntNum ] = false; } level.ActivePlayerEMPs[ self.entNum ] = true; level notify( "emp_updated" ); level notify( "emp_deployed" ); VisionSetNaked( "flash_grenade", 1.5 ); wait ( 0.1 ); VisionSetNaked( "flash_grenade", 0 ); VisionSetNaked( GetDvarString( "mapname" ), 5.0 ); empKillstreakWeapon = GetWeapon( "emp" ); empKillstreakWeapon.isEmpKillstreak = true; level killstreaks::DestroyOtherTeamsActiveVehicles( self, empKillstreakWeapon ); level killstreaks::DestroyOtherTeamsEquipment( self, empKillstreakWeapon ); level weaponobjects::destroy_other_teams_supplemental_watcher_objects( self, empKillstreakWeapon ); } function EMPTracker() { level endon ( "game_ended" ); while( true ) { level waittill( "emp_updated" ); foreach ( player in level.players ) { player UpdateEMP(); } } } function UpdateEMP() { player = self; enemy_emp_active = player EnemyEMPActive(); player setEMPJammed( enemy_emp_active ); emped = player isEMPJammed(); // ask because a perk may stop it player clientfield::set_to_player( "empd_monitor_distance", emped ); if( emped ) { player notify( "emp_jammed" ); } }