diff --git a/Barotrauma/Barotrauma.csproj b/Barotrauma/Barotrauma.csproj
index 0c0d2c2fe..9eb20d525 100644
--- a/Barotrauma/Barotrauma.csproj
+++ b/Barotrauma/Barotrauma.csproj
@@ -387,6 +387,7 @@
PreserveNewest
+ Designer
PreserveNewest
@@ -868,9 +869,21 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
diff --git a/Barotrauma/Barotrauma.csproj.user b/Barotrauma/Barotrauma.csproj.user
index df35fcf91..1782abafa 100644
--- a/Barotrauma/Barotrauma.csproj.user
+++ b/Barotrauma/Barotrauma.csproj.user
@@ -9,7 +9,7 @@
en-US
false
- ProjectFiles
+ ShowAllFiles
diff --git a/Barotrauma/Content/Characters/Carrier/carrier.xml b/Barotrauma/Content/Characters/Carrier/carrier.xml
index 4ff3312d2..268b41d76 100644
--- a/Barotrauma/Content/Characters/Carrier/carrier.xml
+++ b/Barotrauma/Content/Characters/Carrier/carrier.xml
@@ -8,6 +8,16 @@
+
+
@@ -44,16 +54,12 @@
-
-
+
+
-
+
-
\ No newline at end of file
diff --git a/Barotrauma/Content/Characters/Charybdis/charybdis.png b/Barotrauma/Content/Characters/Charybdis/charybdis.png
index ef2796ae1..6d1ba2da8 100644
Binary files a/Barotrauma/Content/Characters/Charybdis/charybdis.png and b/Barotrauma/Content/Characters/Charybdis/charybdis.png differ
diff --git a/Barotrauma/Content/Characters/Charybdis/charybdis.xml b/Barotrauma/Content/Characters/Charybdis/charybdis.xml
index e027dce94..44eeab013 100644
--- a/Barotrauma/Content/Characters/Charybdis/charybdis.xml
+++ b/Barotrauma/Content/Characters/Charybdis/charybdis.xml
@@ -2,20 +2,36 @@
+
+
-
+
-
+
-
+
@@ -24,18 +40,25 @@
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
-
\ No newline at end of file
diff --git a/Barotrauma/Content/Characters/Coelanth/coelanth.xml b/Barotrauma/Content/Characters/Coelanth/coelanth.xml
index 4e820b397..194c95cd9 100644
--- a/Barotrauma/Content/Characters/Coelanth/coelanth.xml
+++ b/Barotrauma/Content/Characters/Coelanth/coelanth.xml
@@ -5,19 +5,35 @@
-
+
+
+
-
+
-
+
@@ -51,10 +67,7 @@
-
-
-
+
-
-
+
\ No newline at end of file
diff --git a/Barotrauma/Content/Characters/Crawler/crawler.xml b/Barotrauma/Content/Characters/Crawler/crawler.xml
index de3270ce2..c429a3e9b 100644
--- a/Barotrauma/Content/Characters/Crawler/crawler.xml
+++ b/Barotrauma/Content/Characters/Crawler/crawler.xml
@@ -1,14 +1,28 @@
-
+
+
+
@@ -16,7 +30,7 @@
-
+
@@ -32,7 +46,9 @@
-
+
@@ -59,25 +75,21 @@
-
+
-
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
diff --git a/Barotrauma/Content/Characters/Endworm/endworm.xml b/Barotrauma/Content/Characters/Endworm/endworm.xml
index 1ed66da24..23f5b98d6 100644
--- a/Barotrauma/Content/Characters/Endworm/endworm.xml
+++ b/Barotrauma/Content/Characters/Endworm/endworm.xml
@@ -1,10 +1,18 @@
-
+
+
+
@@ -75,6 +83,5 @@
-
diff --git a/Barotrauma/Content/Characters/Fractalguardian/fractalguardian.xml b/Barotrauma/Content/Characters/Fractalguardian/fractalguardian.xml
index 69ad89036..abb438036 100644
--- a/Barotrauma/Content/Characters/Fractalguardian/fractalguardian.xml
+++ b/Barotrauma/Content/Characters/Fractalguardian/fractalguardian.xml
@@ -5,6 +5,14 @@
+
+
@@ -41,16 +49,15 @@
-
+
-
+
-
+
-
-
+
+
-
diff --git a/Barotrauma/Content/Characters/Fractalguardian2/fractalguardian2.xml b/Barotrauma/Content/Characters/Fractalguardian2/fractalguardian2.xml
index 9f45de877..c3841f2c4 100644
--- a/Barotrauma/Content/Characters/Fractalguardian2/fractalguardian2.xml
+++ b/Barotrauma/Content/Characters/Fractalguardian2/fractalguardian2.xml
@@ -5,6 +5,14 @@
+
+
@@ -32,19 +40,16 @@
-
-
+
-
-
-
-
-
+
+
+
+
-
diff --git a/Barotrauma/Content/Characters/Human/human.xml b/Barotrauma/Content/Characters/Human/human.xml
index b0f000efd..1d45173db 100644
--- a/Barotrauma/Content/Characters/Human/human.xml
+++ b/Barotrauma/Content/Characters/Human/human.xml
@@ -9,8 +9,8 @@
movementlerp="0.4"
legtorque="15.0"
thightorque="-5.0"
- walkspeed="1.5"
- swimspeed="2.0"
+ walkspeed="1.5" swimspeed="2.0"
+ runspeedmultiplier="3.0" swimspeedmultiplier="1.5"
colliderheightfromfloor="55"
impacttolerance="7.5">
@@ -84,25 +84,25 @@
-
+
-
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
diff --git a/Barotrauma/Content/Characters/Human/humanhusk.xml b/Barotrauma/Content/Characters/Human/humanhusk.xml
index 52f0e389e..29b52fdce 100644
--- a/Barotrauma/Content/Characters/Human/humanhusk.xml
+++ b/Barotrauma/Content/Characters/Human/humanhusk.xml
@@ -9,12 +9,12 @@
movementlerp="0.4"
legtorque="15.0"
thightorque="-5.0"
- walkspeed="1.5"
- swimspeed="2.5"
+ walkspeed="1.5" swimspeed="2.5"
+ runspeedmultiplier="2.0" swimspeedmultiplier="1.5"
impacttolerance="7.5">
-
+
@@ -84,34 +84,35 @@
-
+
-
+
-
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
diff --git a/Barotrauma/Content/Characters/Husk/husk.xml b/Barotrauma/Content/Characters/Husk/husk.xml
index 9ea4a67d2..179d8dbbe 100644
--- a/Barotrauma/Content/Characters/Husk/husk.xml
+++ b/Barotrauma/Content/Characters/Husk/husk.xml
@@ -5,28 +5,38 @@
+
+
+ walkspeed="1.2" swimspeed="2.5"
+ runspeedmultiplier="2.0" swimspeedmultiplier="1.5">
-
+
-
-
+
@@ -82,7 +92,7 @@
-
+
@@ -90,26 +100,21 @@
-
+
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
-
-
diff --git a/Barotrauma/Content/Characters/Mantis/mantis.png b/Barotrauma/Content/Characters/Mantis/mantis.png
index b0b287343..a8a6618e6 100644
Binary files a/Barotrauma/Content/Characters/Mantis/mantis.png and b/Barotrauma/Content/Characters/Mantis/mantis.png differ
diff --git a/Barotrauma/Content/Characters/Mantis/mantis.xml b/Barotrauma/Content/Characters/Mantis/mantis.xml
index 298d4c9e6..36b2b9750 100644
--- a/Barotrauma/Content/Characters/Mantis/mantis.xml
+++ b/Barotrauma/Content/Characters/Mantis/mantis.xml
@@ -7,10 +7,24 @@
+
+
-
+
@@ -35,7 +49,7 @@
-
+
@@ -49,7 +63,8 @@
-
+
@@ -69,27 +84,23 @@
-
+
-
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
-
+
+
-
diff --git a/Barotrauma/Content/Characters/Moloch/moloch.xml b/Barotrauma/Content/Characters/Moloch/moloch.xml
index 12c85dc13..30ab3d997 100644
--- a/Barotrauma/Content/Characters/Moloch/moloch.xml
+++ b/Barotrauma/Content/Characters/Moloch/moloch.xml
@@ -6,6 +6,14 @@
+
+
@@ -46,22 +54,18 @@
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
\ No newline at end of file
diff --git a/Barotrauma/Content/Characters/Tigerthresher/tigerthresher.xml b/Barotrauma/Content/Characters/Tigerthresher/tigerthresher.xml
index aa3d7d237..6c3d35f6e 100644
--- a/Barotrauma/Content/Characters/Tigerthresher/tigerthresher.xml
+++ b/Barotrauma/Content/Characters/Tigerthresher/tigerthresher.xml
@@ -5,12 +5,27 @@
-
+
+
+
-
+
@@ -19,7 +34,7 @@
-
+
@@ -67,10 +82,8 @@
-
-
+
-
\ No newline at end of file
diff --git a/Barotrauma/Content/Characters/Watcher/watcher.xml b/Barotrauma/Content/Characters/Watcher/watcher.xml
index 0baeb14b1..24033c190 100644
--- a/Barotrauma/Content/Characters/Watcher/watcher.xml
+++ b/Barotrauma/Content/Characters/Watcher/watcher.xml
@@ -12,6 +12,14 @@
+
+
@@ -19,7 +27,7 @@
-
+
@@ -36,6 +44,4 @@
-
-
\ No newline at end of file
diff --git a/Barotrauma/Content/Items/Weapons/explosives.xml b/Barotrauma/Content/Items/Weapons/explosives.xml
index 57c683c19..e6a87f07f 100644
--- a/Barotrauma/Content/Items/Weapons/explosives.xml
+++ b/Barotrauma/Content/Items/Weapons/explosives.xml
@@ -13,7 +13,7 @@
-
+
@@ -31,7 +31,7 @@
-
+
@@ -50,7 +50,7 @@
-
+
@@ -69,7 +69,7 @@
-
+
@@ -121,7 +121,7 @@
-
+
diff --git a/Barotrauma/Content/Items/Weapons/railgun.xml b/Barotrauma/Content/Items/Weapons/railgun.xml
index ec3636731..3f73ec4ee 100644
--- a/Barotrauma/Content/Items/Weapons/railgun.xml
+++ b/Barotrauma/Content/Items/Weapons/railgun.xml
@@ -83,7 +83,7 @@
-
+
@@ -115,10 +115,10 @@
-
+
-
+
diff --git a/Barotrauma/Content/Particles/ParticlePrefabs.xml b/Barotrauma/Content/Particles/ParticlePrefabs.xml
index f258b789b..ca9c81561 100644
--- a/Barotrauma/Content/Particles/ParticlePrefabs.xml
+++ b/Barotrauma/Content/Particles/ParticlePrefabs.xml
@@ -57,31 +57,29 @@
-
-
-
+ velocitychange="0.0, -0.1">
+
+
+
-
+
@@ -20,13 +21,14 @@
characterfile="Content/Characters/Moloch/moloch.xml"
commonness="10"
difficulty="30"
- spawntype="mainpath"
+ spawntype="mainpath"
musictype="monster"/>
@@ -70,7 +72,7 @@
commonness="5"
difficulty="10"
minamount="2" maxamount="3"
-
+ repeat="true"
spawntype="mainpath,cave,ruin"
musictype="monster"/>
diff --git a/Barotrauma/Source/Characters/AI/AIController.cs b/Barotrauma/Source/Characters/AI/AIController.cs
index a909e4e31..dee7aab5d 100644
--- a/Barotrauma/Source/Characters/AI/AIController.cs
+++ b/Barotrauma/Source/Characters/AI/AIController.cs
@@ -6,15 +6,13 @@ namespace Barotrauma
{
class AIController : ISteerable
{
-
- public enum AiState { None, Attack, GoTo, Escape }
- public enum SteeringState { Wander, Seek, Escape }
+ public enum AIState { None, Attack, GoTo, Escape, Eat }
public bool Enabled;
public readonly Character Character;
- protected AiState state;
+ protected AIState state;
protected SteeringManager steeringManager;
@@ -44,7 +42,7 @@ namespace Barotrauma
get { return Character.AnimController.Collider.LinearVelocity; }
}
- public AiState State
+ public AIState State
{
get { return state; }
set { state = value; }
diff --git a/Barotrauma/Source/Characters/AI/AITarget.cs b/Barotrauma/Source/Characters/AI/AITarget.cs
index 1f5f47f69..f3038082f 100644
--- a/Barotrauma/Source/Characters/AI/AITarget.cs
+++ b/Barotrauma/Source/Characters/AI/AITarget.cs
@@ -11,7 +11,11 @@ namespace Barotrauma
public static List List = new List();
- public readonly Entity Entity;
+ public Entity Entity
+ {
+ get;
+ private set;
+ }
private float soundRange;
private float sightRange;
@@ -47,6 +51,7 @@ namespace Barotrauma
public void Remove()
{
List.Remove(this);
+ Entity = null;
}
public void Draw(SpriteBatch spriteBatch)
diff --git a/Barotrauma/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/Source/Characters/AI/EnemyAIController.cs
index a18c4e22e..e546032f7 100644
--- a/Barotrauma/Source/Characters/AI/EnemyAIController.cs
+++ b/Barotrauma/Source/Characters/AI/EnemyAIController.cs
@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Xml.Linq;
using FarseerPhysics;
-using Lidgren.Network;
using Microsoft.Xna.Framework;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework.Graphics;
@@ -23,21 +21,22 @@ namespace Barotrauma
//0.0 = doesn't attack targets of the type
//positive values = attacks targets of this type
//negative values = escapes targets of this type
- private float attackRooms, attackHumans, attackWeaker, attackStronger;
+ private float attackRooms, attackHumans, attackWeaker, attackStronger, eatDeadPriority;
+
+ //determines which characters are considered weaker/stronger
+ private float combatStrength;
private SteeringManager outsideSteering, insideSteering;
private float updateTargetsTimer;
private float raycastTimer;
-
- //a timer for attacks such as biting that last for a specific amount of time
- //the duration is determined by the attackDuration of the attacking limb
- private float attackTimer;
-
+
//a "cooldown time" after an attack during which the Character doesn't try to attack again
private float attackCoolDown;
private float coolDownTimer;
+
+ private bool attachToWalls;
//a point in a wall which the Character is currently targeting
private Vector2 wallAttackPos;
@@ -46,10 +45,15 @@ namespace Barotrauma
//the limb selected for the current attack
private Limb attackingLimb;
+
+ //flee when the health is below this value
+ private float fleeHealthThreshold;
private AITarget selectedAiTarget;
private AITargetMemory selectedTargetMemory;
private float targetValue;
+
+ private float eatTimer;
private Dictionary targetMemories;
@@ -73,10 +77,13 @@ namespace Barotrauma
XElement aiElement = doc.Root.Element("ai");
if (aiElement == null) return;
- attackRooms = ToolBox.GetAttributeFloat(aiElement, "attackrooms", 0.0f) / 100.0f;
- attackHumans = ToolBox.GetAttributeFloat(aiElement, "attackhumans", 0.0f) / 100.0f;
- attackWeaker = ToolBox.GetAttributeFloat(aiElement, "attackweaker", 0.0f) / 100.0f;
- attackStronger = ToolBox.GetAttributeFloat(aiElement, "attackstronger", 0.0f) / 100.0f;
+ attackRooms = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackrooms", "attackpriorityrooms") / 100.0f;
+ attackHumans = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackhumans", "attackpriorityhumans") / 100.0f;
+ attackWeaker = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackweaker", "attackpriorityweaker") / 100.0f;
+ attackStronger = ToolBox.GetAttributeFloat(aiElement, 0.0f, "attackstronger", "attackprioritystronger") / 100.0f;
+ eatDeadPriority = ToolBox.GetAttributeFloat(aiElement, "eatpriority", 0.0f) / 100.0f;
+
+ combatStrength = ToolBox.GetAttributeFloat(aiElement, "combatstrength", 1.0f);
attackCoolDown = ToolBox.GetAttributeFloat(aiElement, "attackcooldown", 5.0f);
@@ -85,12 +92,16 @@ namespace Barotrauma
attackWhenProvoked = ToolBox.GetAttributeBool(aiElement, "attackwhenprovoked", false);
+ fleeHealthThreshold = ToolBox.GetAttributeFloat(aiElement, "fleehealththreshold", 0.0f);
+
+ attachToWalls = ToolBox.GetAttributeBool(aiElement, "attachtowalls", false);
+
outsideSteering = new SteeringManager(this);
insideSteering = new IndoorsSteeringManager(this, false);
steeringManager = outsideSteering;
- state = AiState.None;
+ state = AIState.None;
}
public override void SelectTarget(AITarget target)
@@ -138,54 +149,140 @@ namespace Barotrauma
if (selectedAiTarget == null)
{
- state = AiState.None;
+ state = AIState.None;
+ }
+ else if ((selectedAiTarget.Entity is Character) && ((Character)selectedAiTarget.Entity).IsDead)
+ {
+ if (state != AIState.Eat)
+ {
+ eatTimer = 0.0f;
+ state = AIState.Eat;
+ }
}
else
{
- state = (targetValue > 0.0f) ? AiState.Attack : AiState.Escape;
+ state = (targetValue < 0.0f || Character.Health < fleeHealthThreshold) ? AIState.Escape : AIState.Attack;
}
//if (coolDownTimer >= 0.0f) return;
}
steeringManager = Character.Submarine == null ? outsideSteering : insideSteering;
+ bool run = false;
switch (state)
{
- case AiState.None:
+ case AIState.None:
UpdateNone(deltaTime);
break;
- case AiState.Attack:
+ case AIState.Attack:
+ run = coolDownTimer <= 0.0f;
UpdateAttack(deltaTime);
break;
+ case AIState.Eat:
+ UpdateEating(deltaTime);
+ break;
+ case AIState.Escape:
+ run = true;
+ UpdateEscape(deltaTime);
+ break;
+ default:
+ throw new NotImplementedException();
}
- steeringManager.Update();
- }
-
- private void UpdateNone(float deltaTime)
- {
- //wander around randomly
- if (Character.Submarine==null && SimPosition.Y < ConvertUnits.ToSimUnits(SubmarineBody.DamageDepth*0.5f))
+ if (run)
{
- steeringManager.SteeringManual(deltaTime, Vector2.UnitY);
+ steeringManager.Update(Character.AnimController.InWater ?
+ Character.AnimController.SwimSpeedMultiplier : Character.AnimController.RunSpeedMultiplier);
}
else
{
- steeringManager.SteeringAvoid(deltaTime, 0.1f);
- steeringManager.SteeringWander(0.5f);
+ steeringManager.Update();
+ }
+ }
+
+ #region Idle
+
+ private void UpdateNone(float deltaTime)
+ {
+ attackingLimb = null;
+ coolDownTimer -= deltaTime;
+
+ if (Character.Submarine == null && SimPosition.Y < ConvertUnits.ToSimUnits(SubmarineBody.DamageDepth * 0.5f))
+ {
+ //steer straight up if very deep
+ steeringManager.SteeringManual(deltaTime, Vector2.UnitY);
+ return;
}
- attackingLimb = null;
- attackTimer = 0.0f;
+ if (attachToWalls && Character.Submarine == null)
+ {
+ raycastTimer -= deltaTime;
+ //check if there are any walls nearby the character could attach to
+ if (raycastTimer < 1.0f)
+ {
+ wallAttackPos = Vector2.Zero;
- coolDownTimer -= deltaTime;
+ var cells = Level.Loaded.GetCells(WorldPosition, 1);
+ if (cells.Count > 0)
+ {
+ Body closestBody = Submarine.CheckVisibility(Character.SimPosition, ConvertUnits.ToSimUnits(cells[0].Center));
+ if (closestBody != null && closestBody.UserData is Voronoi2.VoronoiCell)
+ {
+ wallAttackPos = Submarine.LastPickedPosition;
+ }
+ }
+ raycastTimer = RaycastInterval;
+ }
+ }
+ else
+ {
+ wallAttackPos = Vector2.Zero;
+ }
+
+ if (wallAttackPos == Vector2.Zero)
+ {
+ //wander around randomly
+ steeringManager.SteeringAvoid(deltaTime, 0.1f);
+ steeringManager.SteeringWander(0.5f);
+ return;
+ }
+
+ float dist = Vector2.Distance(SimPosition, wallAttackPos);
+ if (dist < Math.Max(Math.Max(Character.AnimController.Collider.radius, Character.AnimController.Collider.width), Character.AnimController.Collider.height) * 1.2f)
+ {
+ //close enough to a wall -> attach
+ Character.AnimController.Collider.MoveToPos(wallAttackPos, 1.0f);
+ steeringManager.Reset();
+
+ }
+ else
+ {
+ //move closer to the wall
+ steeringManager.SteeringAvoid(deltaTime, 0.1f);
+ steeringManager.SteeringSeek(wallAttackPos);
+ }
}
-
+
+ #endregion
+
+ #region Escape
+
+ private void UpdateEscape(float deltaTime)
+ {
+ SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition) * 5);
+ SteeringManager.SteeringWander(1.0f);
+ SteeringManager.SteeringAvoid(deltaTime, 2f);
+ }
+
+ #endregion
+
+ #region Attack
+
private void UpdateAttack(float deltaTime)
{
if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed)
{
- state = AiState.None;
+ state = AIState.None;
return;
}
@@ -198,7 +295,22 @@ namespace Barotrauma
if (selectedAiTarget.Entity != null && Character.Submarine == null && selectedAiTarget.Entity.Submarine != null) attackSimPosition += ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
}
-
+ else if (selectedAiTarget.Entity is Character)
+ {
+ //target the closest limb if the target is a character
+ float closestDist = Vector2.DistanceSquared(selectedAiTarget.SimPosition, SimPosition);
+ foreach (Limb limb in ((Character)selectedAiTarget.Entity).AnimController.Limbs)
+ {
+ if (limb == null) continue;
+ float dist = Vector2.DistanceSquared(limb.SimPosition, SimPosition);
+ if (dist < closestDist)
+ {
+ closestDist = dist;
+ attackSimPosition = limb.SimPosition;
+ }
+ }
+ }
+
if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater)
{
Character.AnimController.TargetDir = Character.SimPosition.X < attackSimPosition.X ? Direction.Right : Direction.Left;
@@ -229,13 +341,13 @@ namespace Barotrauma
{
foreach (Limb limb in Character.AnimController.Limbs)
{
- if (limb.attack==null) continue;
+ if (limb.attack == null) continue;
attackLimb = limb;
if (ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackSimPosition)) > limb.attack.Range) continue;
-
+
attackingLimb = limb;
- break;
+ break;
}
if (Character.IsRemotePlayer)
@@ -245,36 +357,18 @@ namespace Barotrauma
}
if (attackLimb != null)
{
- steeringManager.SteeringSeek(attackSimPosition - (attackLimb.SimPosition - SimPosition));
+ steeringManager.SteeringSeek(attackSimPosition - (attackLimb.SimPosition - SimPosition), 3);
if (steeringManager is IndoorsSteeringManager)
{
var indoorsSteering = (IndoorsSteeringManager)steeringManager;
- if (indoorsSteering.CurrentPath!=null && (indoorsSteering.CurrentPath.Finished || indoorsSteering.CurrentPath.Unreachable))
+ if (indoorsSteering.CurrentPath != null && (indoorsSteering.CurrentPath.Finished || indoorsSteering.CurrentPath.Unreachable))
{
steeringManager.SteeringManual(deltaTime, attackSimPosition - attackLimb.SimPosition);
}
}
if (attackingLimb != null) UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition);
- }
- }
-
- private void UpdateCoolDown(Vector2 attackPosition, float deltaTime)
- {
- coolDownTimer -= deltaTime;
- attackingLimb = null;
-
- if (selectedAiTarget.Entity is Hull ||
- Vector2.Distance(attackPosition, Character.AnimController.Limbs[0].SimPosition) < ConvertUnits.ToSimUnits(500.0f))
- {
- steeringManager.SteeringSeek(attackPosition, -0.8f);
- steeringManager.SteeringAvoid(deltaTime, 1.0f);
- }
- else
- {
- steeringManager.SteeringSeek(attackPosition, -0.5f);
- steeringManager.SteeringAvoid(deltaTime, 1.0f);
}
}
@@ -339,13 +433,16 @@ namespace Barotrauma
updateTargetsTimer = Math.Min(updateTargetsTimer, 0.1f);
coolDownTimer *= 0.1f;
- if (amount > 0.1f && attackWhenProvoked)
+ if (amount > 0.0f && attackWhenProvoked)
{
- attackHumans = 100.0f;
- attackRooms = 100.0f;
+ if (!(attacker is AICharacter) || (((AICharacter)attacker).AIController is HumanAIController))
+ {
+ attackHumans = 100.0f;
+ attackRooms = 100.0f;
+ }
}
- if (attacker==null || attacker.AiTarget==null) return;
+ if (attacker == null || attacker.AiTarget == null) return;
AITargetMemory targetMemory = FindTargetMemory(attacker.AiTarget);
targetMemory.Priority += amount;
}
@@ -363,7 +460,112 @@ namespace Barotrauma
coolDownTimer = attackCoolDown;
}
}
+
+ private void UpdateCoolDown(Vector2 attackPosition, float deltaTime)
+ {
+ coolDownTimer -= deltaTime;
+ attackingLimb = null;
+
+ float dist = Vector2.Distance(attackPosition, Character.SimPosition);
+
+ if (dist < ConvertUnits.ToSimUnits(500.0f))
+ {
+ steeringManager.SteeringSeek(attackPosition, -0.8f);
+ steeringManager.SteeringManual(deltaTime, Vector2.Normalize(Character.SimPosition - attackPosition) * (1.0f - (dist / 500.0f)));
+ }
+
+ steeringManager.SteeringAvoid(deltaTime, 1.0f);
+ }
+
+ #endregion
+
+ #region Eat
+
+ private void UpdateEating(float deltaTime)
+ {
+ if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed)
+ {
+ state = AIState.None;
+ return;
+ }
+
+ Limb mouthLimb = Array.Find(Character.AnimController.Limbs, l => l != null && l.MouthPos.HasValue);
+ if (mouthLimb == null) mouthLimb = Character.AnimController.GetLimb(LimbType.Head);
+
+ if (mouthLimb == null)
+ {
+ DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)");
+ state = AIState.None;
+ return;
+ }
+
+ Character targetCharacter = selectedAiTarget.Entity as Character;
+ float eatSpeed = Character.Mass / targetCharacter.Mass * 0.1f;
+
+ eatTimer += deltaTime * eatSpeed;
+
+ Vector2 mouthPos = mouthLimb.SimPosition;
+ if (mouthLimb.MouthPos.HasValue)
+ {
+ float cos = (float)Math.Cos(mouthLimb.Rotation);
+ float sin = (float)Math.Sin(mouthLimb.Rotation);
+
+ mouthPos += new Vector2(
+ mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin,
+ mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos);
+ }
+
+ Vector2 attackSimPosition = Character.Submarine == null ? ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition;
+
+
+ Vector2 limbDiff = attackSimPosition - mouthPos;
+ float limbDist = limbDiff.Length();
+ if (limbDist < 1.0f)
+ {
+ //pull the target character to the position of the mouth
+ //(+ make the force fluctuate to waggle the character a bit)
+ targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
+
+ //pull the character's mouth to the target character (again with a fluctuating force)
+ float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
+ steeringManager.SteeringManual(deltaTime, limbDiff * pullStrength);
+ mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength);
+
+ if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
+ {
+ //apply damage to the target character to get some blood particles flying
+ targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), 10.0f, false);
+
+ //keep severing joints until there is only one limb left
+ LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered);
+ if (nonSeveredJoints.Length == 0)
+ {
+ //only one limb left, the character is now full eaten
+ Entity.Spawner.AddToRemoveQueue(targetCharacter);
+ selectedAiTarget = null;
+ state = AIState.None;
+ }
+ else //sever a random joint
+ {
+ targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]);
+ }
+ }
+ }
+ else if (limbDist < 2.0f)
+ {
+ steeringManager.SteeringManual(deltaTime, limbDiff);
+ Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
+ }
+ else
+ {
+ steeringManager.SteeringSeek(attackSimPosition + (mouthPos - SimPosition), 3);
+ }
+ }
+ #endregion
+
+ #region Targeting
+
//goes through all the AItargets, evaluates how preferable it is to attack the target,
//whether the Character can see/hear the target and chooses the most preferable target within
//sight/hearing range
@@ -387,33 +589,62 @@ namespace Barotrauma
float valueModifier = 0.0f;
float dist = 0.0f;
- IDamageable targetDamageable = target.Entity as IDamageable;
- if (targetDamageable!=null && targetDamageable.Health <= 0.0f) continue;
Character targetCharacter = target.Entity as Character;
//ignore the aitarget if it is the Character itself
if (targetCharacter == character) continue;
-
- if (targetCharacter!=null)
+
+ if (targetCharacter != null)
{
- if (attackHumans == 0.0f || targetCharacter.SpeciesName != "human") continue;
-
- valueModifier = attackHumans;
+ if (targetCharacter.IsDead)
+ {
+ if (eatDeadPriority == 0.0f) continue;
+ valueModifier = eatDeadPriority;
+ }
+ else if (targetCharacter.SpeciesName == "human")
+ {
+ if (attackHumans == 0.0f) continue;
+ valueModifier = attackHumans;
+ }
+ else
+ {
+ EnemyAIController enemy = targetCharacter.AIController as EnemyAIController;
+ if (enemy != null)
+ {
+ if (enemy.combatStrength > combatStrength)
+ {
+ valueModifier = attackStronger;
+ }
+ else if (enemy.combatStrength < combatStrength)
+ {
+ valueModifier = attackWeaker;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ }
}
else if (target.Entity!=null && attackRooms != 0.0f)
{
+ IDamageable targetDamageable = target.Entity as IDamageable;
+ if (targetDamageable != null && targetDamageable.Health <= 0.0f) continue;
+
//skip the target if it's the room the Character is inside of
if (character.AnimController.CurrentHull != null && character.AnimController.CurrentHull == target.Entity as Hull) continue;
valueModifier = attackRooms;
}
+ if (valueModifier == 0.0f) continue;
+
dist = Vector2.Distance(character.WorldPosition, target.WorldPosition);
//if the target has been within range earlier, the character will notice it more easily
//(i.e. remember where the target was)
- if (targetMemories.ContainsKey(target)) dist *= 0.1f;
+ if (targetMemories.ContainsKey(target)) dist *= 0.5f;
//ignore target if it's too far to see or hear
if (dist > target.SightRange * sight && dist > target.SoundRange * hearing) continue;
@@ -433,20 +664,7 @@ namespace Barotrauma
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd);
Structure closestStructure = (closestBody == null) ? null : closestBody.UserData as Structure;
-
- if (targetDamageable != null)
- {
- valueModifier = valueModifier / targetDamageable.Health;
- }
- else if (closestStructure!=null)
- {
- valueModifier = valueModifier / ((IDamageable)closestStructure).Health;
- }
- else
- {
- valueModifier = valueModifier / 1000.0f;
- }
-
+
if (selectedAiTarget == null || Math.Abs(valueModifier) > Math.Abs(targetValue))
{
selectedAiTarget = target;
@@ -497,6 +715,8 @@ namespace Barotrauma
}
}
+ #endregion
+
public override void DebugDraw(SpriteBatch spriteBatch)
{
if (Character.IsDead) return;
diff --git a/Barotrauma/Source/Characters/AICharacter.cs b/Barotrauma/Source/Characters/AICharacter.cs
index 7a32bd5b6..7411305f7 100644
--- a/Barotrauma/Source/Characters/AICharacter.cs
+++ b/Barotrauma/Source/Characters/AICharacter.cs
@@ -51,7 +51,7 @@ namespace Barotrauma
{
switch (aiController.State)
{
- case AIController.AiState.Attack:
+ case AIController.AIState.Attack:
PlaySound(CharacterSound.SoundType.Attack);
break;
default:
diff --git a/Barotrauma/Source/Characters/Animation/AnimController.cs b/Barotrauma/Source/Characters/Animation/AnimController.cs
index 8eb710cd9..b7e03d710 100644
--- a/Barotrauma/Source/Characters/Animation/AnimController.cs
+++ b/Barotrauma/Source/Characters/Animation/AnimController.cs
@@ -12,13 +12,25 @@ namespace Barotrauma
protected Character character;
- protected float walkSpeed, swimSpeed;
-
+ protected float walkSpeed, swimSpeed;
+
protected float walkPos;
protected readonly Vector2 stepSize;
protected readonly float legTorque;
-
+
+ public float RunSpeedMultiplier
+ {
+ get;
+ private set;
+ }
+
+ public float SwimSpeedMultiplier
+ {
+ get;
+ private set;
+ }
+
public AnimController(Character character, XElement element)
: base(character, element)
{
@@ -30,6 +42,9 @@ namespace Barotrauma
walkSpeed = ToolBox.GetAttributeFloat(element, "walkspeed", 1.0f);
swimSpeed = ToolBox.GetAttributeFloat(element, "swimspeed", 1.0f);
+ RunSpeedMultiplier = ToolBox.GetAttributeFloat(element, "runspeedmultiplier", 2f);
+ SwimSpeedMultiplier = ToolBox.GetAttributeFloat(element, "swimspeedmultiplier", 1.5f);
+
legTorque = ToolBox.GetAttributeFloat(element, "legtorque", 0.0f);
}
@@ -37,7 +52,7 @@ namespace Barotrauma
public virtual void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle) { }
- public virtual void DragCharacter(Character target, LimbType rightHandTarget = LimbType.RightHand, LimbType leftHandTarget = LimbType.LeftHand) { }
+ public virtual void DragCharacter(Character target) { }
}
diff --git a/Barotrauma/Source/Characters/Animation/FishAnimController.cs b/Barotrauma/Source/Characters/Animation/FishAnimController.cs
index bce305169..1d352de69 100644
--- a/Barotrauma/Source/Characters/Animation/FishAnimController.cs
+++ b/Barotrauma/Source/Characters/Animation/FishAnimController.cs
@@ -15,6 +15,8 @@ namespace Barotrauma
private float waveAmplitude;
private float waveLength;
+ private float steerTorque;
+
private bool rotateTowardsMovement;
private bool mirror, flip;
@@ -23,16 +25,20 @@ namespace Barotrauma
private float? footRotation;
+ private float deathAnimTimer, deathAnimDuration = 5.0f;
+
public FishAnimController(Character character, XElement element)
: base(character, element)
{
- waveAmplitude = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "waveamplitude", 0.0f));
- waveLength = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "wavelength", 0.0f));
+ waveAmplitude = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "waveamplitude", 0.0f));
+ waveLength = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "wavelength", 0.0f));
+
+ steerTorque = ToolBox.GetAttributeFloat(element, "steertorque", 25.0f);
- flip = ToolBox.GetAttributeBool(element, "flip", true);
- mirror = ToolBox.GetAttributeBool(element, "mirror", false);
+ flip = ToolBox.GetAttributeBool(element, "flip", true);
+ mirror = ToolBox.GetAttributeBool(element, "mirror", false);
- float footRot = ToolBox.GetAttributeFloat(element,"footrotation", float.NaN);
+ float footRot = ToolBox.GetAttributeFloat(element, "footrotation", float.NaN);
if (float.IsNaN(footRot))
{
footRotation = null;
@@ -63,6 +69,12 @@ namespace Barotrauma
Collider.LinearVelocity = (MainLimb.SimPosition - Collider.SimPosition) * 60.0f;
Collider.SmoothRotate(MainLimb.Rotation);
}
+
+ if (character.IsDead && deathAnimTimer < deathAnimDuration)
+ {
+ deathAnimTimer += deltaTime;
+ UpdateDying(deltaTime);
+ }
return;
}
@@ -176,12 +188,12 @@ namespace Barotrauma
if (rotateTowardsMovement)
{
Collider.SmoothRotate(movementAngle, 25.0f);
- MainLimb.body.SmoothRotate(movementAngle, 25.0f);
+ MainLimb.body.SmoothRotate(movementAngle, steerTorque);
}
else
{
Collider.SmoothRotate(HeadAngle * Dir, 25.0f);
- MainLimb.body.SmoothRotate(HeadAngle * Dir, 25.0f);
+ MainLimb.body.SmoothRotate(HeadAngle * Dir, steerTorque);
}
Limb tail = GetLimb(LimbType.Tail);
@@ -260,7 +272,7 @@ namespace Barotrauma
if (limb.RefJointIndex>-1)
{
- RevoluteJoint refJoint = limbJoints[limb.RefJointIndex];
+ RevoluteJoint refJoint = LimbJoints[limb.RefJointIndex];
footPos.X = refJoint.WorldAnchorA.X;
}
footPos.X += limb.StepOffset.X * Dir;
@@ -297,8 +309,8 @@ namespace Barotrauma
Limb head = GetLimb(LimbType.Head);
Limb tail = GetLimb(LimbType.Tail);
- if (head != null) head.body.ApplyTorque(head.Mass * Dir * (float)Math.Sin(walkPos) * 5.0f);
- if (tail != null) tail.body.ApplyTorque(tail.Mass * -Dir * (float)Math.Sin(walkPos) * 5.0f);
+ if (head != null && !head.IsSevered) head.body.ApplyTorque((float)(Math.Sqrt(head.Mass) * Dir * Math.Sin(walkPos)) * 10.0f);
+ if (tail != null && !tail.IsSevered) tail.body.ApplyTorque((float)(Math.Sqrt(tail.Mass) * -Dir * (float)Math.Sin(walkPos)) * 10.0f);
walkPos += deltaTime * 5.0f;
@@ -306,9 +318,9 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
- if (limb.type == LimbType.Head || limb.type == LimbType.Tail) continue;
+ if (limb.type == LimbType.Head || limb.type == LimbType.Tail || limb.IsSevered) continue;
- limb.body.ApplyForce((centerOfMass - limb.SimPosition) * (float)Math.Sin(walkPos) * limb.Mass * 10.0f);
+ limb.body.ApplyForce((centerOfMass - limb.SimPosition) * (float)(Math.Sin(walkPos) * Math.Sqrt(limb.Mass)) * 10.0f);
}
}
diff --git a/Barotrauma/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/Source/Characters/Animation/HumanoidAnimController.cs
index 74de33026..c126eaa2b 100644
--- a/Barotrauma/Source/Characters/Animation/HumanoidAnimController.cs
+++ b/Barotrauma/Source/Characters/Animation/HumanoidAnimController.cs
@@ -571,7 +571,6 @@ namespace Barotrauma
}
float targetSpeed = TargetMovement.Length();
- if (targetSpeed > 0.0f) TargetMovement /= targetSpeed;
if (targetSpeed > 0.1f)
{
@@ -633,7 +632,7 @@ namespace Barotrauma
Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement * swimSpeed, movementLerp);
}
- walkPos += movement.Length() * 0.15f;
+ walkPos += movement.Length() * 0.2f;
footPos = Collider.SimPosition - new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation)) * 0.4f;
for (int i = -1; i<2; i+=2)
@@ -691,7 +690,7 @@ namespace Barotrauma
handPos += head.LinearVelocity * 0.1f;
- float handCyclePos = walkPos / 3.0f * -Dir;
+ float handCyclePos = walkPos / 2.0f * -Dir;
float handPosX = (float)Math.Cos(handCyclePos) * 0.4f;
float handPosY = (float)Math.Sin(handCyclePos) * 1.0f;
handPosY = MathHelper.Clamp(handPosY, -0.8f, 0.8f);
@@ -883,23 +882,48 @@ namespace Barotrauma
head.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.6f + yPos);
head.pullJoint.Enabled = true;
}
- public override void DragCharacter(Character target, LimbType rightHandTarget = LimbType.RightHand, LimbType leftHandTarget = LimbType.LeftHand)
+ public override void DragCharacter(Character target)
{
if (target == null) return;
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
+ Limb targetLeftHand = target.AnimController.GetLimb(LimbType.LeftHand);
+ Limb targetRightHand = target.AnimController.GetLimb(LimbType.RightHand);
+
//only grab with one hand when swimming
leftHand.Disabled = true;
if (!inWater) rightHand.Disabled = true;
for (int i = 0; i < 2; i++)
{
- LimbType type = i == 0 ? leftHandTarget : rightHandTarget;
- Limb targetLimb = target.AnimController.GetLimb(type);
+ Limb targetLimb = target.AnimController.GetLimb(LimbType.Torso);
- Limb pullLimb = GetLimb(i == 0 ? LimbType.LeftHand : LimbType.RightHand);
+ if (i == 0)
+ {
+ if (!targetLeftHand.IsSevered)
+ {
+ targetLimb = targetLeftHand;
+ }
+ else if (!targetRightHand.IsSevered)
+ {
+ targetLimb = targetRightHand;
+ }
+ }
+ else
+ {
+ if (!targetRightHand.IsSevered)
+ {
+ targetLimb = targetRightHand;
+ }
+ else if (!targetLeftHand.IsSevered)
+ {
+ targetLimb = targetLeftHand;
+ }
+ }
+
+ Limb pullLimb = i == 0 ? leftHand : rightHand;
if (i == 1 && inWater)
{
@@ -1013,7 +1037,7 @@ namespace Barotrauma
itemAngle = (torso.body.Rotation + holdAngle * Dir);
}
- Vector2 shoulderPos = limbJoints[2].WorldAnchorA;
+ Vector2 shoulderPos = LimbJoints[2].WorldAnchorA;
Vector2 transformedHoldPos = shoulderPos;
if (itemPos == Vector2.Zero || Anim == Animation.Climbing || usingController)
@@ -1072,7 +1096,7 @@ namespace Barotrauma
private void HandIK(Limb hand, Vector2 pos, float force = 1.0f)
{
- Vector2 shoulderPos = limbJoints[2].WorldAnchorA;
+ Vector2 shoulderPos = LimbJoints[2].WorldAnchorA;
Limb arm = (hand.type == LimbType.LeftHand) ? GetLimb(LimbType.LeftArm) : GetLimb(LimbType.RightArm);
diff --git a/Barotrauma/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/Source/Characters/Animation/Ragdoll.cs
index f069d5d1f..bde487a1e 100644
--- a/Barotrauma/Source/Characters/Animation/Ragdoll.cs
+++ b/Barotrauma/Source/Characters/Animation/Ragdoll.cs
@@ -28,17 +28,13 @@ namespace Barotrauma
if (frozen == value) return;
frozen = value;
-
- /*foreach (Limb l in Limbs)
- {
- l.body.PhysEnabled = !frozen;
- }*/
+
Collider.PhysEnabled = !frozen;
}
}
private Dictionary limbDictionary;
- public RevoluteJoint[] limbJoints;
+ public LimbJoint[] LimbJoints;
private bool simplePhysicsEnabled;
@@ -163,18 +159,20 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
+ if (limb.IsSevered) continue;
limb.body.Enabled = !simplePhysicsEnabled;
}
- foreach (RevoluteJoint joint in limbJoints)
+ foreach (LimbJoint joint in LimbJoints)
{
- joint.Enabled = !simplePhysicsEnabled;
+ joint.Enabled = !joint.IsSevered && !simplePhysicsEnabled;
}
if (!simplePhysicsEnabled)
{
foreach (Limb limb in Limbs)
{
+ if (limb.IsSevered) continue;
limb.body.SetTransform(Collider.SimPosition, Collider.Rotation);
}
}
@@ -278,9 +276,9 @@ namespace Barotrauma
float scale = ToolBox.GetAttributeFloat(element, "scale", 1.0f);
- Limbs = new Limb[element.Elements("limb").Count()];
- limbJoints = new RevoluteJoint[element.Elements("joint").Count()];
- limbDictionary = new Dictionary();
+ Limbs = new Limb[element.Elements("limb").Count()];
+ LimbJoints = new LimbJoint[element.Elements("joint").Count()];
+ limbDictionary = new Dictionary();
headPosition = ToolBox.GetAttributeFloat(element, "headposition", 50.0f);
headPosition = ConvertUnits.ToSimUnits(headPosition);
@@ -346,7 +344,7 @@ namespace Barotrauma
UpdateCollisionCategories();
- foreach (var joint in limbJoints)
+ foreach (var joint in LimbJoints)
{
joint.BodyB.SetTransform(
joint.BodyA.Position + (joint.LocalAnchorA - joint.LocalAnchorB)*0.1f,
@@ -385,9 +383,8 @@ namespace Barotrauma
Vector2 limb2Pos = ToolBox.GetAttributeVector2(subElement, "limb2anchor", Vector2.Zero) * scale;
limb2Pos = ConvertUnits.ToSimUnits(limb2Pos);
- RevoluteJoint joint = new RevoluteJoint(Limbs[limb1ID].body.FarseerBody, Limbs[limb2ID].body.FarseerBody, limb1Pos, limb2Pos);
-
- joint.CollideConnected = false;
+ LimbJoint joint = new LimbJoint(Limbs[limb1ID], Limbs[limb2ID], limb1Pos, limb2Pos);
+ joint.CanBeSevered = ToolBox.GetAttributeBool(subElement, "canbesevered", false);
if (subElement.Attribute("lowerlimit") != null)
{
@@ -396,22 +393,18 @@ namespace Barotrauma
joint.UpperLimit = float.Parse(subElement.Attribute("upperlimit").Value) * ((float)Math.PI / 180.0f);
}
- joint.MotorEnabled = true;
- joint.MaxMotorTorque = 0.25f;
-
GameMain.World.AddJoint(joint);
- for (int i = 0; i < limbJoints.Length; i++)
+ for (int i = 0; i < LimbJoints.Length; i++)
{
- if (limbJoints[i] != null) continue;
+ if (LimbJoints[i] != null) continue;
- limbJoints[i] = joint;
+ LimbJoints[i] = joint;
return;
}
- Array.Resize(ref limbJoints, limbJoints.Length + 1);
- limbJoints[limbJoints.Length - 1] = joint;
-
+ Array.Resize(ref LimbJoints, LimbJoints.Length + 1);
+ LimbJoints[LimbJoints.Length - 1] = joint;
}
public void AddLimb(Limb limb)
@@ -509,9 +502,9 @@ namespace Barotrauma
{
Limb limb = (Limb)f1.Body.UserData;
- if (impact > 3.0f && limb.HitSound != null && limb.soundTimer <= 0.0f)
+ if (impact > 3.0f && limb.HitSound != null && limb.SoundTimer <= 0.0f)
{
- limb.soundTimer = Limb.SoundInterval;
+ limb.SoundTimer = Limb.SoundInterval;
limb.HitSound.Play(volume, impact * 100.0f, limb.WorldPosition);
}
}
@@ -531,6 +524,51 @@ namespace Barotrauma
}
}
+ public void SeverLimbJoint(LimbJoint limbJoint)
+ {
+ limbJoint.IsSevered = true;
+ limbJoint.Enabled = false;
+
+ List connectedLimbs = new List();
+ List checkedJoints = new List();
+
+ GetConnectedLimbs(connectedLimbs, checkedJoints, MainLimb);
+ foreach (Limb limb in Limbs)
+ {
+ if (!connectedLimbs.Contains(limb))
+ {
+ limb.IsSevered = true;
+ }
+ }
+
+ }
+
+ private void GetConnectedLimbs(List connectedLimbs, List checkedJoints, Limb limb)
+ {
+ connectedLimbs.Add(limb);
+
+ foreach (LimbJoint joint in LimbJoints)
+ {
+ if (joint.IsSevered || checkedJoints.Contains(joint)) continue;
+ if (joint.LimbA == limb)
+ {
+ if (!connectedLimbs.Contains(joint.LimbB))
+ {
+ checkedJoints.Add(joint);
+ GetConnectedLimbs(connectedLimbs, checkedJoints, joint.LimbB);
+ }
+ }
+ else if (joint.LimbB == limb)
+ {
+ if (!connectedLimbs.Contains(joint.LimbA))
+ {
+ checkedJoints.Add(joint);
+ GetConnectedLimbs(connectedLimbs, checkedJoints, joint.LimbA);
+ }
+ }
+ }
+ }
+
public virtual void Draw(SpriteBatch spriteBatch)
{
if (simplePhysicsEnabled) return;
@@ -559,13 +597,15 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Red, true, 0.01f);
}
- limb.body.DebugDraw(spriteBatch, inWater ? Color.Cyan : Color.White);
+ Color limbColor = inWater ? Color.Cyan : Color.White;
+ if (limb.IsSevered) limbColor = Color.Red;
+ limb.body.DebugDraw(spriteBatch, limbColor);
}
Collider.DebugDraw(spriteBatch, frozen ? Color.Red : (inWater ? Color.SkyBlue : Color.Gray));
GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.ToString(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange);
- foreach (RevoluteJoint joint in limbJoints)
+ foreach (RevoluteJoint joint in LimbJoints)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
@@ -619,22 +659,24 @@ namespace Barotrauma
{
dir = (dir == Direction.Left) ? Direction.Right : Direction.Left;
- for (int i = 0; i < limbJoints.Length; i++)
+ for (int i = 0; i < LimbJoints.Length; i++)
{
- float lowerLimit = -limbJoints[i].UpperLimit;
- float upperLimit = -limbJoints[i].LowerLimit;
+ float lowerLimit = -LimbJoints[i].UpperLimit;
+ float upperLimit = -LimbJoints[i].LowerLimit;
- limbJoints[i].LowerLimit = lowerLimit;
- limbJoints[i].UpperLimit = upperLimit;
+ LimbJoints[i].LowerLimit = lowerLimit;
+ LimbJoints[i].UpperLimit = upperLimit;
- limbJoints[i].LocalAnchorA = new Vector2(-limbJoints[i].LocalAnchorA.X, limbJoints[i].LocalAnchorA.Y);
- limbJoints[i].LocalAnchorB = new Vector2(-limbJoints[i].LocalAnchorB.X, limbJoints[i].LocalAnchorB.Y);
+ LimbJoints[i].LocalAnchorA = new Vector2(-LimbJoints[i].LocalAnchorA.X, LimbJoints[i].LocalAnchorA.Y);
+ LimbJoints[i].LocalAnchorB = new Vector2(-LimbJoints[i].LocalAnchorB.X, LimbJoints[i].LocalAnchorB.Y);
}
foreach (Limb limb in Limbs)
{
- if (limb == null) continue;
+ if (limb == null || limb.IsSevered) continue;
+
+ limb.Dir = Dir;
if (limb.sprite != null)
{
@@ -643,12 +685,13 @@ namespace Barotrauma
limb.sprite.Origin = spriteOrigin;
}
- limb.Dir = Dir;
-
- /*if (limb.LightSource != null)
+
+ if (limb.MouthPos.HasValue)
{
- limb.LightSource.FlipX();
- }*/
+ limb.MouthPos = new Vector2(
+ -limb.MouthPos.Value.X,
+ limb.MouthPos.Value.Y);
+ }
if (limb.pullJoint != null)
{
@@ -663,12 +706,15 @@ namespace Barotrauma
public Vector2 GetCenterOfMass()
{
Vector2 centerOfMass = Vector2.Zero;
+ float totalMass = 0.0f;
foreach (Limb limb in Limbs)
{
+ if (limb.IsSevered) continue;
centerOfMass += limb.Mass * limb.SimPosition;
+ totalMass += limb.Mass;
}
- centerOfMass /= Mass;
+ centerOfMass /= totalMass;
return centerOfMass;
}
@@ -769,6 +815,7 @@ namespace Barotrauma
{
foreach (Limb limb in Limbs)
{
+ if (limb.IsSevered) continue;
if (limb.body.FarseerBody.ContactList == null) continue;
ContactEdge ce = limb.body.FarseerBody.ContactList;
@@ -781,6 +828,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
+ if (limb.IsSevered) continue;
limb.body.LinearVelocity += velocityChange;
}
@@ -805,7 +853,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
- if (limb.ignoreCollisions) continue;
+ if (limb.ignoreCollisions || limb.IsSevered) continue;
try
{
@@ -1145,6 +1193,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
+ if (limb.IsSevered) continue;
//check visibility from the new position of the collider to the new position of this limb
Vector2 movePos = limb.SimPosition + limbMoveAmount;
@@ -1208,6 +1257,7 @@ namespace Barotrauma
//(in case the ragdoll has gotten stuck somewhere)
foreach (Limb limb in Limbs)
{
+ if (limb.IsSevered) continue;
limb.body.CollidesWith = Physics.CollisionNone;
}
@@ -1463,22 +1513,28 @@ namespace Barotrauma
public void Remove()
{
- foreach (Limb l in Limbs)
+ if (Limbs != null)
{
- l.Remove();
+ foreach (Limb l in Limbs)
+ {
+ l.Remove();
+ }
+ Limbs = null;
}
- Limbs = null;
foreach (PhysicsBody b in collider)
{
b.Remove();
}
- foreach (RevoluteJoint joint in limbJoints)
+ if (LimbJoints != null)
{
- GameMain.World.RemoveJoint(joint);
+ foreach (RevoluteJoint joint in LimbJoints)
+ {
+ GameMain.World.RemoveJoint(joint);
+ }
+ LimbJoints = null;
}
- limbJoints = null;
list.Remove(this);
}
diff --git a/Barotrauma/Source/Characters/Attack.cs b/Barotrauma/Source/Characters/Attack.cs
index ee2d24c04..dcdf7dd0e 100644
--- a/Barotrauma/Source/Characters/Attack.cs
+++ b/Barotrauma/Source/Characters/Attack.cs
@@ -18,7 +18,7 @@ namespace Barotrauma
{
public readonly float Damage;
public readonly float Bleeding;
-
+
public readonly bool HitArmor;
public AttackResult(float damage, float bleeding, bool hitArmor=false)
@@ -49,6 +49,12 @@ namespace Barotrauma
public readonly float TargetForce;
+ public readonly float SeverLimbsProbability;
+
+ //the indices of the limbs Force is applied on
+ //(if none, force is applied only to the limb the attack is attached to)
+ public readonly List ApplyForceOnLimbs;
+
private Sound sound;
private ParticleEmitterPrefab particleEmitterPrefab;
@@ -91,29 +97,41 @@ namespace Barotrauma
DamageType = DamageType.None;
}
-
- damage = ToolBox.GetAttributeFloat(element, "damage", 0.0f);
+ damage = ToolBox.GetAttributeFloat(element, "damage", 0.0f);
structureDamage = ToolBox.GetAttributeFloat(element, "structuredamage", 0.0f);
- bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f);
+ bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f);
+ Stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f);
- Force = ToolBox.GetAttributeFloat(element,"force", 0.0f);
- TargetForce = ToolBox.GetAttributeFloat(element, "targetforce", 0.0f);
-
- Torque = ToolBox.GetAttributeFloat(element, "torque", 0.0f);
-
- Stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f);
+ SeverLimbsProbability = ToolBox.GetAttributeFloat(element, "severlimbsprobability", 0.0f);
+ Force = ToolBox.GetAttributeFloat(element, "force", 0.0f);
+ TargetForce = ToolBox.GetAttributeFloat(element, "targetforce", 0.0f);
+ Torque = ToolBox.GetAttributeFloat(element, "torque", 0.0f);
+
string soundPath = ToolBox.GetAttributeString(element, "sound", "");
if (!string.IsNullOrWhiteSpace(soundPath))
{
sound = Sound.Load(soundPath);
}
- Range = ToolBox.GetAttributeFloat(element, "range", 0.0f);
+ Range = ToolBox.GetAttributeFloat(element, "range", 0.0f);
+ Duration = ToolBox.GetAttributeFloat(element, "duration", 0.0f);
- Duration = ToolBox.GetAttributeFloat(element, "duration", 0.0f);
-
- priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f);
+ priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f);
+
+ string limbIndicesStr = ToolBox.GetAttributeString(element, "applyforceonlimbs", "");
+ if (!string.IsNullOrWhiteSpace(limbIndicesStr))
+ {
+ ApplyForceOnLimbs = new List();
+ foreach (string limbIndexStr in limbIndicesStr.Split(','))
+ {
+ int limbIndex;
+ if (int.TryParse(limbIndexStr, out limbIndex))
+ {
+ ApplyForceOnLimbs.Add(limbIndex);
+ }
+ }
+ }
foreach (XElement subElement in element.Elements())
{
diff --git a/Barotrauma/Source/Characters/Character.cs b/Barotrauma/Source/Characters/Character.cs
index 0e133f1fa..471a0aab2 100644
--- a/Barotrauma/Source/Characters/Character.cs
+++ b/Barotrauma/Source/Characters/Character.cs
@@ -785,16 +785,14 @@ namespace Barotrauma
if (IsKeyDown(InputType.Run))
{
//can't run if
- // - not a humanoid
// - dragging someone
// - crouching
// - moving backwards
- if (AnimController is HumanoidAnimController &&
- selectedCharacter == null &&
- !((HumanoidAnimController)AnimController).Crouching &&
+ if (selectedCharacter == null &&
+ (!(AnimController is HumanoidAnimController) || !((HumanoidAnimController)AnimController).Crouching) &&
Math.Sign(targetMovement.X) != -Math.Sign(AnimController.Dir))
{
- targetMovement *= 3.0f;
+ targetMovement *= AnimController.InWater ? AnimController.SwimSpeedMultiplier : AnimController.RunSpeedMultiplier;
}
}
@@ -1161,9 +1159,10 @@ namespace Barotrauma
}
}
- if (moveCam && needsAir)
+ if (moveCam)
{
- if (pressureProtection < 80.0f &&
+ if (needsAir &&
+ pressureProtection < 80.0f &&
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 50.0f))
{
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
@@ -1172,7 +1171,16 @@ namespace Barotrauma
(pressure / 50.0f) * Rand.Range(1.0f, 1.05f),
(pressure - 50.0f) / 50.0f);
}
- cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, 250.0f, 0.05f);
+
+ if (IsHumanoid)
+ {
+ cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, 250.0f, deltaTime);
+ }
+ else
+ {
+ //increased visibility range when controlling large a non-humanoid
+ cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, MathHelper.Clamp(Mass, 250.0f, 800.0f), deltaTime);
+ }
}
cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
@@ -1721,46 +1729,77 @@ namespace Barotrauma
public virtual AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false)
{
- var attackResult = AddDamage(worldPosition, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce);
+ Limb limbHit = null;
+ var attackResult = AddDamage(worldPosition, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce, out limbHit);
+ if (limbHit == null) return new AttackResult();
var attackingCharacter = attacker as Character;
if (attackingCharacter != null && attackingCharacter.AIController == null)
{
GameServer.Log(Name + " attacked by " + attackingCharacter.Name+". Damage: "+attackResult.Damage+" Bleeding damage: "+attackResult.Bleeding, ServerLog.MessageType.Attack);
}
+
+ if (GameMain.Client == null &&
+ isDead &&
+ health - attackResult.Damage <= minHealth && Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability)
+ {
+ foreach (LimbJoint joint in AnimController.LimbJoints)
+ {
+ if (joint.CanBeSevered && (joint.LimbA == limbHit || joint.LimbB == limbHit))
+ {
+ AnimController.SeverLimbJoint(joint);
+
+ if (joint.LimbA == limbHit)
+ {
+ joint.LimbB.body.LinearVelocity += limbHit.LinearVelocity * 0.5f;
+ }
+ else
+ {
+ joint.LimbA.body.LinearVelocity += limbHit.LinearVelocity * 0.5f;
+ }
+ }
+ }
+ }
return attackResult;
}
public AttackResult AddDamage(Vector2 worldPosition, DamageType damageType, float amount, float bleedingAmount, float stun, bool playSound, float attackForce = 0.0f)
{
+ Limb temp = null;
+ 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)
+ {
+ hitLimb = null;
+
if (Removed) return new AttackResult();
SetStun(stun);
-
- Limb closestLimb = null;
+
float closestDistance = 0.0f;
foreach (Limb limb in AnimController.Limbs)
{
float distance = Vector2.Distance(worldPosition, limb.WorldPosition);
- if (closestLimb == null || distance < closestDistance)
+ if (hitLimb == null || distance < closestDistance)
{
- closestLimb = limb;
+ hitLimb = limb;
closestDistance = distance;
}
}
if (Math.Abs(attackForce) > 0.0f)
{
- closestLimb.body.ApplyForce((closestLimb.WorldPosition - worldPosition) * attackForce);
+ 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 = closestLimb.AddDamage(worldPosition, damageType, amount, bleedingAmount, playSound);
+ AttackResult attackResult = hitLimb.AddDamage(worldPosition, damageType, amount, bleedingAmount, playSound);
AddDamage(damageType == DamageType.Burn ? CauseOfDeath.Burn : causeOfDeath, attackResult.Damage, null);
-
- //health -= attackResult.Damage;
- //if (health <= 0.0f && damageType == DamageType.Burn) Kill(CauseOfDeath.Burn);
+
if (DoesBleed)
{
Bleeding += attackResult.Bleeding;
@@ -1834,7 +1873,7 @@ namespace Barotrauma
new Vector2(Rand.Range(-50f, 50f), Rand.Range(-100f, 50f)));
}
- foreach (var joint in AnimController.limbJoints)
+ foreach (var joint in AnimController.LimbJoints)
{
joint.LimitEnabled = false;
}
@@ -1884,20 +1923,14 @@ namespace Barotrauma
{
if (selectedItems[i] != null) selectedItems[i].Drop(this);
}
-
- if (aiTarget != null)
- {
- aiTarget.Remove();
- aiTarget = null;
- }
-
+
foreach (Limb limb in AnimController.Limbs)
{
if (limb.pullJoint == null) continue;
limb.pullJoint.Enabled = false;
}
- foreach (RevoluteJoint joint in AnimController.limbJoints)
+ foreach (RevoluteJoint joint in AnimController.LimbJoints)
{
joint.MotorEnabled = false;
}
@@ -1916,9 +1949,16 @@ namespace Barotrauma
Health = Math.Max(maxHealth * 0.1f, health);
- foreach (RevoluteJoint joint in AnimController.limbJoints)
+ foreach (LimbJoint joint in AnimController.LimbJoints)
{
joint.MotorEnabled = true;
+ joint.Enabled = true;
+ joint.IsSevered = false;
+ }
+
+ foreach (Limb limb in AnimController.Limbs)
+ {
+ limb.IsSevered = false;
}
if (GameMain.GameSession != null)
@@ -1929,6 +1969,13 @@ namespace Barotrauma
public override void Remove()
{
+#if DEBUG
+ if (Removed)
+ {
+ DebugConsole.ThrowError("Attempting to remove an already removed character\n" + Environment.StackTrace);
+ }
+#endif
+
base.Remove();
if (info != null) info.Remove();
@@ -1939,7 +1986,7 @@ namespace Barotrauma
if (GameMain.Client != null && GameMain.Client.Character == this) GameMain.Client.Character = null;
- if (aiTarget != null) aiTarget.Remove();
+ if (aiTarget != null) aiTarget.Remove();
if (AnimController != null) AnimController.Remove();
diff --git a/Barotrauma/Source/Characters/CharacterNetworking.cs b/Barotrauma/Source/Characters/CharacterNetworking.cs
index 0f52f1c80..83ef5b208 100644
--- a/Barotrauma/Source/Characters/CharacterNetworking.cs
+++ b/Barotrauma/Source/Characters/CharacterNetworking.cs
@@ -638,6 +638,19 @@ namespace Barotrauma
if (isDead)
{
msg.Write((byte)causeOfDeath);
+ List severedJointIndices = new List();
+ for (int i = 0; i < AnimController.LimbJoints.Length; i++)
+ {
+ if (AnimController.LimbJoints[i] != null && AnimController.LimbJoints[i].IsSevered)
+ {
+ severedJointIndices.Add(i);
+ }
+ }
+ msg.Write((byte)severedJointIndices.Count);
+ foreach (int jointIndex in severedJointIndices)
+ {
+ msg.Write((byte)jointIndex);
+ }
}
else
{
@@ -677,6 +690,7 @@ namespace Barotrauma
if (isDead)
{
causeOfDeath = (CauseOfDeath)msg.ReadByte();
+ byte severedLimbCount = msg.ReadByte();
if (causeOfDeath == CauseOfDeath.Pressure)
{
Implode(true);
@@ -685,6 +699,11 @@ namespace Barotrauma
{
Kill(causeOfDeath, true);
}
+ for (int i = 0; i < severedLimbCount; i++)
+ {
+ int severedJointIndex = msg.ReadByte();
+ AnimController.SeverLimbJoint(AnimController.LimbJoints[severedJointIndex]);
+ }
}
else
{
diff --git a/Barotrauma/Source/Characters/Limb.cs b/Barotrauma/Source/Characters/Limb.cs
index 2fa474003..fb61f3c50 100644
--- a/Barotrauma/Source/Characters/Limb.cs
+++ b/Barotrauma/Source/Characters/Limb.cs
@@ -19,11 +19,33 @@ namespace Barotrauma
LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist
};
+ class LimbJoint : RevoluteJoint
+ {
+ public bool IsSevered;
+ public bool CanBeSevered;
+
+ public readonly Limb LimbA, LimbB;
+
+ public LimbJoint(Limb limbA, Limb limbB, Vector2 anchor1, Vector2 anchor2)
+ : base(limbA.body.FarseerBody, limbB.body.FarseerBody, anchor1, anchor2)
+ {
+ CollideConnected = false;
+ MotorEnabled = true;
+ MaxMotorTorque = 0.25f;
+
+ LimbA = limbA;
+ LimbB = limbB;
+ }
+ }
+
class Limb
{
private const float LimbDensity = 15;
private const float LimbAngularDamping = 7;
+ //how long it takes for severed limbs to fade out
+ private const float SeveredFadeOutTime = 10.0f;
+
public readonly Character character;
//the physics body of the limb
@@ -51,12 +73,17 @@ namespace Barotrauma
private float damage, burnt;
+ private bool isSevered;
+ private float severedFadeOutTimer;
+
private readonly Vector2 armorSector;
private readonly float armorValue;
- Sound hitSound;
+ public Vector2? MouthPos;
+
+ private Sound hitSound;
//a timer for delaying when a hitsound/attacksound can be played again
- public float soundTimer;
+ public float SoundTimer;
public const float SoundInterval = 0.4f;
public readonly Attack attack;
@@ -66,7 +93,24 @@ namespace Barotrauma
private List wearingItems;
private Vector2 animTargetPos;
+
+ private float scale;
+ public float AttackTimer;
+
+ public bool IsSevered
+ {
+ get { return isSevered; }
+ set
+ {
+ isSevered = value;
+ if (isSevered)
+ {
+ damage = 100.0f;
+ }
+ }
+ }
+
public bool DoesFlip
{
get { return doesFlip; }
@@ -92,6 +136,11 @@ namespace Barotrauma
get { return body.Rotation; }
}
+ public float Scale
+ {
+ get { return scale; }
+ }
+
//where an animcontroller is trying to pull the limb, only used for debug visualization
public Vector2 AnimTargetPos
{
@@ -135,55 +184,23 @@ namespace Barotrauma
{
get { return stepOffset; }
}
-
+
public float Burnt
{
get { return burnt; }
set { burnt = MathHelper.Clamp(value,0.0f,100.0f); }
}
-
- private float scale;
-
- public float AttackTimer;
-
- //public float Damage
- //{
- // get { return damage; }
- // set
- // {
- // damage = Math.Max(value, 0.0f);
- // if (damage >=maxHealth) Character.Kill();
- // }
- //}
-
- //public float MaxHealth
- //{
- // get { return maxHealth; }
- //}
-
- //public float Bleeding
- //{
- // get { return bleeding; }
- // set { bleeding = MathHelper.Clamp(value, 0.0f, 100.0f); }
- //}
-
+
public List WearingItems
{
get { return wearingItems; }
- set { wearingItems = value; }
}
-
- //public WearableSprite WearingItemSprite
- //{
- // get { return wearingItemSprite; }
- // set { wearingItemSprite = value; }
- //}
-
+
public Limb (Character character, XElement element, float scale = 1.0f)
{
this.character = character;
- WearingItems = new List();
+ wearingItems = new List();
dir = Direction.Right;
@@ -255,7 +272,12 @@ namespace Barotrauma
armorSector.Y = MathHelper.ToRadians(armorSector.Y);
armorValue = Math.Max(ToolBox.GetAttributeFloat(element, "armor", 0.0f), 0.0f);
-
+
+ if (element.Attribute("mouthpos") != null)
+ {
+ MouthPos = ConvertUnits.ToSimUnits(ToolBox.GetAttributeVector2(element, "mouthpos", Vector2.Zero));
+ }
+
body.BodyType = BodyType.Dynamic;
body.FarseerBody.AngularDamping = LimbAngularDamping;
@@ -340,7 +362,7 @@ namespace Barotrauma
bool hitArmor = false;
float totalArmorValue = 0.0f;
- if (armorValue>0.0f && SectorHit(armorSector, position))
+ if (armorValue > 0.0f && SectorHit(armorSector, position))
{
hitArmor = true;
totalArmorValue += armorValue;
@@ -354,8 +376,7 @@ namespace Barotrauma
hitArmor = true;
totalArmorValue += wearable.WearableComponent.ArmorValue;
}
- }
-
+ }
if (hitArmor)
{
@@ -371,28 +392,19 @@ namespace Barotrauma
SoundPlayer.PlayDamageSound(damageSoundType, amount, position);
}
- //Bleeding += bleedingAmount;
- //Damage += amount;
+ float bloodParticleAmount = hitArmor || bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
+ float bloodParticleSize = MathHelper.Clamp(amount / 50.0f, 0.1f, 1.0f);
- float bloodAmount = hitArmor || bleedingAmount<=0.0f ? 0 : (int)Math.Min((int)(amount * 2.0f), 20);
+ for (int i = 0; i < bloodParticleAmount; i++)
+ {
+ var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
+ if (blood != null)
+ {
+ blood.Size *= bloodParticleSize;
+ }
+ }
- for (int i = 0; i < bloodAmount; i++)
- {
- Vector2 particleVel = SimPosition - position;
- if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel);
-
- GameMain.ParticleManager.CreateParticle("blood",
- WorldPosition,
- particleVel * Rand.Range(100.0f, 300.0f), 0.0f, character.AnimController.CurrentHull);
- }
-
- for (int i = 0; i < bloodAmount / 2; i++)
- {
- GameMain.ParticleManager.CreateParticle("waterblood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
- }
-
- damage += Math.Max(amount,bleedingAmount) / character.MaxHealth * 100.0f;
-
+ damage += Math.Max(amount, bleedingAmount) / character.MaxHealth * 100.0f;
return new AttackResult(amount, bleedingAmount, hitArmor);
}
@@ -418,9 +430,7 @@ namespace Barotrauma
{
LightSource.ParentSub = body.Submarine;
}
-
- if (!character.IsDead) damage = Math.Max(0.0f, damage-deltaTime*0.1f);
-
+
if (burnt > 0.0f) Burnt -= deltaTime;
if (LinearVelocity.X > 500.0f)
@@ -435,9 +445,19 @@ namespace Barotrauma
body.ApplyWaterForces();
}
+ if (isSevered)
+ {
+ severedFadeOutTimer += deltaTime;
+ if (severedFadeOutTimer > SeveredFadeOutTime)
+ {
+ body.Enabled = false;
+ }
+ }
+
if (character.IsDead) return;
- soundTimer -= deltaTime;
+ damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
+ SoundTimer -= deltaTime;
//if (MathUtils.RandomFloat(0.0f, 1000.0f) < Bleeding)
//{
@@ -446,12 +466,7 @@ namespace Barotrauma
// SimPosition, Vector2.Zero);
//}
}
-
- public void ActivateDamagedSprite()
- {
- damage = 100.0f;
- }
-
+
public void UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget)
{
float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
@@ -464,19 +479,34 @@ namespace Barotrauma
{
if (AttackTimer >= attack.Duration && damageTarget != null)
{
- attack.DoDamage(character, damageTarget, WorldPosition, 1.0f, (soundTimer <= 0.0f));
+ attack.DoDamage(character, damageTarget, WorldPosition, 1.0f, (SoundTimer <= 0.0f));
- soundTimer = Limb.SoundInterval;
+ SoundTimer = SoundInterval;
}
}
Vector2 diff = attackPosition - SimPosition;
- if (diff.LengthSquared() > 0.00001f)
+ if (diff.LengthSquared() < 0.00001f) return;
+
+ if (attack.ApplyForceOnLimbs != null)
{
- Vector2 pos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
- body.ApplyLinearImpulse(Mass * attack.Force *
- Vector2.Normalize(attackPosition - SimPosition), pos);
+ foreach (int limbIndex in attack.ApplyForceOnLimbs)
+ {
+ if (limbIndex < 0 || limbIndex >= character.AnimController.Limbs.Length) continue;
+
+ Limb limb = character.AnimController.Limbs[limbIndex];
+ Vector2 forcePos = limb.pullJoint == null ? limb.body.SimPosition : limb.pullJoint.WorldAnchorA;
+ limb.body.ApplyLinearImpulse(
+ limb.Mass * attack.Force * Vector2.Normalize(attackPosition - SimPosition), forcePos);
+ }
}
+ else
+ {
+ Vector2 forcePos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
+ body.ApplyLinearImpulse(Mass * attack.Force *
+ Vector2.Normalize(attackPosition - SimPosition), forcePos);
+ }
+
}
public void Draw(SpriteBatch spriteBatch)
@@ -484,10 +514,21 @@ namespace Barotrauma
float brightness = 1.0f - (burnt / 100.0f) * 0.5f;
Color color = new Color(brightness, brightness, brightness);
+ if (isSevered)
+ {
+ if (severedFadeOutTimer > SeveredFadeOutTime)
+ {
+ return;
+ }
+ else if (severedFadeOutTimer > SeveredFadeOutTime - 1.0f)
+ {
+ color *= SeveredFadeOutTime - severedFadeOutTimer;
+ }
+ }
+
body.Dir = Dir;
bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb);
-
if (!hideLimb)
{
body.Draw(spriteBatch, sprite, color, null, scale);
diff --git a/Barotrauma/Source/Events/MonsterEvent.cs b/Barotrauma/Source/Events/MonsterEvent.cs
index bdc894828..9bc7a5cdd 100644
--- a/Barotrauma/Source/Events/MonsterEvent.cs
+++ b/Barotrauma/Source/Events/MonsterEvent.cs
@@ -17,6 +17,8 @@ namespace Barotrauma
private bool spawnDeep;
private bool disallowed;
+
+ private bool repeat;
private Level.PositionType spawnPosType;
@@ -39,7 +41,9 @@ namespace Barotrauma
{
characterFile = ToolBox.GetAttributeString(element, "characterfile", "");
- minAmount = ToolBox.GetAttributeInt(element, "minamount", 1);
+ int defaultAmount = ToolBox.GetAttributeInt(element, "amount", 1);
+
+ minAmount = ToolBox.GetAttributeInt(element, "minamount", defaultAmount);
maxAmount = Math.Max(ToolBox.GetAttributeInt(element, "maxamount", 1), minAmount);
var spawnPosTypeStr = ToolBox.GetAttributeString(element, "spawntype", "");
@@ -52,6 +56,8 @@ namespace Barotrauma
spawnDeep = ToolBox.GetAttributeBool(element, "spawndeep", false);
+ repeat = ToolBox.GetAttributeBool(element, "repeat", repeat);
+
if (GameMain.NetworkMember != null)
{
List monsterNames = GameMain.NetworkMember.monsterEnabled.Keys.ToList();
@@ -67,18 +73,17 @@ namespace Barotrauma
{
base.Init();
- SpawnMonsters();
+ SpawnMonsters(Rand.Range(minAmount, maxAmount, false));
}
- private void SpawnMonsters()
+ private Character[] SpawnMonsters(int amount)
{
- if (disallowed) return;
+ if (disallowed) return null;
Vector2 spawnPos = Level.Loaded.GetRandomInterestingPosition(true, spawnPosType, true);
- int amount = Rand.Range(minAmount, maxAmount, false);
- monsters = new Character[amount];
+ var monsters = new Character[amount];
if (spawnDeep) spawnPos.Y -= Level.Loaded.Size.Y;
@@ -88,6 +93,8 @@ namespace Barotrauma
spawnPos.Y += Rand.Range(-0.5f, 0.5f, false);
monsters[i] = Character.Create(characterFile, spawnPos, null, GameMain.Client != null);
}
+
+ return monsters;
}
public override void Update(float deltaTime)
@@ -97,7 +104,29 @@ namespace Barotrauma
Finished();
return;
}
- if (monsters == null) SpawnMonsters();
+
+ if (monsters == null)
+ {
+ monsters = SpawnMonsters(Rand.Range(minAmount, maxAmount, false));
+ return;
+ }
+
+ if (repeat)
+ {
+ //clients aren't allowed to spawn more monsters mid-round
+ if (GameMain.Client != null)
+ {
+ return;
+ }
+
+ for (int i = 0; i < monsters.Length; i++)
+ {
+ if (monsters[i] == null || monsters[i].Removed || monsters[i].IsDead)
+ {
+ monsters[i] = SpawnMonsters(1)[0];
+ }
+ }
+ }
if (isFinished) return;
@@ -128,7 +157,7 @@ namespace Barotrauma
}
}
- if (monstersDead) Finished();
+ if (monstersDead && !repeat) Finished();
}
}
}
diff --git a/Barotrauma/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs b/Barotrauma/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs
index fbd40ca05..0a391d901 100644
--- a/Barotrauma/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs
+++ b/Barotrauma/Source/GameSession/GameModes/Tutorials/BasicTutorial.cs
@@ -655,7 +655,7 @@ namespace Barotrauma.Tutorials
enemy.Health = 50.0f;
- enemy.AIController.State = AIController.AiState.None;
+ enemy.AIController.State = AIController.AIState.None;
Vector2 targetPos = Character.Controlled.WorldPosition + new Vector2(0.0f, 3000.0f);
diff --git a/Barotrauma/Source/Items/Components/Projectile.cs b/Barotrauma/Source/Items/Components/Projectile.cs
index c525bb5a1..999c35bb9 100644
--- a/Barotrauma/Source/Items/Components/Projectile.cs
+++ b/Barotrauma/Source/Items/Components/Projectile.cs
@@ -60,6 +60,8 @@ namespace Barotrauma.Items.Components
{
if (character != null && !characterUsable) return false;
+ User = character;
+
Launch(new Vector2(
(float)Math.Cos(item.body.Rotation),
(float)Math.Sin(item.body.Rotation)) * launchImpulse * item.body.Mass);
@@ -147,7 +149,7 @@ namespace Barotrauma.Items.Components
return false;
}
- AttackResult attackResult = new AttackResult(0.0f, 0.0f);
+ AttackResult attackResult = new AttackResult();
if (attack != null)
{
var submarine = f2.Body.UserData as Submarine;
diff --git a/Barotrauma/Source/Items/Components/Turret.cs b/Barotrauma/Source/Items/Components/Turret.cs
index c97085f14..5297de04e 100644
--- a/Barotrauma/Source/Items/Components/Turret.cs
+++ b/Barotrauma/Source/Items/Components/Turret.cs
@@ -190,7 +190,7 @@ namespace Barotrauma.Items.Components
}
}
- Launch(projectiles[0].Item);
+ Launch(projectiles[0].Item, character);
if (character != null)
{
@@ -200,7 +200,7 @@ namespace Barotrauma.Items.Components
return true;
}
- private void Launch(Item projectile)
+ private void Launch(Item projectile, Character user = null)
{
reload = reloadTime;
@@ -217,6 +217,7 @@ namespace Barotrauma.Items.Components
if (projectileComponent != null)
{
projectileComponent.Use((float)Timing.Step);
+ projectileComponent.User = user;
}
if (projectile.Container != null) projectile.Container.RemoveContained(projectile);
diff --git a/Barotrauma/Source/Map/Explosion.cs b/Barotrauma/Source/Map/Explosion.cs
index 85705f051..cb88f3a08 100644
--- a/Barotrauma/Source/Map/Explosion.cs
+++ b/Barotrauma/Source/Map/Explosion.cs
@@ -90,7 +90,7 @@ namespace Barotrauma
if (force == 0.0f && attack.Stun == 0.0f && attack.GetDamage(1.0f) == 0.0f) return;
- ApplyExplosionForces(worldPosition, attack.Range, force, attack.GetDamage(1.0f), attack.Stun);
+ ApplyExplosionForces(worldPosition, attack, force);
if (flames && GameMain.Client == null)
{
@@ -141,9 +141,9 @@ namespace Barotrauma
yield return CoroutineStatus.Success;
}
- public static void ApplyExplosionForces(Vector2 worldPosition, float range, float force, float damage = 0.0f, float stun = 0.0f)
+ public static void ApplyExplosionForces(Vector2 worldPosition, Attack attack, float force)
{
- if (range <= 0.0f) return;
+ if (attack.Range <= 0.0f) return;
foreach (Character c in Character.CharacterList)
{
@@ -152,6 +152,7 @@ namespace Barotrauma
explosionPos = ConvertUnits.ToSimUnits(explosionPos);
+ bool wasDead = c.IsDead;
foreach (Limb limb in c.AnimController.Limbs)
{
float dist = Vector2.Distance(limb.WorldPosition, worldPosition);
@@ -160,24 +161,36 @@ namespace Barotrauma
//doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
dist = Math.Max(0.0f, dist - FarseerPhysics.ConvertUnits.ToDisplayUnits(limbRadius));
+
+ if (dist > attack.Range) continue;
- if (dist > range) continue;
-
- float distFactor = 1.0f - dist / range;
+ float distFactor = 1.0f - dist / attack.Range;
//solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null) distFactor *= 0.1f;
c.AddDamage(limb.WorldPosition, DamageType.None,
- damage / c.AnimController.Limbs.Length * distFactor, 0.0f, stun * distFactor, false);
+ attack.GetDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
+ attack.GetBleedingDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
+ attack.Stun * distFactor,
+ false);
- if (limb.WorldPosition == worldPosition) continue;
-
- if (force > 0.0f)
+ if (limb.WorldPosition != worldPosition && force > 0.0f)
{
limb.body.ApplyLinearImpulse(Vector2.Normalize(limb.WorldPosition - worldPosition) * distFactor * force);
}
- }
+ }
+
+ if (!wasDead && c.IsDead)
+ {
+ foreach (LimbJoint joint in c.AnimController.LimbJoints)
+ {
+ if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability)
+ {
+ c.AnimController.SeverLimbJoint(joint);
+ }
+ }
+ }
}
}
diff --git a/Barotrauma/Source/Networking/EntitySpawner.cs b/Barotrauma/Source/Networking/EntitySpawner.cs
index 229fe6ab8..17a32b977 100644
--- a/Barotrauma/Source/Networking/EntitySpawner.cs
+++ b/Barotrauma/Source/Networking/EntitySpawner.cs
@@ -109,6 +109,7 @@ namespace Barotrauma
public void AddToRemoveQueue(Entity entity)
{
if (GameMain.Client != null) return;
+ if (removeQueue.Contains(entity) || entity.Removed) return;
removeQueue.Enqueue(entity);
}
@@ -116,6 +117,7 @@ namespace Barotrauma
public void AddToRemoveQueue(Item item)
{
if (GameMain.Client != null) return;
+ if (removeQueue.Contains(item) || item.Removed) return;
removeQueue.Enqueue(item);
if (item.ContainedItems == null) return;
diff --git a/Barotrauma/Source/Screens/EditCharacterScreen.cs b/Barotrauma/Source/Screens/EditCharacterScreen.cs
index 5c70fbd7b..6cae14af2 100644
--- a/Barotrauma/Source/Screens/EditCharacterScreen.cs
+++ b/Barotrauma/Source/Screens/EditCharacterScreen.cs
@@ -217,7 +217,7 @@ namespace Barotrauma
}
jointList.ClearChildren();
- foreach (RevoluteJoint joint in character.AnimController.limbJoints)
+ foreach (RevoluteJoint joint in character.AnimController.LimbJoints)
{
Limb limb1 = (Limb)(joint.BodyA.UserData);
Limb limb2 = (Limb)(joint.BodyB.UserData);
@@ -236,7 +236,7 @@ namespace Barotrauma
private void DrawJoints(SpriteBatch spriteBatch, Limb limb, Vector2 limbBodyPos)
{
- foreach (var joint in editingCharacter.AnimController.limbJoints)
+ foreach (var joint in editingCharacter.AnimController.LimbJoints)
{
Vector2 jointPos = Vector2.Zero;
@@ -253,24 +253,27 @@ namespace Barotrauma
{
continue;
}
-
- jointPos.Y = -jointPos.Y;
- jointPos += limbBodyPos;
+
+ Vector2 tformedJointPos = jointPos /= limb.Scale;
+ tformedJointPos.Y = -tformedJointPos.Y;
+ tformedJointPos += limbBodyPos;
+
if (joint.BodyA == limb.body.FarseerBody)
{
float a1 = joint.UpperLimit - MathHelper.PiOver2;
float a2 = joint.LowerLimit - MathHelper.PiOver2;
- float a3 =( a1+a2)/2.0f;
- GUI.DrawLine(spriteBatch, jointPos, jointPos + new Vector2((float)Math.Cos(a1), -(float)Math.Sin(a1)) * 30.0f, Color.Green);
- GUI.DrawLine(spriteBatch, jointPos, jointPos + new Vector2((float)Math.Cos(a2), -(float)Math.Sin(a2)) * 30.0f, Color.DarkGreen);
+ float a3 = (a1 + a2) / 2.0f;
+ GUI.DrawLine(spriteBatch, tformedJointPos, tformedJointPos + new Vector2((float)Math.Cos(a1), -(float)Math.Sin(a1)) * 30.0f, Color.Green);
+ GUI.DrawLine(spriteBatch, tformedJointPos, tformedJointPos + new Vector2((float)Math.Cos(a2), -(float)Math.Sin(a2)) * 30.0f, Color.DarkGreen);
- GUI.DrawLine(spriteBatch, jointPos, jointPos + new Vector2((float)Math.Cos(a3), -(float)Math.Sin(a3)) * 30.0f, Color.LightGray);
+ GUI.DrawLine(spriteBatch, tformedJointPos, tformedJointPos + new Vector2((float)Math.Cos(a3), -(float)Math.Sin(a3)) * 30.0f, Color.LightGray);
}
- GUI.DrawRectangle(spriteBatch, jointPos, new Vector2(5.0f, 5.0f), Color.Red, true);
- if (Vector2.Distance(PlayerInput.MousePosition, jointPos) < 6.0f)
+ GUI.DrawRectangle(spriteBatch, tformedJointPos, new Vector2(5.0f, 5.0f), Color.Red, true);
+ if (Vector2.Distance(PlayerInput.MousePosition, tformedJointPos) < 10.0f)
{
- GUI.DrawRectangle(spriteBatch, jointPos - new Vector2(3.0f, 3.0f), new Vector2(11.0f, 11.0f), Color.Red, false);
+ GUI.DrawString(spriteBatch, tformedJointPos + Vector2.One*10.0f, jointPos.ToString(), Color.White, Color.Black * 0.5f);
+ GUI.DrawRectangle(spriteBatch, tformedJointPos - new Vector2(3.0f, 3.0f), new Vector2(11.0f, 11.0f), Color.Red, false);
if (PlayerInput.LeftButtonHeld())
{
Vector2 speed = ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed);
diff --git a/Barotrauma/Source/Utils/ToolBox.cs b/Barotrauma/Source/Utils/ToolBox.cs
index bbf2aa6c5..c75c35cc2 100644
--- a/Barotrauma/Source/Utils/ToolBox.cs
+++ b/Barotrauma/Source/Utils/ToolBox.cs
@@ -162,6 +162,35 @@ namespace Barotrauma
return value;
}
+ public static float GetAttributeFloat(XElement element, float defaultValue, params string[] matchingAttributeName)
+ {
+ if (element == null) return defaultValue;
+
+ foreach (string name in matchingAttributeName)
+ {
+ if (element.Attribute(name) == null) continue;
+
+ float val = defaultValue;
+
+ try
+ {
+ if (!float.TryParse(element.Attribute(name).Value, NumberStyles.Float, CultureInfo.InvariantCulture, out val))
+ {
+ continue;
+ }
+ }
+ catch (Exception e)
+ {
+ DebugConsole.ThrowError("Error in "+element+"!", e);
+ continue;
+ }
+
+ return val;
+ }
+
+ return defaultValue;
+ }
+
public static float GetAttributeFloat(XElement element, string name, float defaultValue)
{
if (element == null || element.Attribute(name) == null) return defaultValue;