Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -38,21 +38,7 @@ namespace Barotrauma
public readonly AnimationType AnimationType;
public readonly AnimationParams TemporaryAnimation;
public readonly float Priority;
public bool IsActive
{
get { return _isActive; }
set
{
if (value)
{
expirationTimer = expirationTime;
}
_isActive = value;
}
}
private bool _isActive;
private float expirationTimer;
private const float expirationTime = 0.1f;
public bool IsActive;
public AnimSwap(AnimationParams temporaryAnimation, float priority)
{
@@ -61,15 +47,6 @@ namespace Barotrauma
Priority = priority;
IsActive = true;
}
public void Update(float deltaTime)
{
expirationTimer -= deltaTime;
if (expirationTimer <= 0)
{
IsActive = false;
}
}
}
protected readonly Dictionary<AnimationType, AnimSwap> tempAnimations = new Dictionary<AnimationType, AnimSwap>();
@@ -151,7 +128,8 @@ namespace Barotrauma
}
else
{
return Math.Abs(TargetMovement.X) > (WalkParams.MovementSpeed + RunParams.MovementSpeed) / 2.0f;
float movementSpeed = IsClimbing ? TargetMovement.Y : TargetMovement.X;
return Math.Abs(movementSpeed) > (WalkParams.MovementSpeed + RunParams.MovementSpeed) / 2.0f;
}
}
}
@@ -226,7 +204,7 @@ namespace Barotrauma
public void UpdateAnimations(float deltaTime)
{
UpdateTemporaryAnimations(deltaTime);
UpdateTemporaryAnimations();
UpdateAnim(deltaTime);
}
@@ -338,6 +316,31 @@ namespace Barotrauma
{
FlipLockTime = (float)Timing.TotalTime + time;
}
protected void UpdateConstantTorque(float deltaTime)
{
foreach (var limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (Math.Abs(limb.Params.ConstantTorque) > 0)
{
// TODO: not sure if this works on ground
float movementFactor = Math.Max(character.AnimController.Collider.LinearVelocity.Length() * 0.5f, 1);
limb.body.SmoothRotate(MainLimb.Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Mass * limb.Params.ConstantTorque * movementFactor, wrapAngle: true);
}
}
}
protected void UpdateBlink(float deltaTime)
{
foreach (var limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (limb.Params.BlinkFrequency <= 0) { continue; }
if (!limb.InWater && limb.Params.OnlyBlinkInWater) { continue; }
limb.UpdateBlink(deltaTime, MainLimb.Rotation);
}
}
public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
{
@@ -408,9 +411,9 @@ namespace Barotrauma
character.WorldPosition.Y - handWorldPos.Y > ConvertUnits.ToDisplayUnits(CurrentGroundedParams.TorsoPosition) / 4 &&
this is HumanoidAnimController humanoidAnimController)
{
humanoidAnimController.Crouching = true;
humanoidAnimController.Crouch();
// TODO: is this redundant/required?
humanoidAnimController.ForceSelectAnimationType = AnimationType.Crouch;
character.SetInput(InputType.Crouch, hit: false, held: true);
}
}
@@ -605,7 +608,7 @@ namespace Barotrauma
{
if (!character.Inventory.IsInLimbSlot(item, i == 0 ? InvSlotType.RightHand : InvSlotType.LeftHand)) { continue; }
#if DEBUG
if (handlePos[i].LengthSquared() > ArmLength)
if (ArmLength > 0 && handlePos[i].LengthSquared() > ArmLength)
{
DebugConsole.AddWarning($"Aim position for the item {item.Name} may be incorrect (further than the length of the character's arm)",
item.Prefab.ContentPackage);
@@ -696,6 +699,290 @@ namespace Barotrauma
hand.body.SmoothRotate(handAngle, 10.0f * handTorque * hand.Mass, wrapAngle: false);
}
}
private float prevFootPos;
protected void UpdateClimbing()
{
var ladder = character.SelectedSecondaryItem?.GetComponent<Ladder>();
if (character.IsIncapacitated)
{
Anim = Animation.None;
return;
}
else if (ladder == null)
{
StopClimbing();
return;
}
onGround = false;
IgnorePlatforms = true;
bool climbFast = !character.Params.ForceSlowClimbing && character.AnimController.IsMovingFast;
var animParams = climbFast ? RunParams : WalkParams;
// Don't slide if we can climb faster than slide.
bool slide = animParams.SlideSpeed > animParams.ClimbSpeed && targetMovement.Y < -0.1f && climbFast;
float maxClimbingSpeed = climbFast && !character.Params.ForceSlowClimbing ? RunParams.ClimbSpeed : WalkParams.ClimbSpeed;
Vector2 tempTargetMovement = TargetMovement;
tempTargetMovement.Y = Math.Clamp(tempTargetMovement.Y, slide ? -animParams.SlideSpeed : -maxClimbingSpeed, maxClimbingSpeed);
movement = MathUtils.SmoothStep(movement, tempTargetMovement, 0.3f);
Limb leftFoot = GetClimbingLimb(LimbType.LeftFoot);
Limb rightFoot = GetClimbingLimb(LimbType.RightFoot);
Limb head = GetClimbingLimb(LimbType.Head);
Limb torso = GetClimbingLimb(LimbType.Torso);
Limb leftHand = GetClimbingLimb(LimbType.LeftHand);
Limb rightHand = GetClimbingLimb(LimbType.RightHand);
Vector2 ladderSimPos = ConvertUnits.ToSimUnits(
ladder.Item.Rect.X + ladder.Item.Rect.Width / 2.0f,
ladder.Item.Rect.Y);
Vector2 ladderSimSize = ConvertUnits.ToSimUnits(ladder.Item.Rect.Size.ToVector2());
var lowestNearbyLadder = GetLowestNearbyLadder(ladder);
if (lowestNearbyLadder != null && lowestNearbyLadder != ladder)
{
ladderSimSize.Y = ConvertUnits.ToSimUnits(ladder.Item.WorldRect.Y - (lowestNearbyLadder.Item.WorldRect.Y - lowestNearbyLadder.Item.Rect.Size.Y));
}
float stepHeight = ConvertUnits.ToSimUnits(animParams.ClimbStepHeight);
if (currentHull == null && ladder.Item.Submarine != null)
{
ladderSimPos += ladder.Item.Submarine.SimPosition;
}
else if (currentHull?.Submarine != null && currentHull.Submarine != ladder.Item.Submarine && ladder.Item.Submarine != null)
{
ladderSimPos += ladder.Item.Submarine.SimPosition - currentHull.Submarine.SimPosition;
}
else if (currentHull?.Submarine != null && ladder.Item.Submarine == null)
{
ladderSimPos -= currentHull.Submarine.SimPosition;
}
float bottomPos = Collider.SimPosition.Y - ColliderHeightFromFloor - Collider.Radius - Collider.Height / 2.0f;
float torsoPos = TorsoPosition ?? 0;
float bodyMoveForce = animParams.ClimbBodyMoveForce;
if (torso != null)
{
MoveLimb(torso, new Vector2(ladderSimPos.X - 0.35f * Dir, bottomPos + torsoPos), bodyMoveForce);
}
if (head != null)
{
float headPos = HeadPosition ?? 0;
MoveLimb(head, new Vector2(ladderSimPos.X - 0.2f * Dir, bottomPos + headPos), bodyMoveForce);
}
Collider.MoveToPos(new Vector2(ladderSimPos.X - 0.1f * Dir, Collider.SimPosition.Y), bodyMoveForce);
Vector2 handPos = new Vector2(
ladderSimPos.X,
bottomPos + torsoPos + movement.Y * 0.1f - ladderSimPos.Y);
if (climbFast) { handPos.Y -= stepHeight; }
float handMoveForce = animParams.ClimbHandMoveForce;
//prevent the hands from going above the top of the ladders
handPos.Y = Math.Min(-0.5f, handPos.Y);
if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
{
if (rightHand != null)
{
MoveLimb(rightHand,
new Vector2(slide ? handPos.X + ladderSimSize.X * 0.75f : handPos.X,
(slide ? handPos.Y + stepHeight : MathUtils.Round(handPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y),
handMoveForce);
rightHand.body.ApplyTorque(Dir * 2.0f);
}
}
if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
{
if (leftHand != null)
{
MoveLimb(leftHand,
new Vector2(handPos.X - ladderSimSize.X * (slide ? 1.0f : 0.5f),
(slide ? handPos.Y + stepHeight : MathUtils.Round(handPos.Y - stepHeight, stepHeight * 2.0f) + stepHeight) + ladderSimPos.Y),
handMoveForce); ;
leftHand.body.ApplyTorque(Dir * 2.0f);
}
}
float stepHeightAdjustment = stepHeight * 2.7f;
Vector2 footPos = new Vector2(
handPos.X - Dir * 0.05f,
bottomPos + ColliderHeightFromFloor - stepHeightAdjustment - ladderSimPos.Y);
if (climbFast) { footPos.Y += stepHeight; }
//apply torque to the legs to make the knees bend
Limb leftLeg = GetClimbingLimb(LimbType.LeftLeg);
Limb rightLeg = GetClimbingLimb(LimbType.RightLeg);
//only move the feet if they're above the bottom of the ladders
//(if not, they'll just dangle in air, and the character holds itself up with its arms)
if (footPos.Y > -ladderSimSize.Y - 0.2f && leftFoot != null && rightFoot != null && leftLeg != null && rightLeg != null)
{
Limb refLimb = GetClimbingLimb(LimbType.Waist) ?? GetClimbingLimb(LimbType.Torso) ?? MainLimb;
bool leftLegBackwards = Math.Abs(leftLeg.body.Rotation - refLimb.body.Rotation) > MathHelper.Pi;
bool rightLegBackwards = Math.Abs(rightLeg.body.Rotation - refLimb.body.Rotation) > MathHelper.Pi;
float footMoveForce = animParams.ClimbFootMoveForce;
if (slide)
{
if (!leftLegBackwards) { MoveLimb(leftFoot, new Vector2(footPos.X - ladderSimSize.X * 0.5f, footPos.Y + ladderSimPos.Y), footMoveForce, pullFromCenter: true); }
if (!rightLegBackwards) { MoveLimb(rightFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), footMoveForce, pullFromCenter: true); }
}
else
{
float leftFootPos = MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight;
float prevLeftFootPos = MathUtils.Round(prevFootPos + stepHeight, stepHeight * 2.0f) - stepHeight;
if (!leftLegBackwards) { MoveLimb(leftFoot, new Vector2(footPos.X, leftFootPos + ladderSimPos.Y), footMoveForce, pullFromCenter: true); }
float rightFootPos = MathUtils.Round(footPos.Y, stepHeight * 2.0f);
float prevRightFootPos = MathUtils.Round(prevFootPos, stepHeight * 2.0f);
if (!rightLegBackwards) { MoveLimb(rightFoot, new Vector2(footPos.X, rightFootPos + ladderSimPos.Y), footMoveForce, pullFromCenter: true); }
#if CLIENT
if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
{
SoundPlayer.PlaySound("footstep_armor_heavy", leftFoot.WorldPosition, hullGuess: currentHull);
leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
}
if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
{
SoundPlayer.PlaySound("footstep_armor_heavy", rightFoot.WorldPosition, hullGuess: currentHull);
rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
}
#endif
prevFootPos = footPos.Y;
}
if (!leftLegBackwards) { leftLeg.body.ApplyTorque(Dir * -8.0f); } // TODO: expose?
if (!rightLegBackwards) { rightLeg.body.ApplyTorque(Dir * -8.0f); }
}
float movementFactor = (handPos.Y / stepHeight) * (float)Math.PI;
movementFactor = 0.8f + (float)Math.Abs(Math.Sin(movementFactor));
Vector2 subSpeed = currentHull != null || ladder.Item.Submarine == null
? Vector2.Zero : ladder.Item.Submarine.Velocity;
//reached the top of the ladders -> can't go further up
Vector2 climbForce = new Vector2(0.0f, movement.Y) * movementFactor;
if (!InWater) { climbForce.Y += 0.3f * movementFactor; }
if (character.SimPosition.Y > ladderSimPos.Y) { climbForce.Y = Math.Min(0.0f, climbForce.Y); }
//reached the bottom -> can't go further down
float minHeightFromFloor = ColliderHeightFromFloor / 2 + Collider.Height;
if (floorFixture != null &&
!floorFixture.CollisionCategories.HasFlag(Physics.CollisionStairs) &&
!floorFixture.CollisionCategories.HasFlag(Physics.CollisionPlatform) &&
character.SimPosition.Y < standOnFloorY + minHeightFromFloor)
{
climbForce.Y = MathHelper.Clamp((standOnFloorY + minHeightFromFloor - character.SimPosition.Y) * 5.0f, climbForce.Y, 1.0f);
}
//apply forces to the collider to move the Character up/down
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass);
// Don't rotate the head on non-humanoids, because it can cause issues with some ragdolls.
// E.g. the head might not actually be head, or it's not where we expect it to be.
if (head != null && character.IsHumanoid)
{
if (Aiming)
{
RotateHead(head);
}
else if (Anim == Animation.UsingItemWhileClimbing && character.SelectedItem is { } selectedItem)
{
Vector2 diff = (selectedItem.WorldPosition - head.WorldPosition) * Dir;
float targetRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 * Dir);
head.body.SmoothRotate(targetRotation, force: animParams.HeadTorque);
}
else
{
float movementMultiplier = targetMovement.Y < 0 ? 0 : 1;
head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, force: animParams.HeadTorque);
}
}
if (ladder.Item.Prefab.Triggers.None())
{
character.ReleaseSecondaryItem();
return;
}
Rectangle trigger = ladder.Item.Prefab.Triggers.FirstOrDefault();
trigger = ladder.Item.TransformTrigger(trigger);
bool isRemote = false;
bool isClimbing = true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
isRemote = character.IsRemotelyControlled;
}
//if the character is remotely controlled,
//let the server decide when to deselect the ladder and stop climbing
if (!isRemote)
{
if ((character.IsKeyDown(InputType.Left) || character.IsKeyDown(InputType.Right)) &&
(!character.IsKeyDown(InputType.Up) && !character.IsKeyDown(InputType.Down)))
{
isClimbing = false;
}
}
if (!isClimbing)
{
character.StopClimbing();
IgnorePlatforms = false;
}
Ladder GetLowestNearbyLadder(Ladder currentLadder, float threshold = 16.0f)
{
foreach (Ladder ladder in Ladder.List)
{
if (ladder == currentLadder || !ladder.Item.IsInteractable(character)) { continue; }
if (Math.Abs(ladder.Item.WorldPosition.X - currentLadder.Item.WorldPosition.X) > threshold) { continue; }
if (ladder.Item.WorldPosition.Y > currentLadder.Item.WorldPosition.Y) { continue; }
if ((currentLadder.Item.WorldRect.Y - currentLadder.Item.Rect.Height) - ladder.Item.WorldRect.Y > threshold) { continue; }
return ladder;
}
return null;
}
Limb GetClimbingLimb(LimbType limbType)
{
if (HasMultipleLimbsOfSameType)
{
// First try to find a match using the secondary type, if that fails, use the primary type and exclude all the limbs with the secondary type.
// Secondary limbs are first excluded and then targeted, because some feet are meant to be used as hands in this context, which means we don't want to get them when seeking the feet.
return GetLimb(limbType, useSecondaryType: true) ?? GetLimb(limbType, excludeLimbsWithSecondaryType: true);
}
else
{
return GetLimb(limbType);
}
}
}
protected void RotateHead(Limb head)
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
Vector2 dir = (mousePos - head.SimPosition) * Dir;
float rot = MathUtils.VectorToAngle(dir);
var neckJoint = GetJointBetweenLimbs(LimbType.Head, LimbType.Torso);
if (neckJoint != null)
{
float offset = MathUtils.WrapAnglePi(GetLimb(LimbType.Torso).body.Rotation);
float lowerLimit = neckJoint.LowerLimit + offset;
float upperLimit = neckJoint.UpperLimit + offset;
float min = Math.Min(lowerLimit, upperLimit);
float max = Math.Max(lowerLimit, upperLimit);
rot = Math.Clamp(rot, min, max);
}
head.body.SmoothRotate(rot, CurrentAnimationParams.HeadTorque);
}
public void ApplyPose(Vector2 leftHandPos, Vector2 rightHandPos, Vector2 leftFootPos, Vector2 rightFootPos, float footMoveForce = 10)
{
@@ -754,8 +1041,16 @@ namespace Barotrauma
Limb rightHand = GetLimb(LimbType.RightHand);
if (rightHand == null) { return; }
rightShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.RightArm) ?? GetJointBetweenLimbs(LimbType.Head, LimbType.RightArm) ?? GetJoint(LimbType.RightArm, new LimbType[] { LimbType.RightHand, LimbType.RightForearm });
leftShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.LeftArm) ?? GetJointBetweenLimbs(LimbType.Head, LimbType.LeftArm) ?? GetJoint(LimbType.LeftArm, new LimbType[] { LimbType.LeftHand, LimbType.LeftForearm });
rightShoulder =
GetJointBetweenLimbs(LimbType.Torso, LimbType.RightArm) ??
GetJointBetweenLimbs(LimbType.Head, LimbType.RightArm) ??
GetJoint(LimbType.RightArm, new LimbType[] { LimbType.RightHand, LimbType.RightForearm }) ??
GetJointBetweenLimbs(LimbType.Torso, LimbType.RightHand);
leftShoulder =
GetJointBetweenLimbs(LimbType.Torso, LimbType.LeftArm) ??
GetJointBetweenLimbs(LimbType.Head, LimbType.LeftArm) ??
GetJoint(LimbType.LeftArm, new LimbType[] { LimbType.LeftHand, LimbType.LeftForearm }) ??
GetJointBetweenLimbs(LimbType.Torso, LimbType.LeftHand);
Vector2 localAnchorShoulder = Vector2.Zero;
Vector2 localAnchorElbow = Vector2.Zero;
@@ -818,6 +1113,13 @@ namespace Barotrauma
CalculateArmLengths();
}
}
public void RecreateAndRespawn(RagdollParams ragdollParams = null)
{
Vector2 pos = character.WorldPosition;
Recreate(ragdollParams);
character.TeleportTo(pos);
}
private void StartAnimation(Animation animation)
{
@@ -906,7 +1208,7 @@ namespace Barotrauma
return true;
}
private void UpdateTemporaryAnimations(float deltaTime)
private void UpdateTemporaryAnimations()
{
if (tempAnimations.None()) { return; }
foreach ((AnimationType animationType, AnimSwap animSwap) in tempAnimations)
@@ -932,7 +1234,8 @@ namespace Barotrauma
expiredAnimations.Clear();
foreach (AnimSwap animSwap in tempAnimations.Values)
{
animSwap.Update(deltaTime);
// Will be removed on the next frame, unless something keeps it alive.
animSwap.IsActive = false;
}
}
@@ -139,26 +139,15 @@ namespace Barotrauma
ResetState();
return;
}
UpdateConstantTorque(deltaTime);
UpdateBlink(deltaTime);
var mainLimb = MainLimb;
levitatingCollider = !IsHangingWithRope;
levitatingCollider = !IsHangingWithRope && !IsClimbing;
if (!character.CanMove)
{
levitatingCollider = false;
Collider.FarseerBody.FixedRotation = false;
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
Collider.Enabled = false;
Collider.LinearVelocity = mainLimb.LinearVelocity;
Collider.SetTransformIgnoreContacts(mainLimb.SimPosition, mainLimb.Rotation);
//reset pull joints to prevent the character from "hanging" mid-air if pull joints had been active when the character was still moving
//(except when dragging, then we need the pull joints)
if (!Draggable || character.SelectedBy == null)
{
ResetPullJoints();
}
}
UpdateRagdollControlsMovement();
if (character.IsDead && deathAnimTimer < deathAnimDuration)
{
deathAnimTimer += deltaTime;
@@ -184,11 +173,11 @@ namespace Barotrauma
if (InWater)
{
Collider.SetTransform(new Vector2(Collider.SimPosition.X, MainLimb.SimPosition.Y), 0.0f);
Collider.SetTransformIgnoreContacts(new Vector2(Collider.SimPosition.X, MainLimb.SimPosition.Y), 0.0f);
}
else
{
Collider.SetTransform(new Vector2(
Collider.SetTransformIgnoreContacts(new Vector2(
Collider.SimPosition.X,
Math.Max(lowestLimb.SimPosition.Y + (Collider.Radius + Collider.Height / 2), Collider.SimPosition.Y)),
0.0f);
@@ -208,6 +197,11 @@ namespace Barotrauma
{
TargetMovement = TargetMovement.ClampLength(2);
}
if (IsClimbing)
{
UpdateClimbing();
}
if (inWater && !forceStanding)
{
@@ -336,7 +330,6 @@ namespace Barotrauma
if (target == null) { return; }
Limb mouthLimb = GetLimb(LimbType.Head);
if (mouthLimb == null) { return; }
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
//stop dragging if there's something between the pull limb and the target
@@ -357,23 +350,23 @@ namespace Barotrauma
return;
}
}
float dmg = character.Params.EatingSpeed;
float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.Mass, 1)) * 10);
eatTimer += deltaTime * eatSpeed;
Vector2 mouthPos = SimplePhysicsEnabled ? character.SimPosition : GetMouthPosition().Value;
Vector2 attackSimPosition = character.Submarine == null ? ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;
Vector2 limbDiff = attackSimPosition - mouthPos;
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 1);
bool tooFar = character.InWater ? limbDiff.LengthSquared() > extent * extent : limbDiff.X > extent;
if (tooFar)
{
character.SelectedCharacter = null;
}
else
if (Character.CanEat)
{
Vector2 mouthPos = SimplePhysicsEnabled ? character.SimPosition : GetMouthPosition() ?? Vector2.Zero;
Vector2 attackSimPosition = character.Submarine == null ? ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;
Vector2 limbDiff = attackSimPosition - mouthPos;
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 1);
bool tooFar = character.InWater ? limbDiff.LengthSquared() > extent * extent : limbDiff.X > extent;
if (tooFar)
{
character.DeselectCharacter();
return;
}
float dmg = character.Params.EatingSpeed;
float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.Mass, 1)) * 10);
eatTimer += deltaTime * eatSpeed;
//pull the target character to the position of the mouth
//(+ make the force fluctuate to waggle the character a bit)
float dragForce = MathHelper.Clamp(eatSpeed * 10, 0, 40);
@@ -405,20 +398,19 @@ namespace Barotrauma
else
{
float force = (float)Math.Sin(eatTimer * 100) * mouthLimb.Mass;
mouthLimb.body.ApplyLinearImpulse(Vector2.UnitY * force * 2, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
mouthLimb.body.ApplyTorque(-force * 50);
mouthLimb.body.ApplyLinearImpulse(Vector2.UnitY * force * mouthLimb.Params.EatImpulse, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
mouthLimb.body.ApplyTorque(-force * mouthLimb.Params.EatTorque);
}
if (Character.CanEat && target.IsDead)
var jaw = GetLimb(LimbType.Jaw);
if (jaw != null)
{
jaw.body.ApplyTorque(-(float)Math.Sin(eatTimer * 150) * jaw.Mass * 25);
}
character.ApplyStatusEffects(ActionType.OnEating, deltaTime);
if (target.IsDead)
{
var jaw = GetLimb(LimbType.Jaw);
if (jaw != null)
{
jaw.body.ApplyTorque(-(float)Math.Sin(eatTimer * 150) * jaw.Mass * 25);
}
character.ApplyStatusEffects(ActionType.OnEating, deltaTime);
float particleFrequency = MathHelper.Clamp(eatSpeed / 2, 0.02f, 0.5f);
if (Rand.Value() < particleFrequency / 6)
{
@@ -430,7 +422,7 @@ namespace Barotrauma
}
if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
{
static bool CanBeSevered(LimbJoint j) => !j.IsSevered && j.CanBeSevered && j.LimbA != null && !j.LimbA.IsSevered && j.LimbB != null && !j.LimbB.IsSevered;
static bool CanBeSevered(LimbJoint j) => !j.IsSevered && j.CanBeSevered && j.LimbA is { IsSevered: false } && j.LimbB is { IsSevered: false };
//keep severing joints until there is only one limb left
var nonSeveredJoints = target.AnimController.LimbJoints.Where(CanBeSevered);
if (nonSeveredJoints.None())
@@ -440,16 +432,13 @@ namespace Barotrauma
{
target.Inventory?.AllItemsMod.ForEach(it => it?.Drop(dropper: null));
}
//only one limb left, the character is now full eaten
Entity.Spawner?.AddEntityToRemoveQueue(target);
if (Character.AIController is EnemyAIController enemyAi)
{
enemyAi.PetBehavior?.OnEat(target);
}
character.SelectedCharacter = null;
character.DeselectCharacter();
}
else //sever a random joint
{
@@ -460,7 +449,7 @@ namespace Barotrauma
}
}
public bool reverse;
public bool Reverse;
void UpdateSineAnim(float deltaTime)
{
@@ -510,7 +499,7 @@ namespace Barotrauma
}
else
{
Vector2 transformedMovement = reverse ? -movement : movement;
Vector2 transformedMovement = Reverse ? -movement : movement;
float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
float mainLimbAngle = 0;
if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
@@ -555,7 +544,6 @@ namespace Barotrauma
foreach (var limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (limb.type != LimbType.Tail) { continue; }
if (!limb.Params.ApplyTailAngle) { continue; }
RotateTail(limb);
isAngleApplied = true;
@@ -592,7 +580,7 @@ namespace Barotrauma
else
{
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
if (reverse)
if (Reverse)
{
movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
}
@@ -651,29 +639,21 @@ namespace Barotrauma
foreach (var limb in Limbs)
{
if (limb.IsSevered) { continue; }
switch (limb.type)
if (limb.type is LimbType.LeftFoot or LimbType.RightFoot)
{
case LimbType.LeftFoot:
case LimbType.RightFoot:
if (CurrentSwimParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
{
SmoothRotateWithoutWrapping(limb, movementAngle + CurrentSwimParams.FootAnglesInRadians[limb.Params.ID] * Dir, mainLimb, FootTorque);
}
break;
case LimbType.Tail:
if (waveLength > 0 && waveAmplitude > 0)
{
float waveRotation = (float)Math.Sin(WalkPos * limb.Params.SineFrequencyMultiplier);
limb.body.ApplyTorque(waveRotation * limb.Mass * waveAmplitude * limb.Params.SineAmplitudeMultiplier);
}
break;
if (CurrentSwimParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
{
SmoothRotateWithoutWrapping(limb, movementAngle + CurrentSwimParams.FootAnglesInRadians[limb.Params.ID] * Dir, mainLimb, FootTorque);
}
}
if (limb.type == LimbType.Tail || limb.Params.ApplySineMovement)
{
if (waveLength > 0 && waveAmplitude > 0)
{
float waveRotation = (float)Math.Sin(WalkPos * limb.Params.SineFrequencyMultiplier);
limb.body.ApplyTorque(waveRotation * limb.Mass * waveAmplitude * limb.Params.SineAmplitudeMultiplier);
}
}
}
for (int i = 0; i < Limbs.Length; i++)
{
var limb = Limbs[i];
if (limb.IsSevered) { continue; }
if (limb.SteerForce <= 0.0f) { continue; }
if (!Collider.PhysEnabled) { continue; }
Vector2 pullPos = limb.PullJointWorldAnchorA;
@@ -698,20 +678,6 @@ namespace Barotrauma
}
}
foreach (var limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (Math.Abs(limb.Params.ConstantTorque) > 0)
{
float movementFactor = Math.Max(character.AnimController.Collider.LinearVelocity.Length() * 0.5f, 1);
limb.body.SmoothRotate(MainLimb.Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Mass * limb.Params.ConstantTorque * movementFactor, wrapAngle: true);
}
if (limb.Params.BlinkFrequency > 0)
{
limb.UpdateBlink(deltaTime, MainLimb.Rotation);
}
}
floorY = Limbs[0].SimPosition.Y;
}
@@ -744,9 +710,9 @@ namespace Barotrauma
}
float offset = MathHelper.Pi * CurrentGroundedParams.StepLiftOffset;
if (CurrentGroundedParams.MultiplyByDir)
if (character.AnimController.Dir < 0)
{
offset *= Dir;
offset += MathHelper.Pi * CurrentGroundedParams.StepLiftFrequency;
}
float stepLift = TargetMovement.X == 0.0f ? 0 :
(float)Math.Sin(WalkPos * Dir * CurrentGroundedParams.StepLiftFrequency + offset) * (CurrentGroundedParams.StepLiftAmount / 100);
@@ -847,14 +813,6 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
if (Math.Abs(limb.Params.ConstantTorque) > 0)
{
limb.body.SmoothRotate(MainLimb.Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Mass * limb.Params.ConstantTorque, wrapAngle: true);
}
if (limb.Params.BlinkFrequency > 0 && !limb.Params.OnlyBlinkInWater)
{
limb.UpdateBlink(deltaTime, MainLimb.Rotation);
}
switch (limb.type)
{
case LimbType.LeftFoot:
@@ -1024,7 +982,7 @@ namespace Barotrauma
if (RagdollParams.IsSpritesheetOrientationHorizontal)
{
//horizontally aligned limbs need to be flipped 180 degrees
l.body.SetTransform(l.SimPosition, l.body.Rotation + MathHelper.Pi * Dir);
l.body.SetTransformIgnoreContacts(l.SimPosition, l.body.Rotation + MathHelper.Pi * Dir);
}
//no need to do anything when flipping vertically oriented limbs
//the sprite gets flipped horizontally, which does the job
@@ -13,10 +13,8 @@ namespace Barotrauma
private const float SteepestWalkableSlopeAngleDegrees = 55f;
private const float SlowlyWalkableSlopeAngleDegrees = 30f;
private static readonly float SteepestWalkableSlopeNormalX =
MathF.Sin(MathHelper.ToRadians(SteepestWalkableSlopeAngleDegrees));
private static readonly float SlowlyWalkableSlopeNormalX =
MathF.Sin(MathHelper.ToRadians(SlowlyWalkableSlopeAngleDegrees));
private static readonly float SteepestWalkableSlopeNormalX = MathF.Sin(MathHelper.ToRadians(SteepestWalkableSlopeAngleDegrees));
private static readonly float SlowlyWalkableSlopeNormalX = MathF.Sin(MathHelper.ToRadians(SlowlyWalkableSlopeAngleDegrees));
private const float MaxSpeedOnStairs = 1.7f;
private const float SteepSlopePushMagnitude = MaxSpeedOnStairs;
@@ -254,7 +252,8 @@ namespace Barotrauma
{
if (Frozen) { return; }
if (MainLimb == null) { return; }
UpdateConstantTorque(deltaTime);
UpdateBlink(deltaTime);
levitatingCollider = !IsHangingWithRope;
if (onGround && character.CanMove)
{
@@ -297,25 +296,7 @@ namespace Barotrauma
fallingProneAnimTimer += deltaTime;
UpdateFallingProne(1.0f);
}
levitatingCollider = false;
Collider.FarseerBody.FixedRotation = false;
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
if (Collider.Enabled)
{
//deactivating the collider -> make the main limb inherit the collider's velocity because it'll control the movement now
MainLimb.body.LinearVelocity = Collider.LinearVelocity;
Collider.Enabled = false;
}
Collider.LinearVelocity = MainLimb.LinearVelocity;
Collider.SetTransformIgnoreContacts(MainLimb.SimPosition, MainLimb.Rotation);
//reset pull joints to prevent the character from "hanging" mid-air if pull joints had been active when the character was still moving
//(except when dragging, then we need the pull joints)
if (!Draggable || character.SelectedBy == null)
{
ResetPullJoints();
}
}
UpdateRagdollControlsMovement();
return;
}
fallingProneAnimTimer = 0.0f;
@@ -325,7 +306,7 @@ namespace Barotrauma
{
var lowestLimb = FindLowestLimb();
Collider.SetTransform(new Vector2(
Collider.SetTransformIgnoreContacts(new Vector2(
Collider.SimPosition.X,
Math.Max(lowestLimb.SimPosition.Y + (Collider.Radius + Collider.Height / 2), Collider.SimPosition.Y)),
Collider.Rotation);
@@ -357,7 +338,7 @@ namespace Barotrauma
float angleDiff = MathUtils.GetShortestAngle(Collider.Rotation, 0.0f);
if (Math.Abs(angleDiff) > 0.001f)
{
Collider.SetTransform(Collider.SimPosition, Collider.Rotation + angleDiff);
Collider.SetTransformIgnoreContacts(Collider.SimPosition, Collider.Rotation + angleDiff);
}
}
@@ -582,9 +563,7 @@ namespace Barotrauma
footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(TargetMovement.X), 0.3f)) * Dir;
}
movement = overrideTargetMovement == Vector2.Zero ?
MathUtils.SmoothStep(movement, TargetMovement, movementLerp) :
overrideTargetMovement;
movement = overrideTargetMovement ?? MathUtils.SmoothStep(movement, TargetMovement, movementLerp);
if (Math.Abs(movement.X) < 0.005f)
{
@@ -652,9 +631,14 @@ namespace Barotrauma
{
movement = Vector2.Zero;
}
float offset = MathHelper.Pi * currentGroundedParams.StepLiftOffset;
if (character.AnimController.Dir < 0)
{
offset += MathHelper.Pi * currentGroundedParams.StepLiftFrequency;
}
float stepLift = TargetMovement.X == 0.0f ? 0 :
(float)Math.Sin(WalkPos * currentGroundedParams.StepLiftFrequency + MathHelper.Pi * currentGroundedParams.StepLiftOffset) * (currentGroundedParams.StepLiftAmount / 100);
(float)Math.Sin(WalkPos * Dir * currentGroundedParams.StepLiftFrequency + offset) * (currentGroundedParams.StepLiftAmount / 100);
float y = colliderPos.Y + stepLift;
@@ -987,7 +971,7 @@ namespace Barotrauma
{
head.body.SmoothRotate(Collider.Rotation + HeadAngle.Value * Dir, CurrentSwimParams.HeadTorque);
}
else
else if (character.FollowCursor)
{
RotateHead(head);
}
@@ -1145,245 +1129,6 @@ namespace Barotrauma
}
}
private float prevFootPos;
void UpdateClimbing()
{
var ladder = character.SelectedSecondaryItem?.GetComponent<Ladder>();
if (character.IsIncapacitated)
{
Anim = Animation.None;
return;
}
else if (ladder == null)
{
StopClimbing();
return;
}
onGround = false;
IgnorePlatforms = true;
bool climbFast = targetMovement.Y > 3.0f;
bool slide = targetMovement.Y < -1.1f;
Vector2 tempTargetMovement = TargetMovement;
tempTargetMovement.Y = climbFast ?
Math.Min(tempTargetMovement.Y, 2.0f) :
Math.Min(tempTargetMovement.Y, 1.0f);
movement = MathUtils.SmoothStep(movement, tempTargetMovement, 0.3f);
Limb leftFoot = GetLimb(LimbType.LeftFoot);
Limb rightFoot = GetLimb(LimbType.RightFoot);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
if (leftHand == null || rightHand == null || head == null || torso == null) { return; }
Vector2 ladderSimPos = ConvertUnits.ToSimUnits(
ladder.Item.Rect.X + ladder.Item.Rect.Width / 2.0f,
ladder.Item.Rect.Y);
Vector2 ladderSimSize = ConvertUnits.ToSimUnits(ladder.Item.Rect.Size.ToVector2());
float lowestLadderSimPos = ladderSimPos.Y - ladderSimPos.Y;
var lowestNearbyLadder = GetLowestNearbyLadder(ladder);
if (lowestNearbyLadder != null && lowestNearbyLadder != ladder)
{
ladderSimSize.Y = ConvertUnits.ToSimUnits(ladder.Item.WorldRect.Y - (lowestNearbyLadder.Item.WorldRect.Y - lowestNearbyLadder.Item.Rect.Size.Y));
}
float stepHeight = ConvertUnits.ToSimUnits(30.0f);
if (climbFast) { stepHeight *= 2; }
if (currentHull == null && ladder.Item.Submarine != null)
{
ladderSimPos += ladder.Item.Submarine.SimPosition;
}
else if (currentHull?.Submarine != null && currentHull.Submarine != ladder.Item.Submarine && ladder.Item.Submarine != null)
{
ladderSimPos += ladder.Item.Submarine.SimPosition - currentHull.Submarine.SimPosition;
}
else if (currentHull?.Submarine != null && ladder.Item.Submarine == null)
{
ladderSimPos -= currentHull.Submarine.SimPosition;
}
float bottomPos = Collider.SimPosition.Y - ColliderHeightFromFloor - Collider.Radius - Collider.Height / 2.0f;
float torsoPos = TorsoPosition ?? 0;
MoveLimb(torso, new Vector2(ladderSimPos.X - 0.35f * Dir, bottomPos + torsoPos), 10.5f);
float headPos = HeadPosition ?? 0;
MoveLimb(head, new Vector2(ladderSimPos.X - 0.2f * Dir, bottomPos + headPos), 10.5f);
Collider.MoveToPos(new Vector2(ladderSimPos.X - 0.1f * Dir, Collider.SimPosition.Y), 10.5f);
Vector2 handPos = new Vector2(
ladderSimPos.X,
bottomPos + torsoPos + movement.Y * 0.1f - ladderSimPos.Y);
if (climbFast) { handPos.Y -= stepHeight; }
//prevent the hands from going above the top of the ladders
handPos.Y = Math.Min(-0.5f, handPos.Y);
if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
{
MoveLimb(rightHand,
new Vector2(slide ? handPos.X + ladderSimSize.X * 0.5f : handPos.X,
(slide ? handPos.Y : MathUtils.Round(handPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y),
5.2f);
rightHand.body.ApplyTorque(Dir * 2.0f);
}
if (!Aiming || !(character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand)?.GetComponent<Holdable>()?.ControlPose ?? false) || Math.Abs(movement.Y) > 0.01f)
{
MoveLimb(leftHand,
new Vector2(handPos.X - ladderSimSize.X * 0.5f,
(slide ? handPos.Y : MathUtils.Round(handPos.Y - stepHeight, stepHeight * 2.0f) + stepHeight) + ladderSimPos.Y),
5.2f); ;
leftHand.body.ApplyTorque(Dir * 2.0f);
}
Vector2 footPos = new Vector2(
handPos.X - Dir * 0.05f,
bottomPos + ColliderHeightFromFloor - stepHeight * 2.7f - ladderSimPos.Y);
if (climbFast) { footPos.Y += stepHeight; }
//apply torque to the legs to make the knees bend
Limb leftLeg = GetLimb(LimbType.LeftLeg);
Limb rightLeg = GetLimb(LimbType.RightLeg);
//only move the feet if they're above the bottom of the ladders
//(if not, they'll just dangle in air, and the character holds itself up with it's arms)
if (footPos.Y > -ladderSimSize.Y - 0.2f && leftFoot != null && rightFoot != null)
{
Limb refLimb = GetLimb(LimbType.Waist) ?? GetLimb(LimbType.Torso);
bool leftLegBackwards = Math.Abs(leftLeg.body.Rotation - refLimb.body.Rotation) > MathHelper.Pi;
bool rightLegBackwards = Math.Abs(rightLeg.body.Rotation - refLimb.body.Rotation) > MathHelper.Pi;
if (slide)
{
if (!leftLegBackwards) { MoveLimb(leftFoot, new Vector2(footPos.X - ladderSimSize.X * 0.5f, footPos.Y + ladderSimPos.Y), 15.5f, true); }
if (!rightLegBackwards) { MoveLimb(rightFoot, new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f, true); }
}
else
{
float leftFootPos = MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight;
float prevLeftFootPos = MathUtils.Round(prevFootPos + stepHeight, stepHeight * 2.0f) - stepHeight;
if (!leftLegBackwards) { MoveLimb(leftFoot, new Vector2(footPos.X, leftFootPos + ladderSimPos.Y), 15.5f, true); }
float rightFootPos = MathUtils.Round(footPos.Y, stepHeight * 2.0f);
float prevRightFootPos = MathUtils.Round(prevFootPos, stepHeight * 2.0f);
if (!rightLegBackwards) { MoveLimb(rightFoot, new Vector2(footPos.X, rightFootPos + ladderSimPos.Y), 15.5f, true); }
#if CLIENT
if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
{
SoundPlayer.PlaySound("footstep_armor_heavy", leftFoot.WorldPosition, hullGuess: currentHull);
leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
}
if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
{
SoundPlayer.PlaySound("footstep_armor_heavy", rightFoot.WorldPosition, hullGuess: currentHull);
rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
}
#endif
prevFootPos = footPos.Y;
}
if (!leftLegBackwards) { leftLeg.body.ApplyTorque(Dir * -8.0f); }
if (!rightLegBackwards) { rightLeg.body.ApplyTorque(Dir * -8.0f); }
}
float movementFactor = (handPos.Y / stepHeight) * (float)Math.PI;
movementFactor = 0.8f + (float)Math.Abs(Math.Sin(movementFactor));
Vector2 subSpeed = currentHull != null || ladder.Item.Submarine == null
? Vector2.Zero : ladder.Item.Submarine.Velocity;
//reached the top of the ladders -> can't go further up
Vector2 climbForce = new Vector2(0.0f, movement.Y) * movementFactor;
if (!InWater) { climbForce.Y += 0.3f * movementFactor; }
if (character.SimPosition.Y > ladderSimPos.Y) { climbForce.Y = Math.Min(0.0f, climbForce.Y); }
//reached the bottom -> can't go further down
float minHeightFromFloor = ColliderHeightFromFloor / 2 + Collider.Height;
if (floorFixture != null &&
!floorFixture.CollisionCategories.HasFlag(Physics.CollisionStairs) &&
!floorFixture.CollisionCategories.HasFlag(Physics.CollisionPlatform) &&
character.SimPosition.Y < standOnFloorY + minHeightFromFloor)
{
climbForce.Y = MathHelper.Clamp((standOnFloorY + minHeightFromFloor - character.SimPosition.Y) * 5.0f, climbForce.Y, 1.0f);
}
//apply forces to the collider to move the Character up/down
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass);
if (Aiming)
{
RotateHead(head);
}
else if (Anim == Animation.UsingItemWhileClimbing && character.SelectedItem is { } selectedItem)
{
Vector2 diff = (selectedItem.WorldPosition - head.WorldPosition) * Dir;
float targetRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 * Dir);
head.body.SmoothRotate(targetRotation, force: WalkParams.HeadTorque);
}
else
{
float movementMultiplier = targetMovement.Y < 0 ? 0 : 1;
head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, force: WalkParams.HeadTorque);
}
if (ladder.Item.Prefab.Triggers.None())
{
character.ReleaseSecondaryItem();
return;
}
Rectangle trigger = ladder.Item.Prefab.Triggers.FirstOrDefault();
trigger = ladder.Item.TransformTrigger(trigger);
bool isRemote = false;
bool isClimbing = true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
isRemote = character.IsRemotelyControlled;
}
if (isRemote)
{
if (Math.Abs(targetMovement.X) > 0.05f ||
(TargetMovement.Y < 0.0f && ConvertUnits.ToSimUnits(trigger.Height) + handPos.Y < HeadPosition) ||
(TargetMovement.Y > 0.0f && handPos.Y > 0.1f))
{
isClimbing = false;
}
}
else if ((character.IsKeyDown(InputType.Left) || character.IsKeyDown(InputType.Right)) &&
(!character.IsKeyDown(InputType.Up) && !character.IsKeyDown(InputType.Down)))
{
isClimbing = false;
}
if (!isClimbing)
{
character.StopClimbing();
IgnorePlatforms = false;
}
Ladder GetLowestNearbyLadder(Ladder currentLadder, float threshold = 16.0f)
{
foreach (Ladder ladder in Ladder.List)
{
if (ladder == currentLadder || !ladder.Item.IsInteractable(character)) { continue; }
if (Math.Abs(ladder.Item.WorldPosition.X - currentLadder.Item.WorldPosition.X) > threshold) { continue; }
if (ladder.Item.WorldPosition.Y > currentLadder.Item.WorldPosition.Y) { continue; }
if ((currentLadder.Item.WorldRect.Y - currentLadder.Item.Rect.Height) - ladder.Item.WorldRect.Y > threshold) { continue; }
return ladder;
}
return null;
}
}
void UpdateFallingProne(float strength, bool moveHands = true, bool moveTorso = true, bool moveLegs = true)
{
if (strength <= 0.0f) { return; }
@@ -1518,7 +1263,7 @@ namespace Barotrauma
float cprBoost = character.GetStatValue(StatTypes.CPRBoost);
int skill = (int)character.GetSkillLevel("medical");
int skill = (int)character.GetSkillLevel(Tags.MedicalSkill);
if (GameMain.NetworkMember is not { IsClient: true })
{
@@ -1595,7 +1340,7 @@ namespace Barotrauma
//otherwise it's easy to abuse the system by repeatedly reviving in a low-oxygen room
if (!target.IsDead)
{
target.CharacterHealth.CalculateVitality();
target.CharacterHealth.RecalculateVitality();
if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
{
character.Info?.ApplySkillGain(Tags.MedicalSkill, SkillSettings.Current.SkillIncreasePerCprRevive);
@@ -1811,7 +1556,7 @@ namespace Barotrauma
string errorMsg =
$"Attempted to move the anchor B of a limb's pull joint extremely far from the limb in {nameof(DragCharacter)}. " +
$"Character in sub: {character.Submarine != null}, target in sub: {target.Submarine != null}.";
GameAnalyticsManager.AddErrorEventOnce("DragCharacter:PullJointTooFar", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
GameAnalyticsManager.AddErrorEventOnce("DragCharacter:PullJointTooFar", GameAnalyticsManager.ErrorSeverity.Warning, errorMsg);
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#endif
@@ -1876,23 +1621,11 @@ namespace Barotrauma
}
}
}
private void RotateHead(Limb head)
public void Crouch()
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
Vector2 dir = (mousePos - head.SimPosition) * Dir;
float rot = MathUtils.VectorToAngle(dir);
var neckJoint = GetJointBetweenLimbs(LimbType.Head, LimbType.Torso);
if (neckJoint != null)
{
float offset = MathUtils.WrapAnglePi(GetLimb(LimbType.Torso).body.Rotation);
float lowerLimit = neckJoint.LowerLimit + offset;
float upperLimit = neckJoint.UpperLimit + offset;
float min = Math.Min(lowerLimit, upperLimit);
float max = Math.Max(lowerLimit, upperLimit);
rot = Math.Clamp(rot, min, max);
}
head.body.SmoothRotate(rot, CurrentAnimationParams.HeadTorque);
Crouching = true;
character.SetInput(InputType.Crouch, hit: false, held: true);
}
private void FootIK(Limb foot, Vector2 pos, float legTorque, float footTorque, float footAngle)
@@ -64,6 +64,9 @@ namespace Barotrauma
}
}
private readonly List<Body> limbBodies = new List<Body>();
public IEnumerable<Body> LimbBodies => limbBodies;
public bool HasMultipleLimbsOfSameType => limbs != null && limbs.Length > limbDictionary.Count;
private bool frozen;
@@ -91,7 +94,7 @@ namespace Barotrauma
private bool simplePhysicsEnabled;
public Character Character => character;
protected Character character;
protected readonly Character character;
protected float strongestImpact;
@@ -108,7 +111,7 @@ namespace Barotrauma
//a movement vector that overrides targetmovement if trying to steer
//a Character to the position sent by server in multiplayer mode
protected Vector2 overrideTargetMovement;
protected Vector2? overrideTargetMovement;
protected float floorY, standOnFloorY;
protected Fixture floorFixture;
@@ -138,6 +141,12 @@ namespace Barotrauma
private Category prevCollisionCategory = Category.None;
/// <summary>
/// When the character is alive/conscious, the collider drives the character's movement and is used to sync the character's position in MP.
/// When unconscious, the ragdoll controls the movement and the collider just sticks to the main limb.
/// </summary>
public bool ColliderControlsMovement => character.CanMove;
public bool IsStuck => Limbs.Any(l => l.IsStuck);
public PhysicsBody Collider
@@ -185,7 +194,7 @@ namespace Barotrauma
Vector2 pos = collider[colliderIndex].SimPosition;
pos.Y -= collider[colliderIndex].Height * 0.5f;
pos.Y += collider[value].Height * 0.5f;
collider[value].SetTransform(pos, collider[colliderIndex].Rotation);
collider[value].SetTransformIgnoreContacts(pos, collider[colliderIndex].Rotation);
collider[value].LinearVelocity = collider[colliderIndex].LinearVelocity;
collider[value].AngularVelocity = collider[colliderIndex].AngularVelocity;
@@ -282,7 +291,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs)
{
if (limb.IsSevered || !limb.body.PhysEnabled) { continue; }
limb.body.SetTransform(Collider.SimPosition, Collider.Rotation);
limb.body.SetTransformIgnoreContacts(Collider.SimPosition, Collider.Rotation);
//reset pull joints (they may be somewhere far away if the character has moved from the position where animations were last updated)
limb.PullJointEnabled = false;
limb.PullJointWorldAnchorB = limb.SimPosition;
@@ -297,11 +306,11 @@ namespace Barotrauma
{
get
{
return (overrideTargetMovement == Vector2.Zero) ? targetMovement : overrideTargetMovement;
return overrideTargetMovement ?? targetMovement;
}
set
{
if (!MathUtils.IsValid(value)) return;
if (!MathUtils.IsValid(value)) { return; }
targetMovement.X = MathHelper.Clamp(value.X, -MAX_SPEED, MAX_SPEED);
targetMovement.Y = MathHelper.Clamp(value.Y, -MAX_SPEED, MAX_SPEED);
}
@@ -385,15 +394,16 @@ namespace Barotrauma
if (ragdollParams != null)
{
RagdollParams = ragdollParams;
if (!character.VariantOf.IsEmpty)
{
RagdollParams.TryApplyVariantScale(character.Params.VariantFile);
}
}
else
{
// Only re-equip items if the ragdoll doesn't change, because re-equiping items might throw exceptions if the limbs have changed.
items = limbs?.ToDictionary(l => l.Params, l => l.WearingItems);
}
if (character.Params.VariantFile is XDocument variantFile)
{
RagdollParams.TryApplyVariantScale(variantFile);
}
foreach (var limbParams in RagdollParams.Limbs)
{
if (!PhysicsBody.IsValidShape(limbParams.Radius, limbParams.Height, limbParams.Width))
@@ -430,18 +440,13 @@ namespace Barotrauma
if (character.IsHusk && character.Params.UseHuskAppendage)
{
bool inEditor = false;
#if CLIENT
inEditor = Screen.Selected == GameMain.CharacterEditorScreen;
#endif
var characterPrefab = CharacterPrefab.FindByFilePath(character.ConfigPath);
if (characterPrefab?.ConfigElement != null)
{
var mainElement = characterPrefab.ConfigElement;
foreach (var huskAppendage in mainElement.GetChildElements("huskappendage"))
{
if (!inEditor && huskAppendage.GetAttributeBool("onlyfromafflictions", false)) { continue; }
if (huskAppendage.GetAttributeBool("onlyfromafflictions", false)) { continue; }
Identifier afflictionIdentifier = huskAppendage.GetAttributeIdentifier("affliction", Identifier.Empty);
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out AfflictionPrefab affliction) ||
@@ -452,7 +457,7 @@ namespace Barotrauma
}
else
{
AfflictionHusk.AttachHuskAppendage(character, matchingAffliction, huskAppendage, ragdoll: this);
AfflictionHusk.AttachHuskAppendage(character, matchingAffliction, huskedSpeciesName: character.SpeciesName, huskAppendage, ragdoll: this);
}
}
}
@@ -478,7 +483,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Invalid collider dimensions: " + cParams.Name);
break; ;
}
var body = new PhysicsBody(cParams);
var body = new PhysicsBody(cParams, findNewContacts: false);
collider.Add(body);
body.UserData = character;
body.FarseerBody.OnCollision += OnLimbCollision;
@@ -520,7 +525,7 @@ namespace Barotrauma
{
if (joint == null) { continue; }
float angle = (joint.LowerLimit + joint.UpperLimit) / 2.0f;
joint.LimbB?.body?.SetTransform(
joint.LimbB?.body?.SetTransformIgnoreContacts(
(joint.WorldAnchorA - MathUtils.RotatePointAroundTarget(joint.LocalAnchorB, Vector2.Zero, joint.BodyA.Rotation + angle, true)),
joint.BodyA.Rotation + angle);
}
@@ -528,11 +533,13 @@ namespace Barotrauma
protected void CreateLimbs()
{
limbBodies.Clear();
limbs?.ForEach(l => l.Remove());
Mass = 0;
DebugConsole.Log($"Creating limbs from {RagdollParams.Name}.");
limbDictionary = new Dictionary<LimbType, Limb>();
limbs = new Limb[RagdollParams.Limbs.Count];
RagdollParams.Limbs.ForEach(l => AddLimb(l));
RagdollParams.Limbs.ForEach(AddLimb);
if (limbs.Contains(null)) { return; }
SetupDrawOrder();
}
@@ -549,11 +556,11 @@ namespace Barotrauma
/// <summary>
/// Resets the serializable data to the currently selected ragdoll params.
/// Force reloading always loads the xml stored on the disk.
/// Always loads the xml stored on the disk.
/// </summary>
public void ResetRagdoll(bool forceReload = false)
public void ResetRagdoll()
{
RagdollParams.Reset(forceReload);
RagdollParams.Reset(forceReload: true);
ResetJoints();
ResetLimbs();
}
@@ -577,7 +584,7 @@ namespace Barotrauma
public void AddJoint(JointParams jointParams)
{
if (!checkLimbIndex(jointParams.Limb2, "Limb1") || !checkLimbIndex(jointParams.Limb2, "Limb2"))
if (!checkLimbIndex(jointParams.Limb1, "Limb1") || !checkLimbIndex(jointParams.Limb2, "Limb2"))
{
return;
}
@@ -621,6 +628,7 @@ namespace Barotrauma
{
throw new Exception($"Failed to add a limb to the character \"{Character?.ConfigPath ?? "null"}\" (limb index {ID} out of bounds). The ragdoll file may be configured incorrectly.");
}
limbBodies.Add(limb.body.FarseerBody);
Limbs[ID] = limb;
Mass += limb.Mass;
if (!limbDictionary.ContainsKey(limb.type)) { limbDictionary.Add(limb.type, limb); }
@@ -632,6 +640,7 @@ namespace Barotrauma
limb.body.FarseerBody.OnCollision += OnLimbCollision;
Array.Resize(ref limbs, Limbs.Length + 1);
Limbs[Limbs.Length - 1] = limb;
limbBodies.Add(limb.body.FarseerBody);
Mass += limb.Mass;
if (!limbDictionary.ContainsKey(limb.type)) { limbDictionary.Add(limb.type, limb); }
SetupDrawOrder();
@@ -639,14 +648,14 @@ namespace Barotrauma
public void RemoveLimb(Limb limb)
{
if (!Limbs.Contains(limb)) return;
if (!Limbs.Contains(limb)) { return; }
Limb[] newLimbs = new Limb[Limbs.Length - 1];
int i = 0;
foreach (Limb existingLimb in Limbs)
{
if (existingLimb == limb) continue;
if (existingLimb == limb) { continue; }
newLimbs[i] = existingLimb;
i++;
}
@@ -684,8 +693,10 @@ namespace Barotrauma
LimbJoints = newJoints;
}
SubtractMass(limb);
limbBodies.Remove(limb.body.FarseerBody);
limb.Remove();
System.Diagnostics.Debug.Assert(!limbs.Contains(limb));
System.Diagnostics.Debug.Assert(limbs.None(l => l.Removed));
foreach (LimbJoint limbJoint in attachedJoints)
{
GameMain.World.Remove(limbJoint.Joint);
@@ -819,9 +830,10 @@ namespace Barotrauma
{
if (character.DisableImpactDamageTimer > 0.0f) { return; }
if (f2.Body?.UserData is Item)
if (f2.Body?.UserData is Item &&
f2.Body.BodyType != BodyType.Static)
{
//no impact damage from items
//no impact damage from items with a non-static body
//items that can impact characters (melee weapons, projectiles) should handle the damage themselves
return;
}
@@ -1073,7 +1085,7 @@ namespace Barotrauma
Vector2 moveDir = hullDiff.LengthSquared() < 0.001f ? Vector2.UnitY : Vector2.Normalize(hullDiff);
//find a position 32 units away from the hull
if (MathUtils.GetLineRectangleIntersection(
if (MathUtils.GetLineWorldRectangleIntersection(
newHull.WorldPosition,
newHull.WorldPosition + moveDir * Math.Max(newHull.Rect.Width, newHull.Rect.Height),
new Rectangle(newHull.WorldRect.X - 32, newHull.WorldRect.Y + 32, newHull.WorldRect.Width + 64, newHull.Rect.Height + 64),
@@ -1294,6 +1306,11 @@ namespace Barotrauma
}
}
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
Collider.LinearVelocity = new Vector2(
NetConfig.Quantize(Collider.LinearVelocity.X, -MaxVel, MaxVel, 12),
NetConfig.Quantize(Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12));
if (forceStanding)
{
inWater = false;
@@ -1400,7 +1417,12 @@ namespace Barotrauma
limb.Update(deltaTime);
}
if (!inWater && character.AllowInput && levitatingCollider)
bool isAttachedToController =
character.SelectedItem?.GetComponent<Items.Components.Controller>() is { } controller &&
controller.User == character &&
controller.IsAttachedUser(controller.User);
if (!inWater && character.AllowInput && levitatingCollider && !isAttachedToController)
{
if (onGround && Collider.LinearVelocity.Y > -ImpactTolerance)
{
@@ -1433,7 +1455,7 @@ namespace Barotrauma
else
{
// Falling -> ragdoll briefly if we are not moving at all, because we are probably stuck.
if (Collider.LinearVelocity == Vector2.Zero)
if (Collider.LinearVelocity == Vector2.Zero && !character.IsRemotePlayer)
{
character.IsRagdolled = true;
if (character.IsBot)
@@ -1448,6 +1470,30 @@ namespace Barotrauma
forceNotStanding = false;
}
/// <summary>
/// Update the logic that needs to run when the ragdoll is what controls the character's movement instead of the collider <see cref="ColliderControlsMovement"/>
/// (making the collider stick to the ragdoll's main limb).
/// </summary>
protected void UpdateRagdollControlsMovement()
{
levitatingCollider = false;
Collider.FarseerBody.FixedRotation = false;
if (Collider.Enabled)
{
//deactivating the collider -> make the main limb inherit the collider's velocity because it'll control the movement now
MainLimb.body.LinearVelocity = Collider.LinearVelocity;
Collider.Enabled = false;
}
Collider.LinearVelocity = MainLimb.LinearVelocity;
Collider.SetTransformIgnoreContacts(MainLimb.SimPosition, MainLimb.Rotation);
//reset pull joints to prevent the character from "hanging" mid-air if pull joints had been active when the character was still moving
//(except when dragging, then we need the pull joints)
if (!Draggable || character.SelectedBy == null)
{
ResetPullJoints();
}
}
private void CheckBodyInRest(float deltaTime)
{
if (SimplePhysicsEnabled) { return; }
@@ -1910,7 +1956,7 @@ namespace Barotrauma
}
else
{
Collider.SetTransform(simPosition, Collider.Rotation);
Collider.SetTransformIgnoreContacts(simPosition, Collider.Rotation);
}
if (!MathUtils.NearlyEqual(limbMoveAmount, Vector2.Zero))
@@ -2009,7 +2055,7 @@ namespace Barotrauma
}
else
{
limb.body.SetTransform(movePos, rotation);
limb.body.SetTransformIgnoreContacts(movePos, rotation);
limb.PullJointWorldAnchorB = limb.PullJointWorldAnchorA;
limb.PullJointEnabled = false;
}
@@ -2084,7 +2130,7 @@ namespace Barotrauma
partial void UpdateNetPlayerPositionProjSpecific(float deltaTime, float lowestSubPos);
private void UpdateNetPlayerPosition(float deltaTime)
{
if (GameMain.NetworkMember == null) return;
if (GameMain.NetworkMember == null) { return; }
float lowestSubPos = float.MaxValue;
if (Submarine.Loaded.Any())
@@ -2113,26 +2159,48 @@ namespace Barotrauma
/// <summary>
/// Note that if there are multiple limbs of the same type, only the first (valid) limb is returned.
/// </summary>
public Limb GetLimb(LimbType limbType, bool excludeSevered = true)
/// <param name="limbType"></param>
/// <param name="excludeSevered">Should we filter out severed limbs?</param>
/// <param name="useSecondaryType">Should we target limbs with secondary type instead of (primary) type?</param>
/// <param name="excludeLimbsWithSecondaryType">Should we filter out all limbs with a secondary type something else than "None"?</param>
/// <returns></returns>
public Limb GetLimb(LimbType limbType, bool excludeSevered = true, bool excludeLimbsWithSecondaryType = false, bool useSecondaryType = false)
{
if (limbDictionary.TryGetValue(limbType, out Limb limb))
Limb limb = null;
if (!HasMultipleLimbsOfSameType && !useSecondaryType && !excludeLimbsWithSecondaryType)
{
if (excludeSevered && limb.IsSevered)
// Faster method, but doesn't work when there's multiple limbs of the same type or if we want to seek/exclude limbs with different conditions.
if (limbDictionary.TryGetValue(limbType, out limb))
{
limb = null;
}
if (limb.Removed)
{
limb = null;
}
if (excludeSevered && limb is { IsSevered: true } )
{
limb = null;
}
}
}
if (limb == null && HasMultipleLimbsOfSameType)
if (limb == null)
{
// Didn't find a (valid) limb of the matching type. If there's multiple limbs of the same type, check the other limbs.
// Didn't seek or find a (valid) limb of the matching type. If there's multiple limbs of the same type, check the other limbs.
foreach (var l in limbs)
{
if (l.type != limbType) { continue; }
if (!excludeSevered || !l.IsSevered)
if (l.Removed) { continue; }
if (useSecondaryType)
{
limb = l;
break;
if (l.Params.SecondaryType != limbType) { continue; }
}
else if (l.type != limbType)
{
continue;
}
if (excludeSevered && l.IsSevered) { continue; }
if (excludeLimbsWithSecondaryType && l.Params.SecondaryType != LimbType.None) { continue; }
// Found a valid and match
limb = l;
break;
}
}
return limb;
@@ -2220,6 +2288,7 @@ namespace Barotrauma
}
limbs = null;
}
limbBodies.Clear();
if (collider != null)
{