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