From 207facbc402f5639cbcd31f079214351ef605cf2 Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Tue, 22 Jun 2021 14:30:49 +0100 Subject: initial commit after moving to new repo --- .../scripts/vscripts/pilot/_pilot_leeching.gnut | 610 +++++++++++++++++++++ 1 file changed, 610 insertions(+) create mode 100644 Northstar.CustomServers/scripts/vscripts/pilot/_pilot_leeching.gnut (limited to 'Northstar.CustomServers/scripts/vscripts/pilot/_pilot_leeching.gnut') diff --git a/Northstar.CustomServers/scripts/vscripts/pilot/_pilot_leeching.gnut b/Northstar.CustomServers/scripts/vscripts/pilot/_pilot_leeching.gnut new file mode 100644 index 000000000..596ca7118 --- /dev/null +++ b/Northstar.CustomServers/scripts/vscripts/pilot/_pilot_leeching.gnut @@ -0,0 +1,610 @@ +global function PlayerLeeching_Init + +global function LeechSurroundingSpectres +global function CodeCallback_LeechStart +global function LeechPropagate +global function ReleaseLeechOverflow +global function IsBeingLeeched + +// 384 ~ 32 feet +// 256 ~ 21.3 feet +// 192 ~ 16 feet +// 128 ~ 10.6 feet +const float SPECTRE_LEECH_SURROUNDING_RANGE = 384.0 + +#if MP +const int GLOBAL_LEECH_LIMIT = 100 // per team +const int MAX_LEECHABLE = 100 // per player +const bool PROPAGATE_ON_LEECH = true +#elseif SP +const int GLOBAL_LEECH_LIMIT = 8 +const int MAX_LEECHABLE = 4 +const bool PROPAGATE_ON_LEECH = false +#endif + +void function PlayerLeeching_Init() +{ + #if SERVER + PrecacheModel( DATA_KNIFE_MODEL ) + #endif +} + +void function PlayerStopLeeching( entity player, entity target ) +{ + Assert( target != null ) + Assert( player.p.leechTarget == target ) + + StopLeechingProgress( player.p.leechTarget ) + + player.p.leechTarget = null +} + +void function CodeCallback_LeechStart( entity player, entity target ) +{ + thread LeechStartThread( player, target ) +} + +void function LeechStartThread( entity player, entity target ) +{ + if ( !IsAlive( target ) ) + return + + if ( !IsAlive( player ) ) + return + + LeechActionInfo action = FindLeechAction( player, target ) + if ( !action.isValid ) + return + +/* + if ( player.ContextAction_IsActive() + || player.ContextAction_IsActive() + || target.ContextAction_IsActive() ) + { + return + } +*/ + + player.EndSignal( "ScriptAnimStop" ) + player.EndSignal( "OnDeath" ) + target.EndSignal( "OnDestroy" ) + target.EndSignal( "OnDeath" ) + target.EndSignal( "ScriptAnimStop" ) + + StartLeechingProgress( target, player ) + + LeechData e + e.playerStartOrg = player.GetOrigin() + e.targetStartPos = target.GetOrigin() + + OnThreadEnd + ( + function() : ( e, player, target ) + { + if ( IsValid( player ) ) + { + player.SetSyncedEntity( null ) + if ( player.ContextAction_IsLeeching() ) + player.Event_LeechEnd() + + // reset to start position in case animation moves us at all + //player.SetOrigin( e.playerStartOrg ) + player.Anim_Stop() + player.UnforceStand() + + // done with first person anims + ClearPlayerAnimViewEntity( player ) + DeployAndEnableWeapons( player ) + } + + if ( IsValid( target ) ) + { + if ( !e.success ) + { + if ( IsValid( player ) ) + { + TryLeechAbortCallback( target, player ) //Make "failed leech" sounds play here after exiting leech animation + } + } + + #if MP + target.SetUsable() + #endif + target.SetNoTarget( false ) + target.SetNoTargetSmartAmmo( false ) + target.Anim_Stop() + target.ClearParent() + if ( IsAlive( target ) ) + { + // Note that e.targetStartPos is not guarranteed to be a safe spot since we can have moving geo in the game now + PutEntityInSafeSpot( target, null, null, target.GetOrigin(), target.GetOrigin() ) + } + + if ( target.ContextAction_IsLeeching() ) + target.Event_LeechEnd() + + } + + foreach ( knife in e.knives ) + { + if ( IsValid( knife ) ) + { + knife.Destroy() + } + + } + + if ( IsValid( player ) && player.p.leechTarget ) + { + PlayerStopLeeching( player, player.p.leechTarget ) + } + + if ( IsValid( e.ref ) ) + { + if ( IsValid( player ) ) + player.ClearParent() + + if ( IsValid( target ) ) + target.ClearParent() + + //printt( "kill the ref" ) + if ( IsValid( e.ref ) && !e.ref.IsPlayer() ) + e.ref.Destroy() + } + } + ) + + Assert( player.p.leechTarget == null ) + player.p.leechTarget = target + player.Event_LeechStart() + target.Event_LeechStart() + player.ForceStand() + HolsterAndDisableWeapons( player ) + + float leechTime = svGlobal.defaultPilotLeechTime + if ( PlayerHasPassive( player, ePassives.PAS_FAST_HACK ) ) + leechTime *= 0.85 + + e.leechTime = leechTime + + #if MP + target.UnsetUsable() + #endif + target.SetNoTarget( true ) + target.SetNoTargetSmartAmmo( true ) + + if ( IsSpectre( target ) ) + TellSquadmatesSpectreIsGettingLeeched( target, player ) + + waitthread PlayerLeechTargetAnimation( player, target, action, e ) + + e.leechStartTime = Time() + Remote_CallFunction_Replay( player, "ServerCallback_DataKnifeStartLeech", e.leechTime ) + waitthread WaittillFinishedLeeching( player, target, e ) + + if ( e.success ) + { + thread DataKnifeSuccessSounds( player ) + + DoLeech( target, player ) + PlayerStopLeeching( player, target ) + + // this will kill a random leeched ent from within the team, exluding the current target. When it's not done elsewhere + if ( !WIFI_HACK_OVERFLOW_DIES ) + ReleaseLeechOverflow( player, target ) + + //this is called when the player leeches - not when the system is leeching other spectres + if ( PROPAGATE_ON_LEECH && IsSpectre( target ) ) + LeechSurroundingSpectres( target.GetOrigin(), player ) + } + else + { + DataKnifeCanceledSounds( player ) + Remote_CallFunction_Replay( player, "ServerCallback_DataKnifeCancelLeech" ) + PlayerStopLeeching( player, player.p.leechTarget ) + } + + waitthread PlayerExitLeechingAnim( player, target, action, e ) +} + +void function TellSquadmatesSpectreIsGettingLeeched( entity spectre, entity player ) +{ + string squadName = expect string( spectre.kv.squadname ) + if ( squadName == "" ) + return + + array squad = GetNPCArrayBySquad( squadName ) + squad.removebyvalue( spectre ) + + foreach ( squadMate in squad ) + { + //printt( "Setting enemy of " + squadMate + " to player: " + player ) + squadMate.SetEnemyLKP( player, player.GetOrigin() ) + } +} + +void function ReleaseLeechOverflow( entity player, entity lastLeeched ) +{ + array teamLeechedEnts = GetTeamLeechedEnts( player.GetTeam() ) + array leechedEnts = GetLeechedEnts( player ) + int globalOverflow = GLOBAL_LEECH_LIMIT - teamLeechedEnts.len() + int playerOverflow = MAX_LEECHABLE - leechedEnts.len() + + int overflow = minint( globalOverflow, playerOverflow ) + + if ( overflow >= 0 ) + return + + overflow = abs( overflow ) + + teamLeechedEnts.randomize() + foreach ( ent in teamLeechedEnts ) + { + if ( lastLeeched == ent ) + continue + + entity owner = ent.GetBossPlayer() + Assert( owner.IsPlayer() ) + + + // I think it's better to kill the overflow then have it become an enemy again. + ent.Die() + + delete owner.p.leechedEnts[ ent ] + overflow-- + + if ( overflow == 0 ) + break + } + + Assert( overflow == 0 ) +} + + +int function GetMaxNumberOfLeechedEnts( entity player ) +{ + int teamLeechedCount = GetTeamLeechedEnts( player.GetTeam() ).len() + int leechedEntsCount = GetLeechedEnts( player ).len() + int teamLimit = maxint( 0, GLOBAL_LEECH_LIMIT - teamLeechedCount ) + int maxSize = maxint( 0, MAX_LEECHABLE - leechedEntsCount ) + maxSize = minint( teamLimit, maxSize ) + + return maxSize +} + +void function LeechSurroundingSpectres( vector origin, entity player ) +{ + array enemySpectreArray = GetNPCArrayEx( "npc_spectre", TEAM_ANY, player.GetTeam(), player.GetOrigin(), SPECTRE_LEECH_SURROUNDING_RANGE ) + + if ( !enemySpectreArray.len() ) + return + + // don't resize the array if we should kill the overflow instead + if ( !WIFI_HACK_OVERFLOW_DIES ) + { + int maxSize = GetMaxNumberOfLeechedEnts( player ) + int newSize = minint( enemySpectreArray.len(), maxSize ) + + enemySpectreArray.resize( newSize, null ) + } + + foreach ( spectre in enemySpectreArray ) + { + thread LeechPropagate( spectre, player ) + } + + if ( enemySpectreArray.len() ) + { + if ( PlayerHasPassive( player, ePassives.PAS_WIFI_SPECTRE ) ) + { + EmitSoundOnEntity( player, "BurnCard_WiFiVirus_TurnSpectre" ) + printt( "play BurnCard_WiFiVirus_TurnSpectre" ) + } + } +} + +void function LeechPropagate( entity spectre, entity player ) +{ + if ( spectre.ContextAction_IsActive() ) + return + + if ( !spectre.IsInterruptable() ) + return + + if ( spectre.GetParent() ) + return + + if ( !Leech_IsLeechable( spectre ) ) + return + + player.EndSignal( "OnDestroy" ) + spectre.EndSignal( "OnDestroy" ) + spectre.EndSignal( "OnDeath" ) + + spectre.Event_LeechStart() + + AddAnimEvent( spectre, "leech_switchteam", DoLeechAnimEvent, player ) + + OnThreadEnd( + function() : ( spectre ) + { + if ( IsValid( spectre ) ) + { + DeleteAnimEvent( spectre, "leech_switchteam" ) + + if ( spectre.ContextAction_IsLeeching() ) + spectre.Event_LeechEnd() + } + } + ) + + spectre.Anim_Stop() + waitthread PlayAnim( spectre, "sp_reboot" ) + spectre.SetVelocity( Vector(0,0,0) ) +} + +void function WaittillFinishedLeeching( entity player, entity target, LeechData e ) +{ + player.EndSignal( "OnDeath" ) + player.EndSignal( "ScriptAnimStop" ) + target.EndSignal( "OnDeath" ) + + if ( !player.UseButtonPressed() ) + return + + float waitTime = e.leechTime + float timePassed = Time() - e.leechStartTime + waitTime -= timePassed + if ( waitTime > 0 ) + { + float startTime = Time() + while ( Time() < startTime + waitTime && player.UseButtonPressed() ) + { + WaitFrame() + } + } + + if ( player.UseButtonPressed() ) + e.success = true +} + +///////////////////////////////////////////////////////////// +bool function IsLeechTargetUsedAsAnimNode( entity target ) +{ + return target.AISetting_LeechAnimTag() != "" +} + +///////////////////////////////////////////////////////////// +void function PlayerLeechTargetAnimation( entity player, entity target, LeechActionInfo action, LeechData e ) +{ + Assert( action.isValid ) + vector targetStartOrg = target.GetOrigin() + vector targetStartAng = target.GetAngles() + + vector initialPlayerPosition = player.GetOrigin() + vector initialTargetPosition = target.GetOrigin() + + vector endOrigin = target.GetOrigin() + vector startOrigin = player.GetOrigin() + vector refVec = endOrigin - startOrigin + string animTag + + FirstPersonSequenceStruct playerSequence + + //--------------------------------------------------------- + // Leech anims played on the leech target, or at player position? + //--------------------------------------------------------- + if ( IsLeechTargetUsedAsAnimNode( target ) ) + { + e.ref = CreateLeechingScriptMoverBetweenEnts( player, target ) + animTag = target.AISetting_LeechAnimTag() + Assert( animTag != "" ) + e.ref.SetOrigin( target.GetOrigin() ) + e.ref.SetParent( target, animTag ) + } + else + { + e.ref = player + e.ref.SetOrigin( e.playerStartOrg ) + playerSequence.playerPushable = true + } + + e.ref.EndSignal( "OnDestroy" ) + + //----------------------------------------------------------------- + // Player FirstPersonSequence for the leeching + //----------------------------------------------------------------- + playerSequence.blendTime = 0.25 + playerSequence.attachment = "ref" + + //----------------------------------------------------------------- + // Only create FirstPersonSequence for leech target if anims exist + //----------------------------------------------------------------- + bool haveTargetSequence = false + FirstPersonSequenceStruct targetSequence + + if ( action.targetAnimation3pStart != "" ) + { + targetSequence = clone playerSequence + haveTargetSequence = true + } + + playerSequence.thirdPersonAnim = action.playerAnimation3pStart + playerSequence.thirdPersonAnimIdle = action.playerAnimation3pIdle + playerSequence.firstPersonAnim = action.playerAnimation1pStart + playerSequence.firstPersonAnimIdle = action.playerAnimation1pIdle + + entity viewmodel = player.GetFirstPersonProxy() + + if ( !HasAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_Pt1" ) ) + AddAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_Pt1", PlaySound_DataKnife_Hack_Spectre_Pt1 ) + + if ( !HasAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_Pt2" ) ) + AddAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_Pt2", PlaySound_DataKnife_Hack_Spectre_Pt2 ) + + if ( !HasAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_Pt3" ) ) + AddAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_Pt3", PlaySound_DataKnife_Hack_Spectre_Pt3 ) + + if ( !HasAnimEvent( viewmodel, "PlaySound_Spectre_Servo_Heavy_Short" ) ) + AddAnimEvent( viewmodel, "PlaySound_Spectre_Servo_Heavy_Short", PlaySound_Spectre_Servo_Heavy_Short ) + + if ( !HasAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_ArmorRattle" ) ) + AddAnimEvent( viewmodel, "PlaySound_DataKnife_Hack_Spectre_ArmorRattle", PlaySound_DataKnife_Hack_Spectre_ArmorRattle ) + + if ( haveTargetSequence ) + { + targetSequence.thirdPersonAnim = action.targetAnimation3pStart + targetSequence.thirdPersonAnimIdle = action.targetAnimation3pIdle + } + + playerSequence.noParent = true + + //----------------------------------- + // Data knife + //----------------------------------- + asset model = DATA_KNIFE_MODEL + + string knifeTag = GetTagForDataknife( target ) + entity thirdPersonKnife = CreatePropDynamic( model ) + SetTargetName( thirdPersonKnife, "thirdPersonKnife" ) + thirdPersonKnife.SetParent( player, knifeTag, false, 0.0 ) + e.knives.append( thirdPersonKnife ) + + SetForceDrawWhileParented( target, true ) + + //------------------------------------------------------------------------------ + // Play leech anim sequence for player, but only for target if leech anims exist + //------------------------------------------------------------------------------- + player.SetSyncedEntity( target ) + entity ref = e.ref + if ( haveTargetSequence ) + thread Animate_PlayerLeechTarget( targetSequence, target, ref ) + + waitthread FirstPersonSequence( playerSequence, player, null ) +} + + +//Basically copy pasted from CreateMeleeScriptMoverBetweenEnts +entity function CreateLeechingScriptMoverBetweenEnts( entity attacker, entity target ) +{ + vector endOrigin = target.GetOrigin() + vector startOrigin = attacker.GetOrigin() + vector refVec = endOrigin - startOrigin + + vector refAng = VectorToAngles( refVec ) + float pitch = refAng.x + if ( pitch > 180 ) + pitch -= 360 + if ( fabs( pitch ) > 35 ) //If pitch is too much, use angles from target + refAng = target.GetAngles() // Leech does it from behind target, so use target's angles. + + vector refPos = endOrigin - refVec * 0.5 + + entity ref = CreateOwnedScriptMover( attacker ) + ref.SetOrigin( refPos ) + ref.SetAngles( refAng ) + + return ref +} + +void function Animate_PlayerLeechTarget( FirstPersonSequenceStruct targetSequence, entity target, entity ref ) +{ + ref.EndSignal( "OnDestroy" ) + target.EndSignal( "OnDestroy" ) + waitthread FirstPersonSequence( targetSequence, target, ref ) +} + +void function PlayerExitLeechingAnim( entity player, entity target, LeechActionInfo action, LeechData e ) +{ + FirstPersonSequenceStruct playerSequence + playerSequence.blendTime = 0.3 + playerSequence.attachment = "ref" + playerSequence.teleport = false + playerSequence.noParent = true + playerSequence.playerPushable = true + + //-------------------------------------- + // Target animates only if he has anims + //--------------------------------------- + bool hasTargetSequence = false + FirstPersonSequenceStruct targetSequence + if ( action.targetAnimation3pEnd != "" ) + { + targetSequence = clone playerSequence + hasTargetSequence = true + } + + playerSequence.thirdPersonAnim = action.playerAnimation3pEnd + playerSequence.firstPersonAnim = action.playerAnimation1pEnd + playerSequence.snapPlayerFeetToEyes = false + + entity ref = e.ref + + if ( hasTargetSequence ) + { + targetSequence.thirdPersonAnim = action.targetAnimation3pEnd + thread FirstPersonSequence( targetSequence, target, ref ) + } + waitthread FirstPersonSequence( playerSequence, player, null ) + + //------------------------------------------------------------- + // Detach from rodeo if applicable (drones, superspectres, etc) + //------------------------------------------------------------- + if ( Rodeo_IsAttached( player ) ) + player.Signal( "RodeoOver" ) +} + +bool function IsBeingLeeched( entity npc ) +{ + return npc.ai.leechInProgress +} + +void function PlaySound_DataKnife_Hack_Spectre_Pt1( entity playerFirstPersonProxy ) +{ + entity player = playerFirstPersonProxy.GetOwner() + if ( !IsValid( player ) ) + return + + EmitSoundOnEntityOnlyToPlayer( player, player, "DataKnife_Hack_Spectre_Pt1" ) + +} + +void function PlaySound_DataKnife_Hack_Spectre_Pt2( entity playerFirstPersonProxy ) +{ + entity player = playerFirstPersonProxy.GetOwner() + if ( !IsValid( player ) ) + return + + EmitSoundOnEntityOnlyToPlayer( player, player, "DataKnife_Hack_Spectre_Pt2" ) + +} + +void function PlaySound_DataKnife_Hack_Spectre_Pt3( entity playerFirstPersonProxy ) +{ + entity player = playerFirstPersonProxy.GetOwner() + if ( !IsValid( player ) ) + return + + EmitSoundOnEntityOnlyToPlayer( player, player, "DataKnife_Hack_Spectre_Pt3" ) + +} + +void function PlaySound_Spectre_Servo_Heavy_Short( entity playerFirstPersonProxy ) +{ + entity player = playerFirstPersonProxy.GetOwner() + if ( !IsValid( player ) ) + return + + EmitSoundOnEntityOnlyToPlayer( player, player, "Spectre.Servo.Heavy.Short" ) + +} + +void function PlaySound_DataKnife_Hack_Spectre_ArmorRattle( entity playerFirstPersonProxy ) +{ + entity player = playerFirstPersonProxy.GetOwner() + if ( !IsValid( player ) ) + return + + EmitSoundOnEntityOnlyToPlayer( player, player, "DataKnife_Hack_Spectre_ArmorRattle" ) + +} \ No newline at end of file -- cgit v1.2.3