2023-04-13 17:30:38 +02:00

471 lines
23 KiB
Plaintext

#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" );
}
}