diff options
-rwxr-xr-x[-rw-r--r--] | Northstar.Custom/mod/scripts/vscripts/melee/_melee_synced_human.gnut | 1280 |
1 files changed, 641 insertions, 639 deletions
diff --git a/Northstar.Custom/mod/scripts/vscripts/melee/_melee_synced_human.gnut b/Northstar.Custom/mod/scripts/vscripts/melee/_melee_synced_human.gnut index fb0a40a6..efc3283d 100644..100755 --- a/Northstar.Custom/mod/scripts/vscripts/melee/_melee_synced_human.gnut +++ b/Northstar.Custom/mod/scripts/vscripts/melee/_melee_synced_human.gnut @@ -1,639 +1,641 @@ -untyped - -global function MeleeThread_PilotVsEnemy -global function MeleeSyncedServer_Init - -void function MeleeSyncedServer_Init() -{ - RegisterSignal( "NpcDealsExecutionDamage" ) -} - -bool function MeleeThread_PilotVsEnemy( SyncedMelee action, entity attacker, entity target ) -{ - // function off for reload scripts - return MeleeThread_PilotVsEnemyInternal( action, attacker, target ) -} - -bool function MeleeThread_PilotVsEnemyInternal( SyncedMelee action, entity attacker, entity target ) -{ - Assert( IsHumanSized( target ), target + " is not human sized melee target" ) - Assert( attacker.IsPlayer() && IsHumanSized( attacker ), attacker + " is not human sized player attacker" ) - Assert( IsAlive( attacker ) ) - Assert( IsAlive( target ) ) - - bool isAttackerRef - if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) >= eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION ) - { - action.attackerAnimation1p = "ptpov_rspn101_melee_necksnap_rear" - action.attackerAnimation3p = "pt_rspn101_melee_necksnap_rear" - action.targetAnimation1p = "ptpov_melee_necksnap_rear_attacked" - action.targetAnimation3p = "pt_melee_necksnap_rear_attacked" - action.isAttackerRef = false - action.minDot = 0.2 - - /* here be dragons - action.attackerAnimation1p = "ptpov_assassin_melee_necksnap_rear" - action.attackerAnimation3p = "pt_assassin_melee_necksnap_rear" - action.targetAnimation1p = "ptpov_assassin_melee_necksnap_rear_attacked" - action.targetAnimation3p = "pt_assassin_melee_necksnap_rear_attacked" - action.attachModel1p = $"models/weapons/bolo_sword/w_bolo_sword.mdl" - action.attachTag1p = "PROPGUN" - */ - - // need to hardcode false here otherwise it just doesnt work for some reason?? - isAttackerRef = false - } - else - isAttackerRef = IsAttackerRef( action, target ) - - vector attackerOrigin = attacker.GetOrigin() - vector targetOrigin = target.GetOrigin() - - attacker.EndSignal( "OnDestroy" ) - target.EndSignal( "OnDestroy" ) - - if ( IsSingleplayer() ) - { - if ( attacker.IsPlayer() ) - { - if ( IsCloaked( attacker ) ) - { - UnlockAchievement( attacker, achievements.CLOAK_TAKEDOWN ) - } - } - } - - OnThreadEnd( - function() : ( attacker, target, attackerOrigin, targetOrigin, action, isAttackerRef ) - { - if ( IsValid( attacker ) ) - attacker.ClearParent() - - if ( IsValid( target ) ) - target.ClearParent() - - - if ( IsValid( attacker ) ) - { - attacker.PlayerMelee_SetState( PLAYER_MELEE_STATE_NONE ) - } - - // Note that the original attacker/target origins are not guarranteed to be a safe spot now because we have moving geo in the game. - // Whoever is the 'ref' will be in a safe position though, so we can always use the origin of the person who has been designated as the 'ref'. - if ( IsAlive( attacker ) ) - { - if ( !isAttackerRef && IsValid( target ) ) - { - PutEntityInSafeSpot( attacker, target, null, target.GetOrigin(), attacker.GetOrigin() ) - } - else - { - PutEntityInSafeSpot( attacker, target, null, attacker.GetOrigin(), attacker.GetOrigin() ) - } - - } - - if ( IsValid( target ) ) - { - target.ClearParent() - - if ( IsAlive( target ) ) - { - // Note that the original target origin is not guarranteed to be a safe spot now because we have moving geo in the game now. - if ( isAttackerRef && IsValid( attacker ) ) - { - - PutEntityInSafeSpot( target, attacker, null, attacker.GetOrigin(), target.GetOrigin() ) - } - else - { - PutEntityInSafeSpot( target, attacker, null, target.GetOrigin(), target.GetOrigin() ) - } - } - } - } - ) - - thread MeleeThread_PilotVsEnemy_Attacker( action, attacker, target, isAttackerRef ) - // target's sequence is longer - waitthread MeleeThread_PilotVsEnemy_Target( action, attacker, target, isAttackerRef ) - - attacker.Signal( "SyncedMeleeComplete" ) - return true -} - -struct PilotVsEnemyStruct -{ - bool clearInvulnerable = false - bool wasCloaked = false - float cloakEndTime = 0.0 -} - -void function DisableCloakBeforeMelee( entity player, PilotVsEnemyStruct dataStruct ) -{ - if ( IsCloaked( player ) ) - { - dataStruct.wasCloaked = true - dataStruct.cloakEndTime = player.GetCloakEndTime() - DisableCloak( player, 0.0 ) - } -} - -void function RestoreCloakAfterMelee( entity player, PilotVsEnemyStruct dataStruct ) -{ - if ( !IsAlive( player ) ) - return - - if ( !dataStruct.wasCloaked ) - return - - float remainingCloakDuration = max( 0.0, dataStruct.cloakEndTime - Time() ) - if ( remainingCloakDuration > CLOAK_FADE_IN ) //Has to be higher than fade in duration, otherwise will cloak forever - EnableCloak( player, remainingCloakDuration, CLOAK_FADE_IN ) -} - -void function HandleCloakExecutionWithCloakedAttacker( entity player, PilotVsEnemyStruct dataStruct, SyncedMelee action ) -{ - if ( !IsCloaked( player ) ) - return //No need to run DisableCloakBeforeMelee() either - - float attackerSequenceEndTime = Time() + player.GetSequenceDuration( action.attackerAnimation3p ) - float scheduledCloakEndTime = player.GetCloakEndTime() - - //printt( "attackerSequenceEndTime: " + attackerSequenceEndTime + ", scheduledCloakEndTime: " + scheduledCloakEndTime ) - - if ( scheduledCloakEndTime > attackerSequenceEndTime ) - { - //printt( "Cloak ability lasts longer than execution sequence, just doing DisableCloakBeforeMelee" ) - player.SetCloakFlicker( 0.0, 0.0 ) //Turn off flicker; this is normally not a problem for other executions since cloak is turned off for the entirety of those executions - DisableCloakBeforeMelee( player, dataStruct ) - } - else - { - //Cloak would normally run out during the animation of this execution, which is disruptive to the presentation of cloak animation, so just stop cloak now for good and prevent it from coming back. - //printt( "Cloak ability is shorter than execution sequence, DisableCloak now and stop it from coming back" ) - dataStruct.wasCloaked = true //Have to do this to mark player was cloaked during start of execution, so we can track the stat correctly - dataStruct.cloakEndTime = Time() - DisableCloak( player, 0.0 ) - player.Signal( "KillHandleCloakEnd" ) - } -} - - -void function MeleeThread_PilotVsEnemy_Attacker( SyncedMelee action, entity attacker, entity target, bool isAttackerRef ) -{ - attacker.EndSignal( "OnAnimationDone" ) - attacker.EndSignal( "OnAnimationInterrupted" ) - attacker.EndSignal( "OnDeath" ) - attacker.EndSignal( "ScriptAnimStop" ) - - attacker.EndSignal( "OnDestroy" ) - Assert( IsValid( target ) ) - target.EndSignal( "OnDestroy" ) - - - foreach ( AnimEventData animEventData in action.attacker3pAnimEvents ) - { - AddAnimEvent( attacker, animEventData.eventName, animEventData.callback, animEventData.optionalVar ) - } - AddAnimEvent( attacker, "synced_melee_enable_planting", EnablePlantingOnEntity ) - - PilotVsEnemyStruct dataStruct - OnThreadEnd( - function() : ( attacker, target, action, dataStruct ) - { - if ( IsValid( attacker ) ) - { - DeleteAnimEvent( attacker, "synced_melee_enable_planting" ) - - if ( dataStruct.clearInvulnerable ) - { - attacker.ClearInvulnerable() - } - - if ( attacker.IsPlayer() ) - { - attacker.PlayerMelee_ExecutionEndAttacker() - ClearPlayerAnimViewEntity( attacker ) - DeployAndEnableWeapons( attacker ) - - RestoreCloakAfterMelee( attacker, dataStruct ) - #if MP - IncrementStatForPilotExecutionWhileCloaked( attacker, target, dataStruct ) - #endif - } - - foreach ( AnimEventData animEventData in action.attacker3pAnimEvents ) - { - DeleteAnimEvent( attacker, animEventData.eventName ) - } - } - - if ( !IsAlive( attacker ) ) - attacker.Anim_Stop() - } - ) - - FirstPersonSequenceStruct attackerSequence - attackerSequence.blendTime = 0.4 - attackerSequence.attachment = "ref" - attackerSequence.thirdPersonAnim = action.attackerAnimation3p - attackerSequence.firstPersonAnim = action.attackerAnimation1p - attackerSequence.thirdPersonCameraAttachments = [action.thirdPersonCameraAttachment] - attackerSequence.thirdPersonCameraVisibilityChecks = true - - if ( isAttackerRef ) - { - attackerSequence.noParent = true - attackerSequence.playerPushable = true - attackerSequence.enablePlanting = true - } - else - { - attackerSequence.useAnimatedRefAttachment = true - } - - float duration = attacker.GetSequenceDuration( attackerSequence.thirdPersonAnim ) - - if ( attacker.IsPlayer() ) - { - float executionEndTime = Time() + duration - attacker.PlayerMelee_ExecutionStartAttacker( executionEndTime ) - attacker.Lunge_ClearTarget() - HolsterViewModelAndDisableWeapons( attacker ) - - if ( action.ref == "execution_cloak" ) //Special case for cloak execution - { - HandleCloakExecutionWithCloakedAttacker( attacker, dataStruct, action ) - } - else - { - DisableCloakBeforeMelee( attacker, dataStruct ) - } - - if ( IsSingleplayer() ) - { - dataStruct.clearInvulnerable = true - attacker.SetInvulnerable() - thread LowerEnemyAccuracy( attacker, duration ) - } - } - - if ( isAttackerRef ) - { - #if MP - if ( ShouldForce1PFirstPersonSequence() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION ) - FirstPersonSequenceForce1P( attackerSequence, attacker ) - #endif - - thread FirstPersonSequence( attackerSequence, attacker ) - } - else - { - #if MP - if ( ShouldForce1PFirstPersonSequence() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION ) - FirstPersonSequenceForce1P( attackerSequence, attacker, target ) - #endif - - thread FirstPersonSequence( attackerSequence, attacker, target ) - } - - wait duration -} - - -void function MeleeThread_PilotVsEnemy_Target( SyncedMelee action, entity attacker, entity target, bool isAttackerRef ) -{ - attacker.EndSignal( "OnAnimationDone" ) - attacker.EndSignal( "OnAnimationInterrupted" ) - attacker.EndSignal( "OnDeath" ) - attacker.EndSignal( "ScriptAnimStop" ) - - attacker.EndSignal( "OnDestroy" ) - Assert( IsValid( target ) ) - target.EndSignal( "OnDestroy" ) - - foreach ( AnimEventData animEventData in action.target3pAnimEvents ) - { - AddAnimEvent( target, animEventData.eventName, animEventData.callback, animEventData.optionalVar ) - } - AddAnimEvent( target, "synced_melee_enable_planting", EnablePlantingOnEntity ) - - PilotVsEnemyStruct dataStruct - - OnThreadEnd( - function() : ( attacker, target, action, dataStruct ) - { - if ( IsValid( target ) ) - { - if ( target.IsNPC() && IsMultiplayer() ) - { - SetForceDrawWhileParented( target, false ) - } - - TargetClearedExecuted( target ) - DeleteAnimEvent( target, "mark_for_death" ) - DeleteAnimEvent( target, "phase_gib" ) - - foreach ( AnimEventData animEventData in action.target3pAnimEvents ) - { - DeleteAnimEvent( target, animEventData.eventName ) - } - DeleteAnimEvent( target, "synced_melee_enable_planting" ) - - bool isAlive = IsAlive( target ) - - if ( target.IsPlayer() ) - { - EnableOffhandWeapons( target ) - if ( isAlive ) - target.DeployWeapon() - } - - if ( isAlive ) - { - if ( target.e.markedForExecutionDeath ) //Kill off target if he already reached blackout part of melee - { - entity killCreditAttacker = null //If the attacker disconnected, we don't have a player to give credit to, that's fine. Script will not error - if ( IsValid( target.e.syncedMeleeAttacker ) ) - killCreditAttacker = target.e.syncedMeleeAttacker - //printt( "Killing off target " + target + " because he already reached blackout part of execution!" ) - - int damageAmount = target.GetMaxHealth() + 1 - target.TakeDamage( damageAmount, killCreditAttacker, killCreditAttacker, { forceKill = true, damageType = DMG_MELEE_EXECUTION, damageSourceId = eDamageSourceId.human_execution } ) - //markedForExecutionDeath will be cleared in MarkForDeath() which sets it in the first place - } - - if ( target.IsPlayer() ) - RestoreCloakAfterMelee( target, dataStruct ) - } - - if ( IsValid( target.e.syncedMeleeAttacker ) ) - { - if ( IsValid( target.e.lastSyncedMeleeAttacker ) ) - { - target.e.lastSyncedMeleeAttacker = null - } - - target.e.lastSyncedMeleeAttacker = target.e.syncedMeleeAttacker - target.e.syncedMeleeAttacker = null - } - } - } - ) - - TargetSetExecutedBy( target, attacker ) - - AddAnimEvent( target, "mark_for_death", MarkForDeath ) - AddAnimEvent( target, "phase_gib", PhaseGib ) - - FirstPersonSequenceStruct targetSequence - targetSequence.blendTime = 0.25 - targetSequence.attachment = "ref" - targetSequence.thirdPersonAnim = action.targetAnimation3p - targetSequence.thirdPersonCameraAttachments = [action.thirdPersonCameraAttachment] - targetSequence.thirdPersonCameraVisibilityChecks = true - - if ( isAttackerRef ) - { - targetSequence.useAnimatedRefAttachment = true - if ( target.IsNPC() && IsMultiplayer() ) - { - SetForceDrawWhileParented( target, true ) - } - } - else - { - targetSequence.noParent = true - targetSequence.playerPushable = true - targetSequence.enablePlanting = true - } - - - if ( target.IsPlayer() ) - { - HolsterViewModelAndDisableWeapons( target ) - targetSequence.firstPersonAnim = action.targetAnimation1p - DisableCloakBeforeMelee( target, dataStruct ) - } - - if ( attacker.IsPlayer() ) - { - if ( MeleeTargetrequiresDataKnife( target ) ) - { - string tag = GetTagForKnifeMeleeTarget( target ) - thread AttachPlayerModelForDuration( attacker, DATA_KNIFE_MODEL, tag, 2.2 ) - } - else if ( action.attachTag1p != "" && action.attachModel1p != $"" ) - { - thread AttachPlayerModelForDuration( attacker, action.attachModel1p, action.attachTag1p, 2.2 ) - } - } - - if ( isAttackerRef ) - { - #if MP - if ( ShouldForce1PFirstPersonSequence() && target.IsPlayer() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION ) - FirstPersonSequenceForce1P( targetSequence, target, attacker ) - #endif - - waitthread FirstPersonSequence( targetSequence, target, attacker ) - } - else - { - #if MP - if ( ShouldForce1PFirstPersonSequence() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION ) - FirstPersonSequenceForce1P( targetSequence, target ) - #endif - - waitthread FirstPersonSequence( targetSequence, target ) - } -} - -#if MP -void function IncrementStatForPilotExecutionWhileCloaked( entity attacker, entity target, PilotVsEnemyStruct dataStruct ) -{ - if ( !IsAlive( attacker ) ) - return - - if ( IsAlive( target ) ) - return - - if ( !target.IsPlayer() ) - return - - if ( !dataStruct.wasCloaked ) - return - - IncrementPlayerDidPilotExecutionWhileCloaked( attacker ) //Kinda clumsy we have to do it here instead of where all the other kill stats are incremented. Mainly because we turn cloak off at the start of execution so you can't do it where all the other kill stats are incremented -} -#endif - -void function TargetClearedExecuted( entity target ) -{ - target.ClearParent() - target.Solid() - if ( target.ContextAction_IsMeleeExecution() ) - target.PlayerMelee_ExecutionEndTarget() - if ( target.IsPlayer() ) - ClearPlayerAnimViewEntity( target ) -} - -void function TargetSetExecutedBy( entity target, entity attacker ) -{ - //Break out of context actions like hacking control panel etc - if ( target.ContextAction_IsActive() ) - target.Anim_Stop() - - target.PlayerMelee_ExecutionStartTarget( attacker ) - target.e.syncedMeleeAttacker = attacker - target.NotSolid() -} - -bool function MeleeTargetrequiresDataKnife( entity target ) -{ - if ( IsProwler( target ) ) - return true - - if ( IsPilotElite( target ) ) - return true - - return false -} - -string function GetTagForKnifeMeleeTarget( entity target ) -{ - Assert( MeleeTargetrequiresDataKnife( target ) ) - - if ( IsProwler( target ) ) - return "PROPGUN" - - if ( IsPilotElite( target ) ) - return "KNIFE" - - unreachable -} - -function AttachPlayerModelForDuration( var player, asset modelName, var tag, var time ) -{ - expect entity( player ) - - if ( !IsValid( player ) ) - return - - Assert( IsValid( tag ), "No tag specified for player" ) - - entity viewModel = player.GetFirstPersonProxy() //JFS: Defensive fix for player not having view models sometimes - if ( !IsValid( viewModel ) ) - return - - if ( !EntHasModelSet( viewModel ) ) - return - - entity model = CreatePropDynamic( modelName ) - model.SetParent( viewModel, tag, false, 0.0 ) - - OnThreadEnd( - function() : ( model ) - { - if ( IsValid( model ) ) - model.Destroy() - } - ) - - player.EndSignal( "OnDeath" ) - - wait time -} - -void function MarkForDeath( entity target ) -{ - if ( target.IsNPC() ) - { - //printt("Killing marked for death npc " + target ) - //Just kill off NPC now, otherwise it will play pain animations on death - CodeCallback_OnMeleeKilled( target ) - return - } - - //printt("marking player " + target + " for death") - target.e.markedForExecutionDeath = true //This will kill off the player even if the execution animation is interruped from this point forward - - target.EndSignal( "OnDeath" ) - - OnThreadEnd( - function() : ( target ) - { - target.e.markedForExecutionDeath = false - } - ) - - WaitForever() - -} - -void function PhaseGib( entity target ) -{ - if ( !IsAlive( target ) ) - return - - target.ClearInvulnerable() - - entity attacker - if ( IsValid( target.e.syncedMeleeAttacker ) ) - { - attacker = target.e.syncedMeleeAttacker - } - else if ( IsValid( target.e.lastSyncedMeleeAttacker ) ) - { - attacker = target.e.lastSyncedMeleeAttacker - } - else - { - attacker = null - } - - int damageAmount = target.GetMaxHealth() + 1 - target.TakeDamage( damageAmount , attacker, attacker, { forceKill = true, damageType = DMG_MELEE_EXECUTION, damageSourceId = eDamageSourceId.human_execution, scriptType = DF_NO_INDICATOR | DF_GIB } ) -} - - -entity function CreateSyncedMeleeRef( entity attacker, entity target, SyncedMelee action ) -{ - entity ref = CreateMeleeScriptMoverBetweenEnts( attacker, target ) - - vector angles = target.GetAngles() - angles.x = ref.GetAngles().x - - ref.SetAngles( angles ) - if ( action.animRefPos == "attacker" ) - ref.SetOrigin( attacker.GetOrigin() ) - else - ref.SetOrigin( target.GetOrigin() ) - return ref -} - -void function ApplyGruntExecutionDamage( entity ref, entity attacker, entity target, float damageDealt ) -{ - ref.EndSignal( "OnDestroy" ) - attacker.EndSignal( "OnDeath" ) - target.EndSignal( "OnDeath" ) - - for ( ;; ) - { - table results = attacker.WaitSignal( "NpcDealsExecutionDamage" ) - float damage - switch ( results.parm ) - { - case "lethal": - damage = float( target.GetMaxHealth() ) - break - - case "nonlethal": - damage = min( target.GetHealth() - 10, target.GetMaxHealth() * damageDealt ) - break - } - - target.TakeDamage( damage, attacker, attacker, { damageSourceId=eDamageSourceId.human_execution, scriptType = DF_RAGDOLL } ) - } -} +untyped
+
+global function MeleeThread_PilotVsEnemy
+global function MeleeSyncedServer_Init
+
+void function MeleeSyncedServer_Init()
+{
+ RegisterSignal( "NpcDealsExecutionDamage" )
+}
+
+bool function MeleeThread_PilotVsEnemy( SyncedMelee action, entity attacker, entity target )
+{
+ // function off for reload scripts
+ return MeleeThread_PilotVsEnemyInternal( action, attacker, target )
+}
+
+bool function MeleeThread_PilotVsEnemyInternal( SyncedMelee action, entity attacker, entity target )
+{
+ Assert( IsHumanSized( target ), target + " is not human sized melee target" )
+ Assert( attacker.IsPlayer() && IsHumanSized( attacker ), attacker + " is not human sized player attacker" )
+ Assert( IsAlive( attacker ) )
+ Assert( IsAlive( target ) )
+
+ bool isAttackerRef
+ #if MP
+ if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) >= eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION )
+ {
+ action.attackerAnimation1p = "ptpov_rspn101_melee_necksnap_rear"
+ action.attackerAnimation3p = "pt_rspn101_melee_necksnap_rear"
+ action.targetAnimation1p = "ptpov_melee_necksnap_rear_attacked"
+ action.targetAnimation3p = "pt_melee_necksnap_rear_attacked"
+ action.isAttackerRef = false
+ action.minDot = 0.2
+
+ /* here be dragons
+ action.attackerAnimation1p = "ptpov_assassin_melee_necksnap_rear"
+ action.attackerAnimation3p = "pt_assassin_melee_necksnap_rear"
+ action.targetAnimation1p = "ptpov_assassin_melee_necksnap_rear_attacked"
+ action.targetAnimation3p = "pt_assassin_melee_necksnap_rear_attacked"
+ action.attachModel1p = $"models/weapons/bolo_sword/w_bolo_sword.mdl"
+ action.attachTag1p = "PROPGUN"
+ */
+
+ // need to hardcode false here otherwise it just doesnt work for some reason??
+ isAttackerRef = false
+ }
+ else
+ #endif
+ isAttackerRef = IsAttackerRef( action, target )
+
+ vector attackerOrigin = attacker.GetOrigin()
+ vector targetOrigin = target.GetOrigin()
+
+ attacker.EndSignal( "OnDestroy" )
+ target.EndSignal( "OnDestroy" )
+
+ if ( IsSingleplayer() )
+ {
+ if ( attacker.IsPlayer() )
+ {
+ if ( IsCloaked( attacker ) )
+ {
+ UnlockAchievement( attacker, achievements.CLOAK_TAKEDOWN )
+ }
+ }
+ }
+
+ OnThreadEnd(
+ function() : ( attacker, target, attackerOrigin, targetOrigin, action, isAttackerRef )
+ {
+ if ( IsValid( attacker ) )
+ attacker.ClearParent()
+
+ if ( IsValid( target ) )
+ target.ClearParent()
+
+
+ if ( IsValid( attacker ) )
+ {
+ attacker.PlayerMelee_SetState( PLAYER_MELEE_STATE_NONE )
+ }
+
+ // Note that the original attacker/target origins are not guarranteed to be a safe spot now because we have moving geo in the game.
+ // Whoever is the 'ref' will be in a safe position though, so we can always use the origin of the person who has been designated as the 'ref'.
+ if ( IsAlive( attacker ) )
+ {
+ if ( !isAttackerRef && IsValid( target ) )
+ {
+ PutEntityInSafeSpot( attacker, target, null, target.GetOrigin(), attacker.GetOrigin() )
+ }
+ else
+ {
+ PutEntityInSafeSpot( attacker, target, null, attacker.GetOrigin(), attacker.GetOrigin() )
+ }
+
+ }
+
+ if ( IsValid( target ) )
+ {
+ target.ClearParent()
+
+ if ( IsAlive( target ) )
+ {
+ // Note that the original target origin is not guarranteed to be a safe spot now because we have moving geo in the game now.
+ if ( isAttackerRef && IsValid( attacker ) )
+ {
+
+ PutEntityInSafeSpot( target, attacker, null, attacker.GetOrigin(), target.GetOrigin() )
+ }
+ else
+ {
+ PutEntityInSafeSpot( target, attacker, null, target.GetOrigin(), target.GetOrigin() )
+ }
+ }
+ }
+ }
+ )
+
+ thread MeleeThread_PilotVsEnemy_Attacker( action, attacker, target, isAttackerRef )
+ // target's sequence is longer
+ waitthread MeleeThread_PilotVsEnemy_Target( action, attacker, target, isAttackerRef )
+
+ attacker.Signal( "SyncedMeleeComplete" )
+ return true
+}
+
+struct PilotVsEnemyStruct
+{
+ bool clearInvulnerable = false
+ bool wasCloaked = false
+ float cloakEndTime = 0.0
+}
+
+void function DisableCloakBeforeMelee( entity player, PilotVsEnemyStruct dataStruct )
+{
+ if ( IsCloaked( player ) )
+ {
+ dataStruct.wasCloaked = true
+ dataStruct.cloakEndTime = player.GetCloakEndTime()
+ DisableCloak( player, 0.0 )
+ }
+}
+
+void function RestoreCloakAfterMelee( entity player, PilotVsEnemyStruct dataStruct )
+{
+ if ( !IsAlive( player ) )
+ return
+
+ if ( !dataStruct.wasCloaked )
+ return
+
+ float remainingCloakDuration = max( 0.0, dataStruct.cloakEndTime - Time() )
+ if ( remainingCloakDuration > CLOAK_FADE_IN ) //Has to be higher than fade in duration, otherwise will cloak forever
+ EnableCloak( player, remainingCloakDuration, CLOAK_FADE_IN )
+}
+
+void function HandleCloakExecutionWithCloakedAttacker( entity player, PilotVsEnemyStruct dataStruct, SyncedMelee action )
+{
+ if ( !IsCloaked( player ) )
+ return //No need to run DisableCloakBeforeMelee() either
+
+ float attackerSequenceEndTime = Time() + player.GetSequenceDuration( action.attackerAnimation3p )
+ float scheduledCloakEndTime = player.GetCloakEndTime()
+
+ //printt( "attackerSequenceEndTime: " + attackerSequenceEndTime + ", scheduledCloakEndTime: " + scheduledCloakEndTime )
+
+ if ( scheduledCloakEndTime > attackerSequenceEndTime )
+ {
+ //printt( "Cloak ability lasts longer than execution sequence, just doing DisableCloakBeforeMelee" )
+ player.SetCloakFlicker( 0.0, 0.0 ) //Turn off flicker; this is normally not a problem for other executions since cloak is turned off for the entirety of those executions
+ DisableCloakBeforeMelee( player, dataStruct )
+ }
+ else
+ {
+ //Cloak would normally run out during the animation of this execution, which is disruptive to the presentation of cloak animation, so just stop cloak now for good and prevent it from coming back.
+ //printt( "Cloak ability is shorter than execution sequence, DisableCloak now and stop it from coming back" )
+ dataStruct.wasCloaked = true //Have to do this to mark player was cloaked during start of execution, so we can track the stat correctly
+ dataStruct.cloakEndTime = Time()
+ DisableCloak( player, 0.0 )
+ player.Signal( "KillHandleCloakEnd" )
+ }
+}
+
+
+void function MeleeThread_PilotVsEnemy_Attacker( SyncedMelee action, entity attacker, entity target, bool isAttackerRef )
+{
+ attacker.EndSignal( "OnAnimationDone" )
+ attacker.EndSignal( "OnAnimationInterrupted" )
+ attacker.EndSignal( "OnDeath" )
+ attacker.EndSignal( "ScriptAnimStop" )
+
+ attacker.EndSignal( "OnDestroy" )
+ Assert( IsValid( target ) )
+ target.EndSignal( "OnDestroy" )
+
+
+ foreach ( AnimEventData animEventData in action.attacker3pAnimEvents )
+ {
+ AddAnimEvent( attacker, animEventData.eventName, animEventData.callback, animEventData.optionalVar )
+ }
+ AddAnimEvent( attacker, "synced_melee_enable_planting", EnablePlantingOnEntity )
+
+ PilotVsEnemyStruct dataStruct
+ OnThreadEnd(
+ function() : ( attacker, target, action, dataStruct )
+ {
+ if ( IsValid( attacker ) )
+ {
+ DeleteAnimEvent( attacker, "synced_melee_enable_planting" )
+
+ if ( dataStruct.clearInvulnerable )
+ {
+ attacker.ClearInvulnerable()
+ }
+
+ if ( attacker.IsPlayer() )
+ {
+ attacker.PlayerMelee_ExecutionEndAttacker()
+ ClearPlayerAnimViewEntity( attacker )
+ DeployAndEnableWeapons( attacker )
+
+ RestoreCloakAfterMelee( attacker, dataStruct )
+ #if MP
+ IncrementStatForPilotExecutionWhileCloaked( attacker, target, dataStruct )
+ #endif
+ }
+
+ foreach ( AnimEventData animEventData in action.attacker3pAnimEvents )
+ {
+ DeleteAnimEvent( attacker, animEventData.eventName )
+ }
+ }
+
+ if ( !IsAlive( attacker ) )
+ attacker.Anim_Stop()
+ }
+ )
+
+ FirstPersonSequenceStruct attackerSequence
+ attackerSequence.blendTime = 0.4
+ attackerSequence.attachment = "ref"
+ attackerSequence.thirdPersonAnim = action.attackerAnimation3p
+ attackerSequence.firstPersonAnim = action.attackerAnimation1p
+ attackerSequence.thirdPersonCameraAttachments = [action.thirdPersonCameraAttachment]
+ attackerSequence.thirdPersonCameraVisibilityChecks = true
+
+ if ( isAttackerRef )
+ {
+ attackerSequence.noParent = true
+ attackerSequence.playerPushable = true
+ attackerSequence.enablePlanting = true
+ }
+ else
+ {
+ attackerSequence.useAnimatedRefAttachment = true
+ }
+
+ float duration = attacker.GetSequenceDuration( attackerSequence.thirdPersonAnim )
+
+ if ( attacker.IsPlayer() )
+ {
+ float executionEndTime = Time() + duration
+ attacker.PlayerMelee_ExecutionStartAttacker( executionEndTime )
+ attacker.Lunge_ClearTarget()
+ HolsterViewModelAndDisableWeapons( attacker )
+
+ if ( action.ref == "execution_cloak" ) //Special case for cloak execution
+ {
+ HandleCloakExecutionWithCloakedAttacker( attacker, dataStruct, action )
+ }
+ else
+ {
+ DisableCloakBeforeMelee( attacker, dataStruct )
+ }
+
+ if ( IsSingleplayer() )
+ {
+ dataStruct.clearInvulnerable = true
+ attacker.SetInvulnerable()
+ thread LowerEnemyAccuracy( attacker, duration )
+ }
+ }
+
+ if ( isAttackerRef )
+ {
+ #if MP
+ if ( ShouldForce1PFirstPersonSequence() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION )
+ FirstPersonSequenceForce1P( attackerSequence, attacker )
+ #endif
+
+ thread FirstPersonSequence( attackerSequence, attacker )
+ }
+ else
+ {
+ #if MP
+ if ( ShouldForce1PFirstPersonSequence() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION )
+ FirstPersonSequenceForce1P( attackerSequence, attacker, target )
+ #endif
+
+ thread FirstPersonSequence( attackerSequence, attacker, target )
+ }
+
+ wait duration
+}
+
+
+void function MeleeThread_PilotVsEnemy_Target( SyncedMelee action, entity attacker, entity target, bool isAttackerRef )
+{
+ attacker.EndSignal( "OnAnimationDone" )
+ attacker.EndSignal( "OnAnimationInterrupted" )
+ attacker.EndSignal( "OnDeath" )
+ attacker.EndSignal( "ScriptAnimStop" )
+
+ attacker.EndSignal( "OnDestroy" )
+ Assert( IsValid( target ) )
+ target.EndSignal( "OnDestroy" )
+
+ foreach ( AnimEventData animEventData in action.target3pAnimEvents )
+ {
+ AddAnimEvent( target, animEventData.eventName, animEventData.callback, animEventData.optionalVar )
+ }
+ AddAnimEvent( target, "synced_melee_enable_planting", EnablePlantingOnEntity )
+
+ PilotVsEnemyStruct dataStruct
+
+ OnThreadEnd(
+ function() : ( attacker, target, action, dataStruct )
+ {
+ if ( IsValid( target ) )
+ {
+ if ( target.IsNPC() && IsMultiplayer() )
+ {
+ SetForceDrawWhileParented( target, false )
+ }
+
+ TargetClearedExecuted( target )
+ DeleteAnimEvent( target, "mark_for_death" )
+ DeleteAnimEvent( target, "phase_gib" )
+
+ foreach ( AnimEventData animEventData in action.target3pAnimEvents )
+ {
+ DeleteAnimEvent( target, animEventData.eventName )
+ }
+ DeleteAnimEvent( target, "synced_melee_enable_planting" )
+
+ bool isAlive = IsAlive( target )
+
+ if ( target.IsPlayer() )
+ {
+ EnableOffhandWeapons( target )
+ if ( isAlive )
+ target.DeployWeapon()
+ }
+
+ if ( isAlive )
+ {
+ if ( target.e.markedForExecutionDeath ) //Kill off target if he already reached blackout part of melee
+ {
+ entity killCreditAttacker = null //If the attacker disconnected, we don't have a player to give credit to, that's fine. Script will not error
+ if ( IsValid( target.e.syncedMeleeAttacker ) )
+ killCreditAttacker = target.e.syncedMeleeAttacker
+ //printt( "Killing off target " + target + " because he already reached blackout part of execution!" )
+
+ int damageAmount = target.GetMaxHealth() + 1
+ target.TakeDamage( damageAmount, killCreditAttacker, killCreditAttacker, { forceKill = true, damageType = DMG_MELEE_EXECUTION, damageSourceId = eDamageSourceId.human_execution } )
+ //markedForExecutionDeath will be cleared in MarkForDeath() which sets it in the first place
+ }
+
+ if ( target.IsPlayer() )
+ RestoreCloakAfterMelee( target, dataStruct )
+ }
+
+ if ( IsValid( target.e.syncedMeleeAttacker ) )
+ {
+ if ( IsValid( target.e.lastSyncedMeleeAttacker ) )
+ {
+ target.e.lastSyncedMeleeAttacker = null
+ }
+
+ target.e.lastSyncedMeleeAttacker = target.e.syncedMeleeAttacker
+ target.e.syncedMeleeAttacker = null
+ }
+ }
+ }
+ )
+
+ TargetSetExecutedBy( target, attacker )
+
+ AddAnimEvent( target, "mark_for_death", MarkForDeath )
+ AddAnimEvent( target, "phase_gib", PhaseGib )
+
+ FirstPersonSequenceStruct targetSequence
+ targetSequence.blendTime = 0.25
+ targetSequence.attachment = "ref"
+ targetSequence.thirdPersonAnim = action.targetAnimation3p
+ targetSequence.thirdPersonCameraAttachments = [action.thirdPersonCameraAttachment]
+ targetSequence.thirdPersonCameraVisibilityChecks = true
+
+ if ( isAttackerRef )
+ {
+ targetSequence.useAnimatedRefAttachment = true
+ if ( target.IsNPC() && IsMultiplayer() )
+ {
+ SetForceDrawWhileParented( target, true )
+ }
+ }
+ else
+ {
+ targetSequence.noParent = true
+ targetSequence.playerPushable = true
+ targetSequence.enablePlanting = true
+ }
+
+
+ if ( target.IsPlayer() )
+ {
+ HolsterViewModelAndDisableWeapons( target )
+ targetSequence.firstPersonAnim = action.targetAnimation1p
+ DisableCloakBeforeMelee( target, dataStruct )
+ }
+
+ if ( attacker.IsPlayer() )
+ {
+ if ( MeleeTargetrequiresDataKnife( target ) )
+ {
+ string tag = GetTagForKnifeMeleeTarget( target )
+ thread AttachPlayerModelForDuration( attacker, DATA_KNIFE_MODEL, tag, 2.2 )
+ }
+ else if ( action.attachTag1p != "" && action.attachModel1p != $"" )
+ {
+ thread AttachPlayerModelForDuration( attacker, action.attachModel1p, action.attachTag1p, 2.2 )
+ }
+ }
+
+ if ( isAttackerRef )
+ {
+ #if MP
+ if ( ShouldForce1PFirstPersonSequence() && target.IsPlayer() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION )
+ FirstPersonSequenceForce1P( targetSequence, target, attacker )
+ #endif
+
+ waitthread FirstPersonSequence( targetSequence, target, attacker )
+ }
+ else
+ {
+ #if MP
+ if ( ShouldForce1PFirstPersonSequence() && GetCurrentPlaylistVarInt( "fp_embark_enabled", eFirstPersonSequenceForce1PSetting.DISABLED ) < eFirstPersonSequenceForce1PSetting.CLASSIC_EXECUTION )
+ FirstPersonSequenceForce1P( targetSequence, target )
+ #endif
+
+ waitthread FirstPersonSequence( targetSequence, target )
+ }
+}
+
+#if MP
+void function IncrementStatForPilotExecutionWhileCloaked( entity attacker, entity target, PilotVsEnemyStruct dataStruct )
+{
+ if ( !IsAlive( attacker ) )
+ return
+
+ if ( IsAlive( target ) )
+ return
+
+ if ( !target.IsPlayer() )
+ return
+
+ if ( !dataStruct.wasCloaked )
+ return
+
+ IncrementPlayerDidPilotExecutionWhileCloaked( attacker ) //Kinda clumsy we have to do it here instead of where all the other kill stats are incremented. Mainly because we turn cloak off at the start of execution so you can't do it where all the other kill stats are incremented
+}
+#endif
+
+void function TargetClearedExecuted( entity target )
+{
+ target.ClearParent()
+ target.Solid()
+ if ( target.ContextAction_IsMeleeExecution() )
+ target.PlayerMelee_ExecutionEndTarget()
+ if ( target.IsPlayer() )
+ ClearPlayerAnimViewEntity( target )
+}
+
+void function TargetSetExecutedBy( entity target, entity attacker )
+{
+ //Break out of context actions like hacking control panel etc
+ if ( target.ContextAction_IsActive() )
+ target.Anim_Stop()
+
+ target.PlayerMelee_ExecutionStartTarget( attacker )
+ target.e.syncedMeleeAttacker = attacker
+ target.NotSolid()
+}
+
+bool function MeleeTargetrequiresDataKnife( entity target )
+{
+ if ( IsProwler( target ) )
+ return true
+
+ if ( IsPilotElite( target ) )
+ return true
+
+ return false
+}
+
+string function GetTagForKnifeMeleeTarget( entity target )
+{
+ Assert( MeleeTargetrequiresDataKnife( target ) )
+
+ if ( IsProwler( target ) )
+ return "PROPGUN"
+
+ if ( IsPilotElite( target ) )
+ return "KNIFE"
+
+ unreachable
+}
+
+function AttachPlayerModelForDuration( var player, asset modelName, var tag, var time )
+{
+ expect entity( player )
+
+ if ( !IsValid( player ) )
+ return
+
+ Assert( IsValid( tag ), "No tag specified for player" )
+
+ entity viewModel = player.GetFirstPersonProxy() //JFS: Defensive fix for player not having view models sometimes
+ if ( !IsValid( viewModel ) )
+ return
+
+ if ( !EntHasModelSet( viewModel ) )
+ return
+
+ entity model = CreatePropDynamic( modelName )
+ model.SetParent( viewModel, tag, false, 0.0 )
+
+ OnThreadEnd(
+ function() : ( model )
+ {
+ if ( IsValid( model ) )
+ model.Destroy()
+ }
+ )
+
+ player.EndSignal( "OnDeath" )
+
+ wait time
+}
+
+void function MarkForDeath( entity target )
+{
+ if ( target.IsNPC() )
+ {
+ //printt("Killing marked for death npc " + target )
+ //Just kill off NPC now, otherwise it will play pain animations on death
+ CodeCallback_OnMeleeKilled( target )
+ return
+ }
+
+ //printt("marking player " + target + " for death")
+ target.e.markedForExecutionDeath = true //This will kill off the player even if the execution animation is interruped from this point forward
+
+ target.EndSignal( "OnDeath" )
+
+ OnThreadEnd(
+ function() : ( target )
+ {
+ target.e.markedForExecutionDeath = false
+ }
+ )
+
+ WaitForever()
+
+}
+
+void function PhaseGib( entity target )
+{
+ if ( !IsAlive( target ) )
+ return
+
+ target.ClearInvulnerable()
+
+ entity attacker
+ if ( IsValid( target.e.syncedMeleeAttacker ) )
+ {
+ attacker = target.e.syncedMeleeAttacker
+ }
+ else if ( IsValid( target.e.lastSyncedMeleeAttacker ) )
+ {
+ attacker = target.e.lastSyncedMeleeAttacker
+ }
+ else
+ {
+ attacker = null
+ }
+
+ int damageAmount = target.GetMaxHealth() + 1
+ target.TakeDamage( damageAmount , attacker, attacker, { forceKill = true, damageType = DMG_MELEE_EXECUTION, damageSourceId = eDamageSourceId.human_execution, scriptType = DF_NO_INDICATOR | DF_GIB } )
+}
+
+
+entity function CreateSyncedMeleeRef( entity attacker, entity target, SyncedMelee action )
+{
+ entity ref = CreateMeleeScriptMoverBetweenEnts( attacker, target )
+
+ vector angles = target.GetAngles()
+ angles.x = ref.GetAngles().x
+
+ ref.SetAngles( angles )
+ if ( action.animRefPos == "attacker" )
+ ref.SetOrigin( attacker.GetOrigin() )
+ else
+ ref.SetOrigin( target.GetOrigin() )
+ return ref
+}
+
+void function ApplyGruntExecutionDamage( entity ref, entity attacker, entity target, float damageDealt )
+{
+ ref.EndSignal( "OnDestroy" )
+ attacker.EndSignal( "OnDeath" )
+ target.EndSignal( "OnDeath" )
+
+ for ( ;; )
+ {
+ table results = attacker.WaitSignal( "NpcDealsExecutionDamage" )
+ float damage
+ switch ( results.parm )
+ {
+ case "lethal":
+ damage = float( target.GetMaxHealth() )
+ break
+
+ case "nonlethal":
+ damage = min( target.GetHealth() - 10, target.GetMaxHealth() * damageDealt )
+ break
+ }
+
+ target.TakeDamage( damage, attacker, attacker, { damageSourceId=eDamageSourceId.human_execution, scriptType = DF_RAGDOLL } )
+ }
+}
|