From 9a96d0bff56f1969c68bb52a2f33296095bdc67d Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Tue, 31 Aug 2021 23:14:58 +0100 Subject: move to new mod format --- .../vscripts/titan/_titan_triple_health.gnut | 524 +++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_triple_health.gnut (limited to 'Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_triple_health.gnut') diff --git a/Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_triple_health.gnut b/Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_triple_health.gnut new file mode 100644 index 000000000..7515b868f --- /dev/null +++ b/Northstar.CustomServers/mod/scripts/vscripts/titan/_titan_triple_health.gnut @@ -0,0 +1,524 @@ +untyped + +global function HealthRegenInit + +global function TitanLoseSegementFX //JFS: Only being used for Rodeo now, rename later if needed +global function GibBodyPart + +const SEGMENT_DOWN_SOUNDS_3P = [ + "titan_healthbar_tier3_down_3P_vs_3P", // 0 left (doom) + "titan_healthbar_tier2_down_3P_vs_3P", // 1 left + "titan_healthbar_tier1_down_3P_vs_3P", // 2 left + "titan_healthbar_tier1_down_3P_vs_3P" // shield gone +] + +const SEGMENT_DOWN_SOUNDS_3P_ATTACKER = [ + "titan_healthbar_tier3_down_1P_vs_3P", // 0 left (doom) + "titan_healthbar_tier2_down_1P_vs_3P", // 1 left + "titan_healthbar_tier1_down_1P_vs_3P", // 2 left + "titan_healthbar_tier1_down_1P_vs_3P" // shield gone +] + +const SEGMENT_DOWN_SOUNDS_1P = [ + "titan_healthbar_tier3_down_1P", // 0 left (doom) + "titan_healthbar_tier2_down_1P", // 1 left + "titan_healthbar_tier1_down_1P", // 2 left + "titan_healthbar_tier1_down_1P" // shield gone +] + +const DAMAGE_FORGIVENESS_CEILING = 1200.0 +const DAMAGE_FORGIVENESS_FLOOR = 500.0 +const LOW_HEALTH_WARNING_SOUND = "Weapon_Vortex_Gun.ExplosiveWarningBeep" + +const TITAN_DAMAGE_MITIGATION_DAMAGESCALE = 0.5 + +struct { + int shieldDecayRate = 2 + + table< entity, table< entity, float > > soulToSoulDamageMemory +} file; + +function HealthRegenInit() +{ + if ( GetCurrentPlaylistVarInt( "titan_health_decay_amount", 0 ) > 0 ) + { + AddSoulInitFunc( TitanHealthDecayThink ) + } + + AddSoulInitFunc( TitanHealthRegenThink ) + + if ( TitanShieldDecayEnabled() ) + { + AddSoulInitFunc( TitanShieldDecayThink ) + } + + AddDamageCallback( "player", TitanSegmentedHealth_OnDamage ) + AddDamageCallback( "npc_titan", TitanSegmentedHealth_OnDamage ) + AddCallback_OnTitanDoomed( OnTitanDoomed ) + + RegisterSignal( "HealthSegmentLost" ) +} + + +void function TitanHealthDecayThink( entity soul ) +{ + thread TitanHealthDecayThinkInternal( soul ) +} + +void function TitanHealthDecayThinkInternal( entity soul ) +{ + soul.EndSignal( "OnDestroy" ) + soul.EndSignal( "OnTitanDeath" ) + + soul.SetShieldHealth( 0 ) + + while ( 1 ) + { + entity titan = soul.GetTitan() + int damageAmout = GetCurrentPlaylistVarInt( "titan_health_decay_amount", 0 ) + titan.TakeDamage( damageAmout, null, null, { scriptType = DF_DOOMED_HEALTH_LOSS, damageSourceId = damagedef_suicide } ) + WaitFrame() + } +} + +void function TitanHealthRegenThink( entity soul ) +{ + thread TitanHealthRegenThink_Internal( soul ) +} + +void function TitanHealthRegenThink_Internal( entity soul ) +{ + soul.EndSignal( SIGNAL_TITAN_HEALTH_REGEN ) + soul.EndSignal( "OnTitanDeath" ) + soul.EndSignal( "OnDestroy" ) + + if ( !soul.soul.regensHealth ) + return + + entity titan = soul.GetTitan() + + if ( !IsValid( titan ) ) + return + + int healthPerTab = GetSegmentHealthForTitan( titan ) + + // set this if AI titans need to be aware of segment health. Not used currently + //titan.SetHealthPerSegment( healthPerTab ) + + int lastTitanHealth = titan.GetHealth() + bool regenSound = false + int maxHealth = titan.GetMaxHealth() + float lastTime = Time() + + while ( 1 ) + { + titan = soul.GetTitan() + if ( !IsAlive( titan ) ) + return + int titanHealth = titan.GetHealth() + Assert( titan ) + + if ( !titan.IsTitan() ) + return + + if ( !soul.soul.regensHealth ) + return + + int currentRegenTab = GetTitanCurrentRegenTab( titan ) + + if ( currentRegenTab != GetSoulBatteryCount( soul ) ) + SetSoulBatteryCount( soul, GetTitanCurrentRegenTab( titan ) ) + + int maxHealthForCurrentTab = currentRegenTab * healthPerTab + + if ( titanHealth == maxHealthForCurrentTab ) + { + if ( regenSound ) + { + StopSoundOnEntity( titan, "titan_energyshield_up" ) + regenSound = false + } + } + + lastTitanHealth = titanHealth + lastTime = Time() + WaitFrame() + } +} + +void function TitanSegmentedHealth_OnDamage( entity titan, var damageInfo ) +{ + if ( !titan.IsTitan() ) + return + + entity soul = titan.GetTitanSoul() + + if ( !IsValid( soul ) ) + return + + if ( ShouldReduceDamageForSegmentedHealth( soul, damageInfo ) ) + DamageInfo_ScaleDamage( damageInfo, 0.3 ) + + thread TitanSegmentedHealth_OnDamage_Thread( soul, damageInfo ) +} + +bool function ShouldReduceDamageForSegmentedHealth( entity soul, damageInfo ) +{ + if ( !soul.soul.rebooting ) + return false + + if ( IsRodeoDamageFromBatteryPack( soul, damageInfo ) ) + return false + + if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOMED_HEALTH_LOSS ) + return false + + if ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_DOOM_FATALITY ) + return false + + return true +} + +function TitanSegmentedHealth_OnDamage_Thread( entity soul, damageInfo ) +{ + soul.EndSignal( "OnTitanDeath" ) + soul.EndSignal( "OnDestroy" ) + soul.EndSignal( "Doomed" ) + + entity titan = soul.GetTitan() + + vector damageOrigin = GetDamageOrigin( damageInfo, titan ) + float damageAmount = DamageInfo_GetDamage( damageInfo ) + entity attacker = DamageInfo_GetAttacker( damageInfo ) + int hitBox = DamageInfo_GetHitBox( damageInfo ) + + int healthFloor = CalculateHealthFloorForDamage( soul, titan, damageInfo ) + + bool skipDoom = ( DamageInfo_GetCustomDamageType( damageInfo ) & DF_SKIPS_DOOMED_STATE ) > 0 + + + WaitEndFrame() + + titan = soul.GetTitan() + Assert( IsValid( titan ) ) + + if ( soul.soul.lastSegmentLossTime >= Time() ) + return + + if ( GetDoomedState( titan ) ) + return + + if ( titan.GetHealth() > healthFloor ) + return + + if ( !IsAlive( titan ) ) + return + + string settings = GetSoulPlayerSettings( soul ) + if ( Dev_GetPlayerSettingByKeyField_Global( settings, "use_damage_states" ) == 1 ) + UpdateDamageStateForTab( titan, GetTitanCurrentRegenTab( titan ), hitBox ) + + thread TitanLoseSegement( soul, titan, damageOrigin, damageAmount, attacker ) +} + +int function CalculateHealthFloorForDamage( entity soul, entity titan, damageInfo ) +{ + //Lets you bypass the health segment limitation and remove an entire health segment. + /*if ( IsRodeoDamageFromBatteryPack( soul, damageInfo ) ) + return minint( 0, int ( titan.GetHealth() - DamageInfo_GetDamage( damageInfo ) ) ) + */ + int oldTab = GetTitanCurrentRegenTab( titan ) + return ( oldTab - 1 ) * GetSegmentHealthForTitan( titan ) +} + +void function TitanLoseSegement( entity soul, entity titan, vector damageOrigin, float damageAmount, entity attacker ) +{ + if ( !IsValid( soul ) ) + return + + if ( !IsValid( titan ) ) + return + + if ( soul.soul.lastSegmentLossTime >= Time() ) + return + + soul.soul.lastSegmentLossTime = Time() + + entity player + if ( titan.IsPlayer() ) + player = titan + + foreach ( callbackFunc in svGlobal.onTitanHealthSegmentLostCallbacks ) + { + callbackFunc( titan, attacker ) + } + + // Added via AddTitanCallback_OnHealthSegmentLost + foreach ( callbackFunc in titan.e.entSegmentLostCallbacks ) + { + callbackFunc( titan, attacker ) + } + + GiveDefenderAmmo( titan ) + + titan.Signal( "HealthSegmentLost" ) + + soul.EndSignal( "OnTitanDeath" ) + soul.EndSignal( "OnDestroy" ) + + if ( GetCurrentPlaylistVarInt( "titan_health_chicklet_fx", 0 ) == 1 ) + TitanLoseSegementFX( titan, attacker, damageOrigin ) + + SetSoulBatteryCount( soul, GetTitanCurrentRegenTab( titan ) ) +} + +void function TitanLoseSegementFX( entity titan, entity attacker, vector damageOrigin ) +{ + int handle = titan.GetEncodedEHandle() + int handleAttacker = -1 + + if ( IsValid( attacker ) ) + handleAttacker = attacker.GetEncodedEHandle() + + array players = GetPlayerArray() + foreach ( player in players ) + { + Remote_CallFunction_Replay( player, "ServerCallback_TitanLostHealthSegment", handle, handleAttacker, damageOrigin.x, damageOrigin.y, damageOrigin.z ) + } + + if ( !IsAlive( titan ) ) + return + + int currentRegenTab = minint( GetTitanCurrentRegenTab( titan ), SEGMENT_DOWN_SOUNDS_3P_ATTACKER.len()-1 ) + if ( currentRegenTab < SEGMENT_DOWN_SOUNDS_3P_ATTACKER.len() ) + { + if ( titan.IsPlayer() && IsAlive( attacker ) && attacker.IsPlayer() ) + { + EmitSoundOnEntityOnlyToPlayer( titan, attacker, SEGMENT_DOWN_SOUNDS_3P_ATTACKER[ currentRegenTab ] ) + EmitSoundOnEntityOnlyToPlayer( titan, titan, SEGMENT_DOWN_SOUNDS_1P[ currentRegenTab ] ) + + // need a command here to play for not victim and not attacker + EmitSoundOnEntityExceptToPlayer( titan, titan, SEGMENT_DOWN_SOUNDS_3P[ currentRegenTab ] ) + } + else if ( IsAlive( attacker ) && attacker.IsPlayer() ) + { + EmitSoundOnEntityOnlyToPlayer( titan, attacker, SEGMENT_DOWN_SOUNDS_3P_ATTACKER[ currentRegenTab ] ) + EmitSoundOnEntityExceptToPlayer( titan, attacker, SEGMENT_DOWN_SOUNDS_3P[ currentRegenTab ] ) + } + else if ( titan.IsPlayer() ) + { + EmitSoundOnEntityOnlyToPlayer( titan, titan, SEGMENT_DOWN_SOUNDS_1P[ currentRegenTab ] ) + EmitSoundOnEntityExceptToPlayer( titan, titan, SEGMENT_DOWN_SOUNDS_3P[ currentRegenTab ] ) + } + else + { + EmitSoundOnEntity( titan, SEGMENT_DOWN_SOUNDS_3P[ currentRegenTab ] ) + } + } +} + + +void function UpdateDamageStateForTab( entity titan, int tab, int hitBox ) +{ + if ( hitBox == -1 ) // not every hitbox has data defined + return + + var bodyGroup = titan.GetBodyGroupNameFromHitboxId( hitBox ) // can be null + + if ( bodyGroup == null ) + { + printt( "bodyGroup was null" ) + return + } + +#if MP + // these are flipped on purpose to prevent both legs or arms from being blown up + switch ( bodyGroup ) + { + case "left_leg": + if ( IsBroken( titan, "right_leg" ) ) + return + break + + case "right_leg": + if ( IsBroken( titan, "left_leg" ) ) + return + break + + case "left_arm": + if ( IsBroken( titan, "right_arm" ) ) + return + break + + case "right_arm": + if ( IsBroken( titan, "left_arm" ) ) + return + break + + default: + return + } + + GibBodyPart( titan, bodyGroup ) +#else + int maxTab = 3 + int count = maxTab - tab + + RecursiveGibBodyPart( titan, bodyGroup, count ) +#endif +} + +void function RecursiveGibBodyPart( entity titan, var bodyGroup, int count ) +{ + GibBodyPart( titan, bodyGroup ) + + count -= 1 + if ( count <= 0 ) + return + + foreach ( siblingName in titan.s.skeletonData[bodyGroup].siblings ) + { + // printt( count + " recurse: " + siblingName ) + RecursiveGibBodyPart( titan, siblingName, count ) + } +} + +bool function IsBroken( entity titan, var bodyGroup ) +{ + local bodyGroupIndex = titan.FindBodyGroup( bodyGroup ) + local stateCount = GetStateCountForBodyGroup( titan, bodyGroup ) + local bodyGroupState = titan.GetBodyGroupState( bodyGroupIndex ) + + //return ( bodyGroupState >= (stateCount - 1) ) + return ( bodyGroupState > 0 ) +} + +void function GibBodyPart( entity titan, var bodyGroup ) +{ + // if ( IsBodyGroupBroken( titan, bodyGroup ) ) + // return + + // titan.s.damageStateInfo[bodyGroup] = 1 + + local bodyGroupIndex = titan.FindBodyGroup( bodyGroup ) + local stateCount = GetStateCountForBodyGroup( titan, bodyGroup ) + local bodyGroupState = titan.GetBodyGroupState( bodyGroupIndex ) + + if ( bodyGroupState >= (stateCount - 1) ) + return + + titan.SetBodygroup( bodyGroupIndex, bodyGroupState + 1 ) + // printt( "break: " + bodyGroup ) +} + +function GiveAttackerAmmo( entity titan ) +{ +} + +void function TemporaryInvul( entity titan ) +{ + titan.EndSignal( "OnDestroy" ) + titan.EndSignal( "OnDeath" ) + if ( titan.IsPlayer() ) + { + titan.EndSignal( "DisembarkingTitan" ) + titan.EndSignal( "TitanEjectionStarted" ) + } + + OnThreadEnd( + function() : ( titan ) + { + if ( IsValid( titan ) ) + titan.ClearInvulnerable() + } + ) + + titan.SetInvulnerable() + wait 0.25 +} + +void function GiveDefenderAmmo( entity titan ) +{ + entity soul = titan.GetTitanSoul() + + if ( IsSingleplayer() ) + { + if ( titan.IsNPC() ) + { + soul.SetNextCoreChargeAvailable( soul.GetNextCoreChargeAvailable() + 0.5 ) // shave time off core timer + } + } +} + +void function OnTitanDoomed( entity titan, var damageInfo ) +{ + + if ( !IsAlive( titan ) ) + return + + entity soul = titan.GetTitanSoul() + + if ( titan.IsPlayer() ) + { + if ( SoulHasPassive( soul, ePassives.PAS_RONIN_AUTOSHIFT ) ) + PhaseShift( titan, 0, 3.0 ) + + if ( SoulHasPassive( soul, ePassives.PAS_AUTO_EJECT ) ) + return + } + + soul.nextHealthRegenTime = Time() + + vector damageOrigin = GetDamageOrigin( damageInfo, titan ) + float damageAmount = DamageInfo_GetDamage( damageInfo ) + entity attacker = DamageInfo_GetAttacker( damageInfo ) + + if ( TitanDamageRewardsTitanCoreTime() && (titan != attacker) ) + { + AddCreditToTitanCoreBuilderForDoomEntered( titan ) + if ( attacker.IsTitan() ) + AddCreditToTitanCoreBuilderForDoomInflicted( attacker ) + } + + thread TitanLoseSegement( soul, titan, damageOrigin, damageAmount, attacker ) + + if ( SoulHasPassive( soul, ePassives.PAS_DOOMED_TIME ) ) + return + + if ( NoWeaponDoomState() ) + TakeAllWeapons( titan ) +} + +void function OnTitanDeath( entity titan, var damageInfo ) +{ + if ( !titan.IsTitan() ) + return + + if ( !PROTO_AlternateDoomedState() ) + return + + entity soul = titan.GetTitanSoul() + vector damageOrigin = GetDamageOrigin( damageInfo, titan ) + float damageAmount = DamageInfo_GetDamage( damageInfo ) + entity attacker = DamageInfo_GetAttacker( damageInfo ) + + thread TitanLoseSegement( soul, titan, damageOrigin, damageAmount, attacker ) +} + +void function TitanShieldDecayThink( entity soul ) +{ + thread TitanShieldDecayThinkInternal( soul ) +} + +void function TitanShieldDecayThinkInternal( entity soul ) +{ + soul.EndSignal( "OnDestroy" ) //This needs to be OnDestroy instead of OnDeath because souls don't have a death animation + soul.EndSignal( "OnTitanDeath" ) + + while ( 1 ) + { + if ( Time() >= soul.e.nextShieldDecayTime && !TitanHasRegenningShield( soul ) ) + soul.SetShieldHealth( maxint( soul.GetShieldHealth() - file.shieldDecayRate, 0 ) ) + WaitFrame() + } +} \ No newline at end of file -- cgit v1.2.3