global function HealthRegen_Init global function PilotHealthRegenThinkSP global function PilotShieldHealthUpdate struct { float healthRegenRate } file void function HealthRegen_Init() { if ( IsSingleplayer() ) { file.healthRegenRate = 1.0 } else { file.healthRegenRate = 6.0 AddCallback_PlayerClassChanged( HealthRegen_OnPlayerClassChangedMP ) RegisterSignal( "PilotHealthRegenThink" ) } } void function PilotHealthRegenThinkSP( entity player ) { player.EndSignal( "OnDestroy" ) while ( IsValid( player ) ) { wait( HEALTH_REGEN_TICK_TIME ) if ( !IsAlive( player ) ) continue if ( !IsPilot( player ) ) continue if ( shGlobal.proto_pilotHealthRegenDisabled ) continue //Assert( IsTestMap() || player.GetPlayerSettings() == DEFAULT_PILOT_SETTINGS, "for now, we should all be pilot_solo at all times, or in a test map." ) if ( player.GetHealth() == player.GetMaxHealth() ) continue float healthRegenRate = 4.0 float healthRegenStartDelay = GraphCapped( player.GetHealth(), 0, player.GetMaxHealth(), 3.0, 0.8 ) //printt( "recentDamage " + recentDamage + " delay " + healthRegenStartDelay + " rate " + healthRegenRate ) if ( Time() - player.p.lastDamageTime < healthRegenStartDelay ) { continue } player.SetHealth( min( player.GetMaxHealth(), player.GetHealth() + healthRegenRate ) ) } } bool function IsHealActive( entity player ) { return StatusEffect_Get( player, eStatusEffect.stim_visual_effect ) > 0.0 } void function PilotHealthRegenThinkMP( entity player ) { player.EndSignal( "OnDestroy" ) player.Signal( "PilotHealthRegenThink" ) player.EndSignal( "PilotHealthRegenThink" ) //float healthRegenStartDelay = player.GetPlayerSettingsField( "powerRegenRateOp" ) // seconds after we take damager to start regen float healthRegenStartDelay = 5.0 //Needs to use GetPlayerSettingsField() instead of hard coding, waiting on Bug 129567 if ( PlayerHasPassive( player, ePassives.PAS_FAST_HEALTH_REGEN ) ) healthRegenStartDelay = 2.5 while ( IsValid( player ) ) { wait( HEALTH_REGEN_TICK_TIME ) if ( !IsAlive( player ) ) continue if ( !IsPilot( player ) ) continue if ( shGlobal.proto_pilotHealthRegenDisabled ) continue float healthRegenRate = file.healthRegenRate // health regen per tick if ( player.GetHealth() == player.GetMaxHealth() ) continue // No regen during phase shift if ( player.IsPhaseShifted() ) continue if ( IsHealActive( player ) ) { if ( Time() - player.p.lastDamageTime < min( ABILITY_STIM_REGEN_DELAY, healthRegenStartDelay ) ) continue else healthRegenRate = healthRegenRate * ABILITY_STIM_REGEN_MOD } else if ( Time() - player.p.lastDamageTime < healthRegenStartDelay ) { continue } player.SetHealth( min( player.GetMaxHealth(), player.GetHealth() + healthRegenRate ) ) if ( player.GetHealth() == player.GetMaxHealth() ) { ClearRecentDamageHistory( player ) ClearLastAttacker( player ) } } } void function HealthRegen_OnPlayerClassChangedMP( entity player ) { thread PilotHealthRegenThinkMP( player ) } float function PilotShieldHealthUpdate( entity player, var damageInfo ) { if ( DamageInfo_GetForceKill( damageInfo ) ) { player.SetShieldHealth( 0 ) return 0.0 } int shieldHealth = player.GetShieldHealth() float shieldDamage = 0 if ( shieldHealth ) { DamageInfo_AddCustomDamageType( damageInfo, DF_SHIELD_DAMAGE ) shieldDamage = PilotShieldModifyDamage( player, damageInfo ) if ( shieldDamage ) DamageInfo_SetDamage( damageInfo, 0 ) } return shieldDamage } float function PilotShieldModifyDamage( entity player, var damageInfo ) { float shieldHealth = float( player.GetShieldHealth() ) float damage = DamageInfo_GetDamage( damageInfo ) float newShieldHealth = shieldHealth - damage float permanentDamage = 0.0 if ( newShieldHealth < 0 ) permanentDamage = fabs( newShieldHealth ) player.SetShieldHealth( maxint( 0, int( newShieldHealth ) ) ) if ( shieldHealth && newShieldHealth <= 0 ) { EmitSoundOnEntity( player, "titan_energyshield_down" ) } DamageInfo_SetDamage( damageInfo, permanentDamage ) return min( shieldHealth, damage ) }