(6eeea9b7c) v0.9.10.0.0

This commit is contained in:
Joonas Rikkonen
2020-06-04 16:41:07 +03:00
parent ce4ccd99ac
commit eeac247a8e
366 changed files with 7772 additions and 3692 deletions
@@ -76,7 +76,8 @@ namespace Barotrauma
{
if (InWater || !CanWalk)
{
return TargetMovement.Length() > (SwimSlowParams.MovementSpeed + SwimFastParams.MovementSpeed) / 2.0f;
float avg = (SwimSlowParams.MovementSpeed + SwimFastParams.MovementSpeed) / 2.0f;
return TargetMovement.LengthSquared() > avg * avg;
}
else
{
@@ -213,7 +213,12 @@ namespace Barotrauma
UpdateWalkAnim(deltaTime);
}
//don't flip or drag when simply physics is enabled
if (character.SelectedCharacter != null)
{
DragCharacter(character.SelectedCharacter, deltaTime);
}
//don't flip when simply physics is enabled
if (SimplePhysicsEnabled) { return; }
if (!character.IsRemotePlayer && (character.AIController == null || character.AIController.CanFlip))
@@ -248,29 +253,33 @@ namespace Barotrauma
}
}
if (character.SelectedCharacter != null)
{
DragCharacter(character.SelectedCharacter, deltaTime);
}
if (!CurrentFishAnimation.Flip) { return; }
if (IsStuck) { return; }
if (character.AIController != null && !character.AIController.CanFlip) { return; }
flipCooldown -= deltaTime;
if (TargetDir != Direction.None && TargetDir != dir)
if (TargetDir != Direction.None && TargetDir != dir)
{
flipTimer += deltaTime;
if ((flipTimer > 0.5f && flipCooldown <= 0.0f) || character.IsRemotePlayer)
// Speed reductions are not taken into account here. It's intentional: an ai character cannot flip if it's heavily paralyzed (for example).
float requiredSpeed = CurrentAnimationParams.MovementSpeed / 2;
if (CurrentHull != null)
{
// Enemy movement speeds are halved inside submarines
requiredSpeed /= 2;
}
bool isMovingFastEnough = Math.Abs(MainLimb.LinearVelocity.X) > requiredSpeed;
bool isTryingToMoveHorizontally = Math.Abs(TargetMovement.X) > Math.Abs(TargetMovement.Y);
if ((flipTimer > CurrentFishAnimation.FlipDelay && flipCooldown <= 0.0f && ((isMovingFastEnough && isTryingToMoveHorizontally) || IsMovingBackwards))
|| character.IsRemotePlayer)
{
Flip();
if (!inWater || (CurrentSwimParams != null && CurrentSwimParams.Mirror))
{
Mirror();
Mirror(CurrentSwimParams != null ? CurrentSwimParams.MirrorLerp : true);
}
flipTimer = 0.0f;
flipCooldown = 1.0f;
flipCooldown = CurrentFishAnimation.FlipCooldown;
}
}
else
@@ -295,7 +304,7 @@ namespace Barotrauma
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
//stop dragging if there's something between the pull limb and the target
Vector2 sourceSimPos = mouthLimb.SimPosition;
Vector2 sourceSimPos = SimplePhysicsEnabled ? character.SimPosition : mouthLimb.SimPosition;
Vector2 targetSimPos = target.SimPosition;
if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
{
@@ -317,7 +326,7 @@ namespace Barotrauma
float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.Mass, 1)) * 10);
eatTimer += deltaTime * eatSpeed;
Vector2 mouthPos = GetMouthPosition().Value;
Vector2 mouthPos = SimplePhysicsEnabled ? character.SimPosition : GetMouthPosition().Value;
Vector2 attackSimPosition = character.Submarine == null ? ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;
Vector2 limbDiff = attackSimPosition - mouthPos;
@@ -525,6 +534,7 @@ namespace Barotrauma
foreach (var limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (Math.Abs(limb.Params.ConstantTorque) > 0)
{
limb.body.SmoothRotate(movementAngle + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Params.ConstantTorque, wrapAngle: true);
@@ -550,10 +560,12 @@ namespace Barotrauma
for (int i = 0; i < Limbs.Length; i++)
{
if (Limbs[i].SteerForce <= 0.0f) { continue; }
var limb = Limbs[i];
if (limb.IsSevered) { continue; }
if (limb.SteerForce <= 0.0f) { continue; }
if (!Collider.PhysEnabled) { continue; }
Vector2 pullPos = Limbs[i].PullJointWorldAnchorA;
Limbs[i].body.ApplyForce(movement * Limbs[i].SteerForce * Limbs[i].Mass * Math.Max(character.SpeedMultiplier, 1), pullPos);
Vector2 pullPos = limb.PullJointWorldAnchorA;
limb.body.ApplyForce(movement * limb.SteerForce * limb.Mass * Math.Max(character.SpeedMultiplier, 1), pullPos);
}
Vector2 mainLimbDiff = mainLimb.PullJointWorldAnchorB - mainLimb.SimPosition;
@@ -604,6 +616,14 @@ namespace Barotrauma
float stepLift = TargetMovement.X == 0.0f ? 0 :
(float)Math.Sin(WalkPos * CurrentGroundedParams.StepLiftFrequency + MathHelper.Pi * CurrentGroundedParams.StepLiftOffset) * (CurrentGroundedParams.StepLiftAmount / 100);
float limpAmount = character.GetLegPenalty();
if (limpAmount > 0)
{
float walkPosX = (float)Math.Cos(WalkPos);
//make the footpos oscillate when limping
limpAmount = Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f) * Dir;
}
Limb torso = GetLimb(LimbType.Torso);
if (torso != null)
{
@@ -613,7 +633,7 @@ namespace Barotrauma
}
if (TorsoPosition.HasValue)
{
Vector2 pos = colliderBottom + new Vector2(0, TorsoPosition.Value + stepLift);
Vector2 pos = colliderBottom + new Vector2(limpAmount, TorsoPosition.Value + stepLift);
if (torso != mainLimb)
{
@@ -635,7 +655,7 @@ namespace Barotrauma
}
if (HeadPosition.HasValue)
{
Vector2 pos = colliderBottom + new Vector2(0, HeadPosition.Value + stepLift * CurrentGroundedParams.StepLiftHeadMultiplier);
Vector2 pos = colliderBottom + new Vector2(limpAmount, HeadPosition.Value + stepLift * CurrentGroundedParams.StepLiftHeadMultiplier);
if (head != mainLimb)
{
@@ -670,6 +690,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (Math.Abs(limb.Params.ConstantTorque) > 0)
{
limb.body.SmoothRotate(movementAngle + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Params.ConstantTorque, wrapAngle: true);
@@ -766,6 +787,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
#if CLIENT
if (limb.LightSource != null)
{
@@ -821,6 +843,7 @@ namespace Barotrauma
base.Flip();
foreach (Limb l in Limbs)
{
if (l.IsSevered) { continue; }
if (!l.DoesFlip) { continue; }
if (RagdollParams.IsSpritesheetOrientationHorizontal)
{
@@ -838,10 +861,13 @@ namespace Barotrauma
foreach (Limb l in Limbs)
{
if (l.IsSevered) { continue; }
TrySetLimbPosition(l,
centerOfMass,
new Vector2(centerOfMass.X - (l.SimPosition.X - centerOfMass.X), l.SimPosition.Y),
lerp);
l.body.PositionSmoothingFactor = 0.8f;
if (!l.DoesFlip) { continue; }
@@ -862,7 +888,7 @@ namespace Barotrauma
if (diff < 100.0f)
{
character.SelectedCharacter.AnimController.SetPosition(
new Vector2(centerOfMass.X - diff, character.SelectedCharacter.SimPosition.Y), lerp: true);
new Vector2(centerOfMass.X - diff, character.SelectedCharacter.SimPosition.Y), lerp);
}
}
}
@@ -323,7 +323,14 @@ namespace Barotrauma
levitatingCollider = true;
ColliderIndex = Crouching ? 1 : 0;
if (!Crouching && ColliderIndex == 1) Crouching = true;
if (character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false)
{
Crouching = false;
}
else if (!Crouching && ColliderIndex == 1)
{
Crouching = true;
}
//stun (= disable the animations) if the ragdoll receives a large enough impact
if (strongestImpact > 0.0f)
@@ -542,12 +549,6 @@ namespace Barotrauma
Limb leftLeg = GetLimb(LimbType.LeftLeg);
Limb rightLeg = GetLimb(LimbType.RightLeg);
float limpAmount =
character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) +
character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true) +
character.CharacterHealth.GetAfflictionStrength("spaceherpes");
limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f);
float walkCycleMultiplier = 1.0f;
if (Stairs != null)
{
@@ -582,6 +583,11 @@ namespace Barotrauma
stepSize.Y *= walkPosY;
float footMid = colliderPos.X;
var herpes = character.CharacterHealth.GetAffliction("spaceherpes", false);
float herpesAmount = herpes == null ? 0 : herpes.Strength / herpes.Prefab.MaxStrength;
float legDamage = character.GetLegPenalty(startSum: -0.1f) * 1.1f;
float limpAmount = MathHelper.Lerp(0, 1, legDamage + herpesAmount);
if (limpAmount > 0.0f)
{
//make the footpos oscillate when limping
@@ -652,6 +658,7 @@ namespace Barotrauma
(float)Math.Sin(WalkPos * CurrentGroundedParams.StepLiftFrequency + MathHelper.Pi * CurrentGroundedParams.StepLiftOffset) * (CurrentGroundedParams.StepLiftAmount / 100);
float y = colliderPos.Y + stepLift;
if (TorsoPosition.HasValue)
{
y += TorsoPosition.Value;
@@ -690,6 +697,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
MoveLimb(limb, limb.SimPosition + move, 15.0f, true);
}
@@ -947,8 +955,6 @@ namespace Barotrauma
torso.body.MoveToPos(Collider.SimPosition + new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation)) * 0.4f, 5.0f);
if (TargetMovement == Vector2.Zero) { return; }
movement = MathUtils.SmoothStep(movement, TargetMovement, 0.3f);
if (TorsoAngle.HasValue)
@@ -1001,29 +1007,31 @@ namespace Barotrauma
}
WalkPos += movement.Length();
legCyclePos += Vector2.Normalize(movement).Length();
legCyclePos += Math.Min(movement.LengthSquared() + Collider.AngularVelocity, 1.0f);
handCyclePos += MathHelper.ToRadians(CurrentSwimParams.HandCycleSpeed) * Math.Sign(movement.X);
var waist = GetLimb(LimbType.Waist);
footPos = waist == null ? Vector2.Zero : waist.SimPosition - new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation)) * (upperLegLength + lowerLegLength);
Vector2 transformedFootPos = new Vector2((float)Math.Sin(legCyclePos / CurrentSwimParams.LegCycleLength / character.SpeedMultiplier) * CurrentSwimParams.LegMoveAmount, 0.0f);
Vector2 transformedFootPos = new Vector2((float)Math.Sin(legCyclePos / CurrentSwimParams.LegCycleLength) * CurrentSwimParams.LegMoveAmount, 0.0f);
transformedFootPos = Vector2.Transform(transformedFootPos, Matrix.CreateRotationZ(Collider.Rotation));
float torque = CurrentSwimParams.FootRotateStrength * character.SpeedMultiplier * (1.2f - character.GetLegPenalty());
if (rightFoot != null && !rightFoot.Disabled)
{
FootIK(rightFoot, footPos - transformedFootPos, CurrentSwimParams.FootRotateStrength, CurrentSwimParams.FootRotateStrength, CurrentSwimParams.FootAngleInRadians);
FootIK(rightFoot, footPos - transformedFootPos, torque, torque, CurrentSwimParams.FootAngleInRadians);
}
if (leftFoot != null && !leftFoot.Disabled)
{
FootIK(leftFoot, footPos + transformedFootPos, CurrentSwimParams.FootRotateStrength, CurrentSwimParams.FootRotateStrength, CurrentSwimParams.FootAngleInRadians);
FootIK(leftFoot, footPos + transformedFootPos, torque, torque, CurrentSwimParams.FootAngleInRadians);
}
handPos = (torso.SimPosition + head.SimPosition) / 2.0f;
//at the surface, not moving sideways -> hands just float around
if (!headInWater && TargetMovement.X == 0.0f && TargetMovement.Y > 0)
//at the surface, not moving sideways OR not moving at all
// -> hands just float around
if ((!headInWater && TargetMovement.X == 0.0f && TargetMovement.Y > 0) || TargetMovement.LengthSquared() < 0.001f)
{
handPos.X = handPos.X + Dir * 0.6f;
handPos += MathUtils.RotatePoint(Vector2.UnitX * Dir * 0.6f, torso.Rotation);
float wobbleAmount = 0.1f;
@@ -1060,7 +1068,7 @@ namespace Barotrauma
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.HandMoveStrength * character.SpeedMultiplier);
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.HandMoveStrength * character.SpeedMultiplier * (1 - Character.GetRightHandPenalty()));
}
if (leftHand != null && !leftHand.Disabled)
@@ -1069,7 +1077,7 @@ namespace Barotrauma
leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.HandMoveStrength * character.SpeedMultiplier);
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.HandMoveStrength * character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty()));
}
}
@@ -1251,31 +1259,39 @@ namespace Barotrauma
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
//if the head is moving, try to protect it with the hands
if (head.LinearVelocity.LengthSquared() > 1.0f && !head.IsSevered)
if (head != null && head.LinearVelocity.LengthSquared() > 1.0f && !head.IsSevered)
{
//if the head is moving, try to protect it with the hands
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
//move hands in front of the head in the direction of the movement
Vector2 protectPos = head.SimPosition + Vector2.Normalize(head.LinearVelocity);
if (!rightHand.IsSevered) HandIK(rightHand, protectPos, strength * 0.1f);
if (!leftHand.IsSevered) HandIK(leftHand, protectPos, strength * 0.1f);
if (rightHand != null && !rightHand.IsSevered)
{
HandIK(rightHand, protectPos, strength * 0.1f);
}
if (leftHand != null && !leftHand.IsSevered)
{
HandIK(leftHand, protectPos, strength * 0.1f);
}
}
if (torso == null) { return; }
//attempt to make legs stay in a straight line with the torso to prevent the character from doing a split
for (int i = 0; i < 2; i++)
{
var thigh = i == 0 ? GetLimb(LimbType.LeftThigh) : GetLimb(LimbType.RightThigh);
if (thigh.IsSevered) continue;
if (thigh == null) { continue; }
if (thigh.IsSevered) { continue; }
float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
float thighTorque = thighDiff * thigh.Mass * Math.Sign(torso.Rotation - thigh.Rotation) * 5.0f;
thigh.body.ApplyTorque(thighTorque * strength);
var leg = i == 0 ? GetLimb(LimbType.LeftLeg) : GetLimb(LimbType.RightLeg);
if (leg.IsSevered) continue;
if (leg == null || leg.IsSevered) { continue; }
float legDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, leg.Rotation));
float legTorque = legDiff * leg.Mass * Math.Sign(torso.Rotation - leg.Rotation) * 5.0f;
leg.body.ApplyTorque(legTorque * strength);
@@ -1697,7 +1713,7 @@ namespace Barotrauma
if (holdable.ControlPose)
{
head.body.SmoothRotate(itemAngle);
head?.body.SmoothRotate(itemAngle);
if (TargetMovement == Vector2.Zero && inWater)
{
@@ -1719,13 +1735,13 @@ namespace Barotrauma
{
if (character.SelectedItems[0] == item)
{
if (rightHand.IsSevered) return;
if (rightHand == null || rightHand.IsSevered) { return; }
transformedHoldPos = rightHand.PullJointWorldAnchorA - transformedHandlePos[0];
itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
}
else if (character.SelectedItems[1] == item)
{
if (leftHand.IsSevered) return;
if (leftHand == null || leftHand.IsSevered) { return; }
transformedHoldPos = leftHand.PullJointWorldAnchorA - transformedHandlePos[1];
itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
}
@@ -1734,12 +1750,12 @@ namespace Barotrauma
{
if (character.SelectedItems[0] == item)
{
if (rightHand.IsSevered) return;
if (rightHand == null || rightHand.IsSevered) { return; }
rightHand.Disabled = true;
}
if (character.SelectedItems[1] == item)
{
if (leftHand.IsSevered) return;
if (leftHand == null || leftHand.IsSevered) { return; }
leftHand.Disabled = true;
}
@@ -1797,17 +1813,14 @@ namespace Barotrauma
}
}
item.SetTransform(currItemPos, itemAngle + itemAngleRelativeToHoldAngle * Dir, setPrevTransform: false);
item.SetTransform(currItemPos, itemAngle + itemAngleRelativeToHoldAngle * Dir, setPrevTransform: false);
if (!isClimbing)
if (!isClimbing && !character.IsIncapacitated)
{
for (int i = 0; i < 2; i++)
{
if (character.SelectedItems[i] != item) continue;
if (itemPos == Vector2.Zero) continue;
if (character.SelectedItems[i] != item || itemPos == Vector2.Zero) { continue; }
Limb hand = (i == 0) ? rightHand : leftHand;
HandIK(hand, transformedHoldPos + transformedHandlePos[i]);
}
}
@@ -1987,6 +2000,8 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
bool mirror = false;
bool flipAngle = false;
bool wrapAngle = false;
@@ -47,7 +47,9 @@ namespace Barotrauma
private readonly Queue<Impact> impactQueue = new Queue<Impact>();
protected Hull currentHull;
private bool accessRemovedCharacterErrorShown;
private Limb[] limbs;
public Limb[] Limbs
{
@@ -55,16 +57,17 @@ namespace Barotrauma
{
if (limbs == null)
{
string errorMsg = "Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this);
#if DEBUG || UNSTABLE
errorMsg += '\n' + Environment.StackTrace;
#endif
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce(
"Ragdoll.Limbs:AccessRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this) + "\n" + Environment.StackTrace);
if (!accessRemovedCharacterErrorShown)
{
string errorMsg = "Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this);
errorMsg += '\n' + Environment.StackTrace;
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce(
"Ragdoll.Limbs:AccessRemoved",
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Attempted to access a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this) + "\n" + Environment.StackTrace);
accessRemovedCharacterErrorShown = true;
}
return new Limb[0];
}
return limbs;
@@ -97,12 +100,17 @@ namespace Barotrauma
private bool simplePhysicsEnabled;
public Character Character => character;
protected Character character;
protected float strongestImpact;
private float splashSoundTimer;
//the ragdoll builds a "tolerance" to the flow force when being pushed by water.
//Allows sudden forces (breach, letting water through a door) to heavily push the character around while ensuring flowing water won't make the characters permanently stuck.
private float flowForceTolerance, flowStunTolerance;
//the movement speed of the ragdoll
public Vector2 movement;
//the target speed towards which movement is interpolated
@@ -156,8 +164,8 @@ namespace Barotrauma
}
set
{
if (value == colliderIndex || collider == null) return;
if (value >= collider.Count || value < 0) return;
if (value == colliderIndex || collider == null) { return; }
if (value >= collider.Count || value < 0) { return; }
if (collider[colliderIndex].height < collider[value].height)
{
@@ -165,10 +173,9 @@ namespace Barotrauma
pos1.Y -= collider[colliderIndex].height * ColliderHeightFromFloor;
Vector2 pos2 = pos1;
pos2.Y += collider[value].height * 1.1f;
if (GameMain.World.RayCast(pos1, pos2).Any(f => f.CollisionCategories.HasFlag(Physics.CollisionWall))) return;
if (GameMain.World.RayCast(pos1, pos2).Any(f => f.CollisionCategories.HasFlag(Physics.CollisionWall))) { return; }
}
Vector2 pos = collider[colliderIndex].SimPosition;
pos.Y -= collider[colliderIndex].height * 0.5f;
pos.Y += collider[value].height * 0.5f;
@@ -216,7 +223,7 @@ namespace Barotrauma
mainLimb = torso ?? head;
if (mainLimb == null)
{
mainLimb = Limbs.FirstOrDefault();
mainLimb = Limbs.FirstOrDefault(l => !l.IsSevered && !l.ignoreCollisions);
}
}
return mainLimb;
@@ -238,13 +245,13 @@ namespace Barotrauma
get { return simplePhysicsEnabled; }
set
{
if (value == simplePhysicsEnabled) return;
if (value == simplePhysicsEnabled) { return; }
simplePhysicsEnabled = value;
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) continue;
if (limb.IsSevered) { continue; }
if (limb.body == null)
{
DebugConsole.ThrowError("Limb has no body! (" + (character != null ? character.Name : "Unknown character") + ", " + limb.type.ToString());
@@ -296,8 +303,6 @@ namespace Barotrauma
public float ImpactTolerance => RagdollParams.ImpactTolerance;
public bool Draggable => RagdollParams.Draggable;
public bool CanEnterSubmarine => RagdollParams.CanEnterSubmarine;
public bool CanAttackSubmarine => Limbs.Any(l => l.attack != null && l.attack.IsValidTarget(AttackTarget.Structure));
public bool CanAttackCharacters => Limbs.Any(l => l.attack != null && l.attack.IsValidTarget(AttackTarget.Character));
public float Dir => dir == Direction.Left ? -1.0f : 1.0f;
@@ -324,6 +329,7 @@ namespace Barotrauma
Submarine currSubmarine = currentHull?.Submarine;
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
limb.body.Submarine = currSubmarine;
}
Collider.Submarine = currSubmarine;
@@ -377,7 +383,7 @@ namespace Barotrauma
foreach (var kvp in items)
{
int id = kvp.Key.ID;
// This can be the case if we manipulate the ragdoll in runtime (husk appendage, limb severance)
// This can be the case if we manipulate the ragdoll at runtime (husk appendage, limb removal in the character editor)
if (id > limbs.Length - 1) { continue; }
var limb = limbs[id];
var itemList = kvp.Value;
@@ -438,7 +444,7 @@ namespace Barotrauma
{
foreach (LimbJoint joint in LimbJoints)
{
if (GameMain.World.JointList.Contains(joint)) { GameMain.World.Remove(joint); }
if (GameMain.World.JointList.Contains(joint.Joint)) { GameMain.World.Remove(joint.Joint); }
}
}
DebugConsole.Log($"Creating joints from {RagdollParams.Name}.");
@@ -523,7 +529,7 @@ namespace Barotrauma
public void AddJoint(JointParams jointParams)
{
LimbJoint joint = new LimbJoint(Limbs[jointParams.Limb1], Limbs[jointParams.Limb2], jointParams, this);
GameMain.World.Add(joint);
GameMain.World.Add(joint.Joint);
for (int i = 0; i < LimbJoints.Length; i++)
{
if (LimbJoints[i] != null) continue;
@@ -606,7 +612,7 @@ namespace Barotrauma
limb.Remove();
foreach (LimbJoint limbJoint in attachedJoints)
{
GameMain.World.Remove(limbJoint);
GameMain.World.Remove(limbJoint.Joint);
}
}
@@ -706,7 +712,7 @@ namespace Barotrauma
float impactDamage = Math.Min((impact - ImpactTolerance) * ImpactDamageMultiplayer, character.MaxVitality * MaxImpactDamage);
character.LastDamageSource = null;
character.AddDamage(impactPos, new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate(impactDamage) }, 0.0f, true);
character.AddDamage(impactPos, AfflictionPrefab.ImpactDamage.Instantiate(impactDamage).ToEnumerable(), 0.0f, true);
strongestImpact = Math.Max(strongestImpact, impact - ImpactTolerance);
character.ApplyStatusEffects(ActionType.OnImpact, 1.0f);
//briefly disable impact damage
@@ -720,12 +726,14 @@ namespace Barotrauma
ImpactProjSpecific(impact, f1.Body);
}
public void SeverLimbJoint(LimbJoint limbJoint, bool playSound = true)
private readonly List<Limb> connectedLimbs = new List<Limb>();
private readonly List<LimbJoint> checkedJoints = new List<LimbJoint>();
public bool SeverLimbJoint(LimbJoint limbJoint)
{
if (!limbJoint.CanBeSevered || limbJoint.IsSevered)
{
return;
return false;
}
limbJoint.IsSevered = true;
@@ -738,22 +746,29 @@ namespace Barotrauma
limbJoint.LimbA.body.ApplyLinearImpulse(limbDiff * mass, (limbJoint.LimbA.SimPosition + limbJoint.LimbB.SimPosition) / 2.0f);
limbJoint.LimbB.body.ApplyLinearImpulse(-limbDiff * mass, (limbJoint.LimbA.SimPosition + limbJoint.LimbB.SimPosition) / 2.0f);
List<Limb> connectedLimbs = new List<Limb>();
List<LimbJoint> checkedJoints = new List<LimbJoint>();
connectedLimbs.Clear();
checkedJoints.Clear();
GetConnectedLimbs(connectedLimbs, checkedJoints, MainLimb);
foreach (Limb limb in Limbs)
{
if (connectedLimbs.Contains(limb)) { continue; }
limb.IsSevered = true;
if (limb.type == LimbType.RightHand)
{
character.SelectedItems[0]?.Drop(character);
}
else if (limb.type == LimbType.LeftHand)
{
character.SelectedItems[1]?.Drop(character);
}
}
SeverLimbJointProjSpecific(limbJoint, playSound: true);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.NetworkMember.CreateEntityEvent(character, new object[] { NetEntityEvent.Type.Status });
}
return true;
}
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint, bool playSound);
@@ -764,7 +779,7 @@ namespace Barotrauma
foreach (LimbJoint joint in LimbJoints)
{
if (joint.IsSevered || checkedJoints.Contains(joint)) continue;
if (joint.IsSevered || checkedJoints.Contains(joint)) { continue; }
if (joint.LimbA == limb)
{
if (!connectedLimbs.Contains(joint.LimbB))
@@ -861,7 +876,7 @@ namespace Barotrauma
{
for (int i = 0; i < Limbs.Length; i++)
{
if (Limbs[i] == null) continue;
if (Limbs[i] == null) { continue; }
Limbs[i].PullJointEnabled = false;
}
}
@@ -982,8 +997,8 @@ namespace Barotrauma
{
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) continue;
if (limb.body.FarseerBody.ContactList == null) continue;
if (limb.IsSevered) { continue; }
if (limb.body.FarseerBody.ContactList == null) { continue; }
ContactEdge ce = limb.body.FarseerBody.ContactList;
while (ce != null && ce.Contact != null)
@@ -995,7 +1010,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) continue;
if (limb.IsSevered) { continue; }
limb.body.LinearVelocity += velocityChange;
}
@@ -1020,15 +1035,15 @@ namespace Barotrauma
Category collisionCategory = (IgnorePlatforms) ?
wall | Physics.CollisionProjectile | Physics.CollisionStairs
: wall | Physics.CollisionProjectile | Physics.CollisionPlatform | Physics.CollisionStairs;
if (collisionCategory == prevCollisionCategory) return;
if (collisionCategory == prevCollisionCategory) { return; }
prevCollisionCategory = collisionCategory;
Collider.CollidesWith = collisionCategory | Physics.CollisionItemBlocking;
foreach (Limb limb in Limbs)
{
if (limb.ignoreCollisions || limb.IsSevered) continue;
if (limb.ignoreCollisions || limb.IsSevered) { continue; }
try
{
@@ -1081,8 +1096,6 @@ namespace Barotrauma
CheckDistFromCollider();
UpdateCollisionCategories();
Vector2 flowForce = Vector2.Zero;
FindHull();
PreventOutsideCollision();
@@ -1104,10 +1117,7 @@ namespace Barotrauma
}
else
{
flowForce = GetFlowForce();
headInWater = false;
inWater = false;
if (currentHull.WaterVolume > currentHull.Volume * 0.95f)
{
@@ -1129,7 +1139,7 @@ namespace Barotrauma
if (lowerHull != null) floorY = ConvertUnits.ToSimUnits(lowerHull.Rect.Y - lowerHull.Rect.Height);
}
}
float standHeight =
float standHeight =
HeadPosition.HasValue ? HeadPosition.Value :
TorsoPosition.HasValue ? TorsoPosition.Value :
Collider.GetMaxExtent() * 0.5f;
@@ -1140,10 +1150,7 @@ namespace Barotrauma
}
}
if (flowForce.LengthSquared() > 0.001f)
{
Collider.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
UpdateHullFlowForces(deltaTime);
if (currentHull == null ||
currentHull.WaterVolume > currentHull.Volume * 0.95f ||
@@ -1152,7 +1159,6 @@ namespace Barotrauma
Collider.ApplyWaterForces();
}
foreach (Limb limb in Limbs)
{
//find the room which the limb is in
@@ -1177,14 +1183,7 @@ namespace Barotrauma
if (limb.Position.Y < limbHull.Surface)
{
limb.inWater = true;
if (flowForce.LengthSquared() > 0.001f)
{
limb.body.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
surfaceY = limbHull.Surface;
if (limb.type == LimbType.Head)
{
headInWater = true;
@@ -1256,6 +1255,12 @@ namespace Barotrauma
private int validityResets;
private bool CheckValidity()
{
if (limbs == null)
{
DebugConsole.ThrowError("Attempted to check the validity of a potentially removed ragdoll. Character: " + character.Name + ", id: " + character.ID + ", removed: " + character.Removed + ", ragdoll removed: " + !list.Contains(this));
Invalid = true;
return false;
}
bool isColliderValid = CheckValidity(Collider);
bool limbsValid = true;
foreach (Limb limb in limbs)
@@ -1278,8 +1283,8 @@ namespace Barotrauma
Collider.SetTransform(Vector2.Zero, 0.0f);
foreach (Limb limb in Limbs)
{
limb.body.SetTransform(Collider.SimPosition, 0.0f);
limb.body.ResetDynamics();
limb.body?.SetTransform(Collider.SimPosition, 0.0f);
limb.body?.ResetDynamics();
}
Frozen = true;
}
@@ -1353,6 +1358,74 @@ namespace Barotrauma
partial void Splash(Limb limb, Hull limbHull);
private void UpdateHullFlowForces(float deltaTime)
{
if (currentHull == null) { return; }
const float StunForceThreshold = 5.0f;
const float StunDuration = 0.5f;
const float ToleranceIncreaseSpeed = 5.0f;
const float ToleranceDecreaseSpeed = 1.0f;
//how much distance to a gap affects the force it exerts on the character
const float DistanceFactor = 0.5f;
const float ForceMultiplier = 0.035f;
Vector2 flowForce = Vector2.Zero;
foreach (Gap gap in Gap.GapList)
{
if (gap.Open <= 0.0f || !gap.linkedTo.Contains(currentHull) || gap.LerpedFlowForce.LengthSquared() < 0.01f) { continue; }
float dist = Vector2.Distance(MainLimb.WorldPosition, gap.WorldPosition) * DistanceFactor;
flowForce += Vector2.Normalize(gap.LerpedFlowForce) * (Math.Max(gap.LerpedFlowForce.Length() - dist, 0.0f) * ForceMultiplier);
}
//throwing conscious/moving characters around takes more force -> double the flow force
if (character.CanMove) { flowForce *= 2.0f; }
float flowForceMagnitude = flowForce.Length();
float limbMultipier = limbs.Count(l => l.inWater) / (float)limbs.Length;
//if the force strong enough, stun the character to let it get thrown around by the water
if ((flowForceMagnitude * limbMultipier) - flowStunTolerance > StunForceThreshold)
{
character.Stun = Math.Max(character.Stun, StunDuration);
flowStunTolerance = Math.Max(flowStunTolerance, flowForceMagnitude);
}
if (character == Character.Controlled && inWater && Screen.Selected?.Cam != null)
{
float shakeStrength = Math.Min(flowForceMagnitude / 10.0f, 5.0f) * limbMultipier;
Screen.Selected.Cam.Shake = Math.Max(Screen.Selected.Cam.Shake, shakeStrength);
}
if (flowForceMagnitude > 0.0001f)
{
flowForce = Vector2.Normalize(flowForce) * Math.Max(flowForceMagnitude - flowForceTolerance, 0.0f);
}
if (flowForceTolerance <= flowForceMagnitude * 1.5f && inWater)
{
//build up "tolerance" to the flow force
//ensures the character won't get permanently stuck by forces, while allowing sudden changes in flow to push the character hard
flowForceTolerance += deltaTime * ToleranceIncreaseSpeed;
flowStunTolerance = Math.Max(flowStunTolerance, flowForceTolerance);
}
else
{
flowForceTolerance = Math.Max(flowForceTolerance - deltaTime * ToleranceDecreaseSpeed, 0.0f);
flowStunTolerance = Math.Max(flowStunTolerance - deltaTime * ToleranceDecreaseSpeed, 0.0f);
}
if (flowForce.LengthSquared() > 0.001f)
{
Collider.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
foreach (Limb limb in limbs)
{
if (!limb.inWater) { continue; }
limb.body.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
}
}
private void RefreshFloorY(Limb refLimb = null, bool ignoreStairs = false)
{
PhysicsBody refBody = refLimb == null ? Collider : refLimb.body;
@@ -1490,7 +1563,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) continue;
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;
@@ -1554,7 +1627,7 @@ namespace Barotrauma
Vector2 forceDir = diff / (float)Math.Sqrt(distSqrd);
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) continue;
if (limb.IsSevered) { continue; }
limb.body.CollidesWith = Physics.CollisionNone;
limb.body.ApplyForce(forceDir * limb.Mass * 10.0f, maxVelocity: 10.0f);
}
@@ -1600,28 +1673,37 @@ namespace Barotrauma
UpdateNetPlayerPositionProjSpecific(deltaTime, lowestSubPos);
}
private Vector2 GetFlowForce()
{
Vector2 limbPos = Limbs[0].Position;
Vector2 force = Vector2.Zero;
foreach (Gap gap in Gap.GapList)
{
if (gap.Open <= 0.0f || gap.FlowTargetHull != currentHull || gap.LerpedFlowForce.LengthSquared() < 0.01f) continue;
Vector2 gapPos = gap.SimPosition;
float dist = Vector2.Distance(limbPos, gapPos);
force += Vector2.Normalize(gap.LerpedFlowForce) * (Math.Max(gap.LerpedFlowForce.Length() - dist, 0.0f) / 500.0f);
}
return force;
}
/// <summary>
/// Note that if there are multiple limbs of the same type, only the first of them is found in the dictionary.
/// </summary>
public Limb GetLimb(LimbType limbType)
public Limb GetLimb(LimbType limbType, bool excludeSevered = true)
{
limbDictionary.TryGetValue(limbType, out Limb limb);
Limb limb = null;
if (HasMultipleLimbsOfSameType)
{
for (int i = 0; i < 10; i++)
{
limbDictionary.TryGetValue(limbType, out limb);
if (limb == null)
{
// No limbs found
break;
}
if (!excludeSevered || !limb.IsSevered)
{
// Found a valid limb
break;
}
}
}
else
{
limbDictionary.TryGetValue(limbType, out limb);
}
if (excludeSevered && limb != null && limb.IsSevered)
{
limb = null;
}
return limb;
}
@@ -1664,10 +1746,15 @@ namespace Barotrauma
Limb lowestLimb = null;
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (lowestLimb == null)
{
lowestLimb = limb;
}
else if (limb.SimPosition.Y < lowestLimb.SimPosition.Y)
{
lowestLimb = limb;
}
}
return lowestLimb;
@@ -1700,11 +1787,12 @@ namespace Barotrauma
if (LimbJoints != null)
{
foreach (RevoluteJoint joint in LimbJoints)
foreach (var joint in LimbJoints)
{
if (GameMain.World.JointList.Contains(joint))
var j = joint.Joint;
if (GameMain.World.JointList.Contains(j))
{
GameMain.World.Remove(joint);
GameMain.World.Remove(j);
}
}
LimbJoints = null;