From 63493d2b9d27b52eae88a8969144efc997f7d0b7 Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Tue, 5 Dec 2017 18:03:00 +0200 Subject: [PATCH] Added a Character.AddDamage override method that does damage to a specific limb instead of the limb closest to the source of the damage. Projectiles and melee weapons now always do damage to the limb they hit, even if the center point of some other limb happens to be closer. Also refactored the IDamageable interface to make more sense; now the attacker has to be a character instead of an IDamageable and damageable classes don't need to have an AiTarget. Closes #69 --- .../Source/Characters/AI/AIController.cs | 2 +- .../Source/Characters/AI/EnemyAIController.cs | 2 +- .../Source/Characters/AI/HumanAIController.cs | 2 +- .../Source/Characters/AICharacter.cs | 6 +-- .../Source/Characters/Attack.cs | 44 ++++++++++++---- .../Source/Characters/Character.cs | 51 +++++++++++++------ .../Items/Components/Holdable/MeleeWeapon.cs | 39 +++++++++----- .../Source/Items/Components/Projectile.cs | 3 +- .../BarotraumaShared/Source/Items/Item.cs | 2 +- .../Source/Map/IDamageable.cs | 9 +--- .../BarotraumaShared/Source/Map/Structure.cs | 2 +- 11 files changed, 107 insertions(+), 55 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/AIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/AIController.cs index bcff43a03..b395f97ee 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/AIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/AIController.cs @@ -53,7 +53,7 @@ namespace Barotrauma Enabled = true; } - public virtual void OnAttacked(IDamageable attacker, float amount) { } + public virtual void OnAttacked(Character attacker, float amount) { } public virtual void SelectTarget(AITarget target) { } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index 0d5ceab33..0cbe6be2b 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -445,7 +445,7 @@ namespace Barotrauma targetEntity = closestBody.UserData as IDamageable; } - public override void OnAttacked(IDamageable attacker, float amount) + public override void OnAttacked(Character attacker, float amount) { updateTargetsTimer = Math.Min(updateTargetsTimer, 0.1f); coolDownTimer *= 0.1f; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs index 172f969a3..33e67d60d 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs @@ -137,7 +137,7 @@ namespace Barotrauma } } - public override void OnAttacked(IDamageable attacker, float amount) + public override void OnAttacked(Character attacker, float amount) { if (amount <= 0.0f) return; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AICharacter.cs b/Barotrauma/BarotraumaShared/Source/Characters/AICharacter.cs index 4ccc10788..dddc72d93 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AICharacter.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AICharacter.cs @@ -79,16 +79,16 @@ namespace Barotrauma } partial void SoundUpdate(float deltaTime); - public override void AddDamage(CauseOfDeath causeOfDeath, float amount, IDamageable attacker) + public override void AddDamage(CauseOfDeath causeOfDeath, float amount, Character attacker) { base.AddDamage(causeOfDeath, amount, attacker); if (attacker != null) aiController.OnAttacked(attacker, amount); } - public override AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false) + public override AttackResult ApplyAttack(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false, Limb limb = null) { - AttackResult result = base.AddDamage(attacker, worldPosition, attack, deltaTime, playSound); + AttackResult result = base.ApplyAttack(attacker, worldPosition, attack, deltaTime, playSound, limb); aiController.OnAttacked(attacker, result.Damage + result.Bleeding); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs index 3ea9aa97e..0f836eae8 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Attack.cs @@ -156,7 +156,7 @@ namespace Barotrauma } partial void InitProjSpecific(XElement element); - public AttackResult DoDamage(IDamageable attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true) + public AttackResult DoDamage(Character attacker, IDamageable target, Vector2 worldPosition, float deltaTime, bool playSound = true) { if (onlyHumans) { @@ -167,19 +167,14 @@ namespace Barotrauma DamageParticles(deltaTime, worldPosition); var attackResult = target.AddDamage(attacker, worldPosition, this, deltaTime, playSound); - var effectType = attackResult.Damage > 0.0f ? ActionType.OnUse : ActionType.OnFailure; - - if (statusEffects == null) - { - return attackResult; - } + if (statusEffects == null) return attackResult; foreach (StatusEffect effect in statusEffects) { - if (effect.Targets.HasFlag(StatusEffect.TargetType.This) && attacker is Character) + if (effect.Targets.HasFlag(StatusEffect.TargetType.This)) { - effect.Apply(effectType, deltaTime, (Character)attacker, (Character)attacker); + effect.Apply(effectType, deltaTime, attacker, attacker); } if (effect.Targets.HasFlag(StatusEffect.TargetType.Character) && target is Character) { @@ -189,6 +184,37 @@ namespace Barotrauma return attackResult; } + + public AttackResult DoDamageToLimb(Character attacker, Limb targetLimb, Vector2 worldPosition, float deltaTime, bool playSound = true) + { + if (targetLimb == null) return new AttackResult(); + + if (onlyHumans) + { + if (targetLimb.character != null && targetLimb.character.ConfigPath != Character.HumanConfigFile) return new AttackResult(); + } + + DamageParticles(deltaTime, worldPosition); + + var attackResult = targetLimb.character.ApplyAttack(attacker, worldPosition, this, deltaTime, playSound, targetLimb); + var effectType = attackResult.Damage > 0.0f ? ActionType.OnUse : ActionType.OnFailure; + if (statusEffects == null) return attackResult; + + foreach (StatusEffect effect in statusEffects) + { + if (effect.Targets.HasFlag(StatusEffect.TargetType.This)) + { + effect.Apply(effectType, deltaTime, attacker, attacker); + } + if (effect.Targets.HasFlag(StatusEffect.TargetType.Character)) + { + effect.Apply(effectType, deltaTime, targetLimb.character, targetLimb.character); + } + } + + return attackResult; + } + partial void DamageParticles(float deltaTime, Vector2 worldPosition); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index eced2d45c..40ba7328f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -1523,19 +1523,19 @@ namespace Barotrauma speechBubbleTimer = Math.Max(speechBubbleTimer, duration); speechBubbleColor = color; } - - private void AdjustKarma(IDamageable attacker,float amount) + + private void AdjustKarma(Character attacker, float amount) { if (GameMain.Server != null) { if (attacker is Character) { Character attackerCharacter = attacker as Character; - Barotrauma.Networking.Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attackerCharacter); + Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attackerCharacter); if (attackerClient != null) { - Barotrauma.Networking.Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this); - if (targetClient != null || this == Character.Controlled) + Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this); + if (targetClient != null || this == Controlled) { if (attackerCharacter.TeamID == TeamID) { @@ -1548,9 +1548,12 @@ namespace Barotrauma } } - public virtual void AddDamage(CauseOfDeath causeOfDeath, float amount, IDamageable attacker) + /// + /// Directly reduce the health of the character without any additional effects (particles, sounds, status effects...) + /// + public virtual void AddDamage(CauseOfDeath causeOfDeath, float amount, Character attacker) { - Health = health-amount; + Health = health - amount; if (amount > 0.0f) { lastAttackCauseOfDeath = causeOfDeath; @@ -1562,10 +1565,21 @@ namespace Barotrauma } partial void DamageHUD(float amount); - public virtual AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false) + public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false) { - Limb limbHit = null; - var attackResult = AddDamage(worldPosition, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce, out limbHit, attacker); + return ApplyAttack(attacker, worldPosition, attack, deltaTime, playSound, null); + } + + /// + /// Apply the specified attack to this character. If the targetLimb is not specified, the limb closest to worldPosition will receive the damage. + /// + public virtual AttackResult ApplyAttack(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false, Limb targetLimb = null) + { + Limb limbHit = targetLimb; + var attackResult = targetLimb == null ? + AddDamage(worldPosition, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce, out limbHit, attacker) : + DamageLimb(worldPosition, targetLimb, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce, attacker); + if (limbHit == null) return new AttackResult(); var attackingCharacter = attacker as Character; @@ -1612,14 +1626,12 @@ namespace Barotrauma return AddDamage(worldPosition, damageType, amount, bleedingAmount, stun, playSound, attackForce, out temp); } - public AttackResult AddDamage(Vector2 worldPosition, DamageType damageType, float amount, float bleedingAmount, float stun, bool playSound, float attackForce, out Limb hitLimb,IDamageable attacker=null) + public AttackResult AddDamage(Vector2 worldPosition, DamageType damageType, float amount, float bleedingAmount, float stun, bool playSound, float attackForce, out Limb hitLimb, Character attacker = null) { hitLimb = null; if (Removed) return new AttackResult(); - SetStun(stun); - float closestDistance = 0.0f; foreach (Limb limb in AnimController.Limbs) { @@ -1630,13 +1642,22 @@ namespace Barotrauma closestDistance = distance; } } - + + return DamageLimb(worldPosition, hitLimb, damageType, amount, bleedingAmount, stun, playSound, attackForce, attacker); + } + + public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, DamageType damageType, float amount, float bleedingAmount, float stun, bool playSound, float attackForce, Character attacker = null) + { + if (Removed) return new AttackResult(); + + SetStun(stun); + if (Math.Abs(attackForce) > 0.0f) { Vector2 diff = hitLimb.WorldPosition - worldPosition; if (diff == Vector2.Zero) diff = Rand.Vector(1.0f); hitLimb.body.ApplyForce(Vector2.Normalize(diff) * attackForce, hitLimb.SimPosition + ConvertUnits.ToSimUnits(diff)); - } + } AttackResult attackResult = hitLimb.AddDamage(worldPosition, damageType, amount, bleedingAmount, playSound); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs index fa568aa59..b98cb3361 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/MeleeWeapon.cs @@ -213,28 +213,39 @@ namespace Barotrauma.Items.Components private bool OnCollision(Fixture f1, Fixture f2, Contact contact) { - Character target = null; + Character targetCharacter = null; + Limb targetLimb = null; - Limb limb = f2.Body.UserData as Limb; - if (limb != null) + if (f2.Body.UserData is Limb) { - if (limb.character == picker) return false; - target = limb.character; + targetLimb = (Limb)f2.Body.UserData; + if (targetLimb.IsSevered || targetLimb.character == null) return false; + targetCharacter = targetLimb.character; + } + else if (f2.Body.UserData is Character) + { + + targetCharacter = (Character)f2.Body.UserData; } else { return false; } - if (target == null) + if (targetCharacter == picker) return false; + + if (attack != null) { - target = f2.Body.UserData as Character; + if (targetLimb == null) + { + attack.DoDamageToLimb(user, targetLimb, item.WorldPosition, 1.0f); + } + else + { + attack.DoDamage(user, targetCharacter, item.WorldPosition, 1.0f); + } } - if (target == null) return false; - - if (attack != null) attack.DoDamage(user, target, item.WorldPosition, 1.0f); - RestoreCollision(); hitting = false; @@ -242,18 +253,18 @@ namespace Barotrauma.Items.Components if (GameMain.Server != null) { - GameMain.Server.CreateEntityEvent(item, new object[] { Networking.NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse, target.ID }); + GameMain.Server.CreateEntityEvent(item, new object[] { Networking.NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse, targetCharacter.ID }); string logStr = picker?.Name + " used " + item.Name; if (item.ContainedItems != null && item.ContainedItems.Length > 0) { logStr += "(" + string.Join(", ", item.ContainedItems.Select(i => i?.Name)) + ")"; } - logStr += " on " + target + "."; + logStr += " on " + targetCharacter + "."; Networking.GameServer.Log(logStr, Networking.ServerLog.MessageType.Attack); } - ApplyStatusEffects(ActionType.OnUse, 1.0f, limb.character); + ApplyStatusEffects(ActionType.OnUse, 1.0f, targetLimb.character); return true; } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs index 4f566c810..16e8b97a7 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Projectile.cs @@ -245,7 +245,6 @@ namespace Barotrauma.Items.Components item.Move(-submarine.Position); item.Submarine = submarine; item.body.Submarine = submarine; - //item.FindHull(); return true; } @@ -253,7 +252,7 @@ namespace Barotrauma.Items.Components Structure structure; if ((limb = (target.Body.UserData as Limb)) != null) { - attackResult = attack.DoDamage(User, limb.character, item.WorldPosition, 1.0f); + attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f); } else if ((structure = (target.Body.UserData as Structure)) != null) { diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 1b18d156f..5405a9816 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -762,7 +762,7 @@ namespace Barotrauma } - public AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = true) + public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = true) { if (prefab.Indestructible) return new AttackResult(); diff --git a/Barotrauma/BarotraumaShared/Source/Map/IDamageable.cs b/Barotrauma/BarotraumaShared/Source/Map/IDamageable.cs index 17c4e4061..0d653cd60 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/IDamageable.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/IDamageable.cs @@ -18,12 +18,7 @@ namespace Barotrauma { get; } - - AITarget AiTarget - { - get; - } - - AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound=true); + + AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound=true); } } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs index b07761e33..eef7eccd7 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -643,7 +643,7 @@ namespace Barotrauma } } - public AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false) + public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false) { if (Submarine != null && Submarine.GodMode) return new AttackResult(0.0f, 0.0f); if (!prefab.Body || prefab.Platform) return new AttackResult(0.0f, 0.0f);