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);