diff --git a/Engines/FlatRedBallXNA/FlatRedBall/Entities/IDamageable.cs b/Engines/FlatRedBallXNA/FlatRedBall/Entities/IDamageable.cs index 827795cb7..be94da506 100644 --- a/Engines/FlatRedBallXNA/FlatRedBall/Entities/IDamageable.cs +++ b/Engines/FlatRedBallXNA/FlatRedBall/Entities/IDamageable.cs @@ -11,11 +11,14 @@ public interface IDamageable Dictionary DamageAreaLastDamage { get; } int TeamIndex { get; } bool IsDamageReceivingEnabled { get; } + double InvulnerabilityTimeAfterDamage { get; } + double LastDamageTime { get; set; } decimal CurrentHealth { get; set; } + bool IsInvulnerable { get; } decimal MaxHealth { get; set; } /// - /// Event raised before damave is dealt. This event can be used to modify the damage dealt. + /// Event raised before damage is dealt. This event can be used to modify the damage dealt. /// Func ModifyDamageReceived { get; set; } /// @@ -42,7 +45,8 @@ public static bool ShouldTakeDamage(this IDamageable damageable, IDamageArea dam if (damageable.TeamIndex == damageArea.TeamIndex || damageable.CurrentHealth <= 0 || !damageArea.IsDamageDealingEnabled - || !damageable.IsDamageReceivingEnabled) + || !damageable.IsDamageReceivingEnabled + || damageable.IsInvulnerable) { return false; } @@ -52,6 +56,7 @@ public static bool ShouldTakeDamage(this IDamageable damageable, IDamageArea dam // damage area, so deal damage and record the time in the damageAreaLastDamage // dictionary. damageable.DamageAreaLastDamage.Add(damageArea, TimeManager.CurrentScreenTime); + damageable.LastDamageTime = TimeManager.CurrentScreenTime; // Remove the damage area from the dictionary when it is destroyed or else // the Player may accumulate a large collection of damage areas, resulting in @@ -69,6 +74,7 @@ public static bool ShouldTakeDamage(this IDamageable damageable, IDamageArea dam { // If so, update the last damage time. damageable.DamageAreaLastDamage[damageArea] = TimeManager.CurrentScreenTime; + damageable.LastDamageTime = TimeManager.CurrentScreenTime; return true; } else @@ -100,14 +106,21 @@ public static decimal TakeDamage(this IDamageable damageable, IDamageArea damage var modifiedByBoth = damageArea.ModifyDamageDealt?.Invoke(modifiedByDamageable, damageable) ?? modifiedByDamageable; var healthBefore = damageable.CurrentHealth; - + if (modifiedByBoth != 0) { - damageable.CurrentHealth -= modifiedByBoth; + decimal newHealth = damageable.CurrentHealth - modifiedByBoth; + + if (newHealth > damageable.MaxHealth) + { + newHealth = damageable.MaxHealth; + } + + damageable.CurrentHealth = newHealth; } // We used to not raise events when taking 0 damage, but we may want - // to have some kind of logic play when taking 0 damage, likeplay a sound + // to have some kind of logic play when taking 0 damage, like play a sound // effect to indicate that this is not a spot that an enemy can get hit. damageable.ReactToDamageReceived?.Invoke(modifiedByBoth, damageArea); damageArea.ReactToDamageDealt?.Invoke(modifiedByBoth, damageable); diff --git a/FRBDK/Glue/OfficialPlugins/DamageDealingPlugin/CodeGenerators/DamageDealingCodeGenerator.cs b/FRBDK/Glue/OfficialPlugins/DamageDealingPlugin/CodeGenerators/DamageDealingCodeGenerator.cs index c2065ba77..38232cb95 100644 --- a/FRBDK/Glue/OfficialPlugins/DamageDealingPlugin/CodeGenerators/DamageDealingCodeGenerator.cs +++ b/FRBDK/Glue/OfficialPlugins/DamageDealingPlugin/CodeGenerators/DamageDealingCodeGenerator.cs @@ -102,6 +102,9 @@ public override ICodeBlock GenerateFields(ICodeBlock codeBlock, IElement element if (UsesDamageV3) { codeBlock.Line("public bool IsDamageReceivingEnabled { get; set; } = true;"); + codeBlock.Line("public double InvulnerabilityTimeAfterDamage { get; set; } = 0;"); + codeBlock.Line("public bool IsInvulnerable => TimeManager.CurrentScreenSecondsSince(LastDamageTime) < InvulnerabilityTimeAfterDamage;"); + codeBlock.Line("public double LastDamageTime { get; set; } = -999;"); } } }