v1.4.4.1 (Blood in the Water Update)

This commit is contained in:
Regalis11
2024-04-24 18:09:05 +03:00
parent 89b91d1c3e
commit ff1b8951a7
397 changed files with 15250 additions and 6479 deletions
@@ -32,6 +32,48 @@ namespace Barotrauma
public abstract GroundedMovementParams RunParams { get; set; }
public abstract SwimParams SwimSlowParams { get; set; }
public abstract SwimParams SwimFastParams { get; set; }
protected class AnimSwap
{
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 AnimSwap(AnimationParams temporaryAnimation, float priority)
{
AnimationType = temporaryAnimation.AnimationType;
TemporaryAnimation = temporaryAnimation;
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>();
protected readonly HashSet<AnimationType> expiredAnimations = new HashSet<AnimationType>();
public AnimationParams CurrentAnimationParams
{
@@ -87,7 +129,11 @@ namespace Barotrauma
}
public bool CanWalk => RagdollParams.CanWalk;
public bool IsMovingBackwards => !InWater && Math.Sign(targetMovement.X) == -Math.Sign(Dir) && CurrentAnimationParams is not FishGroundedParams { Flip: false };
public bool IsMovingBackwards =>
!InWater &&
Math.Sign(targetMovement.X) == -Math.Sign(Dir) &&
CurrentAnimationParams is not FishGroundedParams { Flip: false } &&
Anim != Animation.Climbing;
// TODO: define death anim duration in XML
protected float deathAnimTimer, deathAnimDuration = 5.0f;
@@ -177,8 +223,14 @@ namespace Barotrauma
public float WalkPos { get; protected set; }
public AnimController(Character character, string seed, RagdollParams ragdollParams = null) : base(character, seed, ragdollParams) { }
public void UpdateAnimations(float deltaTime)
{
UpdateTemporaryAnimations(deltaTime);
UpdateAnim(deltaTime);
}
public abstract void UpdateAnim(float deltaTime);
protected abstract void UpdateAnim(float deltaTime);
public abstract void DragCharacter(Character target, float deltaTime);
@@ -253,23 +305,26 @@ namespace Barotrauma
switch (type)
{
case AnimationType.Walk:
return WalkParams;
return CanWalk ? WalkParams : null;
case AnimationType.Run:
return RunParams;
return CanWalk ? RunParams : null;
case AnimationType.Crouch:
if (this is HumanoidAnimController humanAnimController)
{
return humanAnimController.HumanCrouchParams;
}
throw new NotImplementedException(type.ToString());
else
{
DebugConsole.ThrowError($"Animation params of type {type} not implemented for non-humanoids!");
return null;
}
case AnimationType.SwimSlow:
return SwimSlowParams;
case AnimationType.SwimFast:
return SwimFastParams;
case AnimationType.NotDefined:
return null;
default:
throw new NotImplementedException(type.ToString());
return null;
}
}
@@ -376,7 +431,7 @@ namespace Barotrauma
private Direction previousDirection;
private readonly Vector2[] transformedHandlePos = new Vector2[2];
//TODO: refactor this method, it's way too convoluted
public void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f, bool aimMelee = false)
public void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 itemPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle = 0.0f, bool aimMelee = false, Vector2? targetPos = null)
{
aimingMelee = aimMelee;
if (character.Stun > 0.0f || character.IsIncapacitated)
@@ -385,22 +440,20 @@ namespace Barotrauma
}
//calculate the handle positions
Matrix itemTransfrom = Matrix.CreateRotationZ(item.body.Rotation);
transformedHandlePos[0] = Vector2.Transform(handlePos[0], itemTransfrom);
transformedHandlePos[1] = Vector2.Transform(handlePos[1], itemTransfrom);
Matrix itemTransform = Matrix.CreateRotationZ(item.body.Rotation);
transformedHandlePos[0] = Vector2.Transform(handlePos[0], itemTransform);
transformedHandlePos[1] = Vector2.Transform(handlePos[1], itemTransform);
Limb torso = GetLimb(LimbType.Torso) ?? MainLimb;
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
Vector2 itemPos = aim ? aimPos : holdPos;
var controller = character.SelectedItem?.GetComponent<Controller>();
bool usingController = controller != null && !controller.AllowAiming;
bool usingController = controller is { AllowAiming: false };
if (!usingController)
{
controller = character.SelectedSecondaryItem?.GetComponent<Controller>();
usingController = controller != null && !controller.AllowAiming;
usingController = controller is { AllowAiming: false };
}
bool isClimbing = character.IsClimbing && Math.Abs(character.AnimController.TargetMovement.Y) > 0.01f;
float itemAngle;
@@ -408,15 +461,17 @@ namespace Barotrauma
float torsoRotation = torso.Rotation;
Item rightHandItem = character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand);
bool equippedInRightHand = rightHandItem == item && rightHand != null && !rightHand.IsSevered;
bool equippedInRightHand = rightHandItem == item && rightHand is { IsSevered: false };
Item leftHandItem = character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand);
bool equippedInLefthand = leftHandItem == item && leftHand != null && !leftHand.IsSevered;
bool equippedInLeftHand = leftHandItem == item && leftHand is { IsSevered: false };
if (aim && !isClimbing && !usingController && character.Stun <= 0.0f && itemPos != Vector2.Zero && !character.IsIncapacitated)
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.SmoothedCursorPosition);
Vector2 diff = holdable.Aimable ?
(mousePos - AimSourceSimPos) * Dir :
targetPos ??= ConvertUnits.ToSimUnits(character.SmoothedCursorPosition);
Vector2 diff = holdable.Aimable ?
(targetPos.Value - AimSourceSimPos) * Dir :
MathUtils.RotatePoint(Vector2.UnitX, torsoRotation);
holdAngle = MathUtils.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torsoRotation * Dir;
holdAngle += GetAimWobble(rightHand, leftHand, item);
itemAngle = torsoRotation + holdAngle * Dir;
@@ -424,7 +479,7 @@ namespace Barotrauma
if (holdable.ControlPose)
{
//if holding two items that should control the characters' pose, let the item in the right hand do it
bool anotherItemControlsPose = equippedInLefthand && rightHandItem != item && (rightHandItem?.GetComponent<Holdable>()?.ControlPose ?? false);
bool anotherItemControlsPose = equippedInLeftHand && rightHandItem != item && (rightHandItem?.GetComponent<Holdable>()?.ControlPose ?? false);
if (!anotherItemControlsPose && TargetMovement == Vector2.Zero && inWater)
{
torso.body.AngularVelocity -= torso.body.AngularVelocity * 0.1f;
@@ -441,7 +496,7 @@ namespace Barotrauma
{
itemAngle = rightHand.Rotation + holdAngle * Dir;
}
else if (equippedInLefthand)
else if (equippedInLeftHand)
{
itemAngle = leftHand.Rotation + holdAngle * Dir;
}
@@ -465,7 +520,7 @@ namespace Barotrauma
transformedHoldPos = rightHand.PullJointWorldAnchorA - transformedHandlePos[0];
itemAngle = rightHand.Rotation + (holdAngle - rightHand.Params.GetSpriteOrientation() + MathHelper.PiOver2) * Dir;
}
else if (equippedInLefthand)
else if (equippedInLeftHand)
{
transformedHoldPos = leftHand.PullJointWorldAnchorA - transformedHandlePos[1];
itemAngle = leftHand.Rotation + (holdAngle - leftHand.Params.GetSpriteOrientation() + MathHelper.PiOver2) * Dir;
@@ -478,7 +533,7 @@ namespace Barotrauma
transformedHoldPos = rightShoulder.WorldAnchorA;
rightHand.Disabled = true;
}
if (equippedInLefthand)
if (equippedInLeftHand)
{
if (leftShoulder == null) { return; }
transformedHoldPos = leftShoulder.WorldAnchorA;
@@ -803,5 +858,260 @@ namespace Barotrauma
public void StopUsingItem() => StopAnimation(Animation.UsingItem);
public void StopClimbing() => StopAnimation(Animation.Climbing);
private readonly Dictionary<AnimationType, AnimationParams> defaultAnimations = new Dictionary<AnimationType, AnimationParams>();
/// <summary>
/// Loads an animation (variation) that automatically resets in 0.1s, unless triggered again.
/// Meant e.g. for triggering animations in status effects, without having to worry about resetting them.
/// </summary>
public bool TryLoadTemporaryAnimation(StatusEffect.AnimLoadInfo animLoadInfo, bool throwErrors)
{
AnimationType animType = animLoadInfo.Type;
if (tempAnimations.TryGetValue(animType, out AnimSwap animSwap))
{
if (animLoadInfo.File.TryGet(out string fileName) && animSwap.TemporaryAnimation.FileNameWithoutExtension.Equals(fileName, StringComparison.OrdinalIgnoreCase))
{
// Already loaded, keep active
animSwap.IsActive = true;
return true;
}
else if (animLoadInfo.File.TryGet(out ContentPath contentPath) && animSwap.TemporaryAnimation.Path == contentPath)
{
// Already loaded, keep active
animSwap.IsActive = true;
return true;
}
else
{
if (animSwap.Priority >= animLoadInfo.Priority)
{
// If the priority of the current animation is higher than the new animation, just return and do nothing.
// Returning false would tell the status effect to not try again, which is not what we want here, which is why we fake a bit with the return value.
return true;
}
else
{
// Override any previous animations of the same type.
tempAnimations.Remove(animType);
}
}
}
AnimationParams defaultAnimation = GetAnimationParamsFromType(animType);
if (defaultAnimation == null) { return false; }
if (!TryLoadAnimation(animType, animLoadInfo.File, out AnimationParams tempParams, throwErrors)) { return false; }
// Store the default animation, if not yet stored. There should always be just one of the same type.
defaultAnimations.TryAdd(animType, defaultAnimation);
tempAnimations.Add(animType, new AnimSwap(tempParams, animLoadInfo.Priority));
return true;
}
private void UpdateTemporaryAnimations(float deltaTime)
{
if (tempAnimations.None()) { return; }
foreach ((AnimationType animationType, AnimSwap animSwap) in tempAnimations)
{
if (!animSwap.IsActive)
{
if (defaultAnimations.TryGetValue(animSwap.AnimationType, out AnimationParams defaultAnimation))
{
TrySwapAnimParams(defaultAnimation);
expiredAnimations.Add(animationType);
}
else
{
DebugConsole.ThrowError($"[AnimController] Failed to find the default animation parameters of type {animSwap.AnimationType}. Cannot swap back the default animations!");
tempAnimations.Clear();
}
}
}
foreach (AnimationType anim in expiredAnimations)
{
tempAnimations.Remove(anim);
}
expiredAnimations.Clear();
foreach (AnimSwap animSwap in tempAnimations.Values)
{
animSwap.Update(deltaTime);
}
}
/// <summary>
/// Loads animations. Non-permanent (= resets on load).
/// </summary>
public bool TryLoadAnimation(AnimationType animationType, Either<string, ContentPath> file, out AnimationParams animParams, bool throwErrors)
{
animParams = null;
if (character.IsHumanoid && this is HumanoidAnimController humanAnimController)
{
switch (animationType)
{
case AnimationType.Walk:
humanAnimController.WalkParams = HumanWalkParams.GetAnimParams(character, file, throwErrors);
animParams = humanAnimController.WalkParams;
break;
case AnimationType.Run:
humanAnimController.RunParams = HumanRunParams.GetAnimParams(character, file, throwErrors);
animParams = humanAnimController.RunParams;
break;
case AnimationType.Crouch:
humanAnimController.HumanCrouchParams = HumanCrouchParams.GetAnimParams(character, file, throwErrors);
animParams = humanAnimController.HumanCrouchParams;
break;
case AnimationType.SwimSlow:
humanAnimController.SwimSlowParams = HumanSwimSlowParams.GetAnimParams(character, file, throwErrors);
animParams = humanAnimController.SwimSlowParams;
break;
case AnimationType.SwimFast:
humanAnimController.SwimFastParams = HumanSwimFastParams.GetAnimParams(character, file, throwErrors);
animParams = humanAnimController.SwimFastParams;
break;
default:
DebugConsole.ThrowError($"[AnimController] Animation of type {animationType} not implemented!");
break;
}
}
else
{
switch (animationType)
{
case AnimationType.Walk:
if (CanWalk)
{
character.AnimController.WalkParams = FishWalkParams.GetAnimParams(character, file, throwErrors);
animParams = character.AnimController.WalkParams;
}
break;
case AnimationType.Run:
if (CanWalk)
{
character.AnimController.RunParams = FishRunParams.GetAnimParams(character, file, throwErrors);
animParams = character.AnimController.RunParams;
}
break;
case AnimationType.SwimSlow:
character.AnimController.SwimSlowParams = FishSwimSlowParams.GetAnimParams(character, file, throwErrors);
animParams = character.AnimController.SwimSlowParams;
break;
case AnimationType.SwimFast:
character.AnimController.SwimFastParams = FishSwimFastParams.GetAnimParams(character, file, throwErrors);
animParams = character.AnimController.SwimFastParams;
break;
default:
DebugConsole.ThrowError($"[AnimController] Animation of type {animationType} not implemented!");
break;
}
}
bool success = animParams != null;
if (!file.TryGet(out string fileName))
{
if (file.TryGet(out ContentPath contentPath))
{
fileName = contentPath.Value;
if (success)
{
success = contentPath == animParams.Path;
}
}
}
else
{
if (success)
{
success = animParams.FileNameWithoutExtension.Equals(fileName, StringComparison.OrdinalIgnoreCase);
}
}
if (success)
{
DebugConsole.NewMessage($"Animation {fileName} successfully loaded for {character.DisplayName}", Color.LightGreen, debugOnly: true);
}
else if (throwErrors)
{
DebugConsole.ThrowError($"Animation {fileName} for {character.DisplayName} could not be loaded!");
}
return success;
}
/// <summary>
/// Simply swaps existing animation parameters as current parameters.
/// </summary>
protected bool TrySwapAnimParams(AnimationParams newParams)
{
AnimationType animationType = newParams.AnimationType;
if (character.IsHumanoid && this is HumanoidAnimController humanAnimController)
{
switch (animationType)
{
case AnimationType.Walk:
if (newParams is HumanWalkParams newWalkParams)
{
humanAnimController.WalkParams = newWalkParams;
}
return true;
case AnimationType.Run:
if (newParams is HumanRunParams newRunParams)
{
humanAnimController.HumanRunParams = newRunParams;
}
break;
case AnimationType.Crouch:
if (newParams is HumanCrouchParams newCrouchParams)
{
humanAnimController.HumanCrouchParams = newCrouchParams;
}
return true;
case AnimationType.SwimSlow:
if (newParams is HumanSwimSlowParams newSwimSlowParams)
{
humanAnimController.HumanSwimSlowParams = newSwimSlowParams;
}
return true;
case AnimationType.SwimFast:
if (newParams is HumanSwimFastParams newSwimFastParams)
{
humanAnimController.HumanSwimFastParams = newSwimFastParams;
}
return true;
default:
DebugConsole.ThrowError($"[AnimController] Animation of type {animationType} not implemented!");
return false;
}
}
else
{
switch (animationType)
{
case AnimationType.Walk:
if (newParams is FishWalkParams walkParams)
{
character.AnimController.WalkParams = walkParams;
}
return true;
case AnimationType.Run:
if (newParams is FishRunParams runParams)
{
character.AnimController.RunParams = runParams;
}
return true;
case AnimationType.SwimSlow:
if (newParams is FishSwimSlowParams swimSlowParams)
{
character.AnimController.SwimSlowParams = swimSlowParams;
}
return true;
case AnimationType.SwimFast:
if (newParams is FishSwimFastParams swimFastParams)
{
character.AnimController.SwimFastParams = swimFastParams;
}
return true;
default:
DebugConsole.ThrowError($"[AnimController] Animation of type {animationType} not implemented!");
break;
}
}
return false;
}
}
}
@@ -22,11 +22,7 @@ namespace Barotrauma
{
if (_ragdollParams == null)
{
_ragdollParams = FishRagdollParams.GetDefaultRagdollParams(character.SpeciesName);
if (!character.VariantOf.IsEmpty)
{
_ragdollParams.ApplyVariantScale(character.Params.VariantFile);
}
_ragdollParams = FishRagdollParams.GetDefaultRagdollParams(character);
}
return _ragdollParams;
}
@@ -133,7 +129,7 @@ namespace Barotrauma
public FishAnimController(Character character, string seed, FishRagdollParams ragdollParams = null) : base(character, seed, ragdollParams) { }
public override void UpdateAnim(float deltaTime)
protected override void UpdateAnim(float deltaTime)
{
//wait a bit for the ragdoll to "settle" (for joints to force the limbs to appropriate positions) before starting to animate
if (Timing.TotalTime - character.SpawnTime < 0.1f) { return; }
@@ -145,7 +141,7 @@ namespace Barotrauma
}
var mainLimb = MainLimb;
levitatingCollider = !IsHanging;
levitatingCollider = !IsHangingWithRope;
if (!character.CanMove)
{
@@ -1011,15 +1007,7 @@ namespace Barotrauma
{
//make sure the angle "has the same number of revolutions" as the reference limb
//(e.g. we don't want to rotate the legs to 0 if the torso is at 360, because that'd blow up the hip joints)
while (referenceLimb.Rotation - angle > MathHelper.TwoPi)
{
angle += MathHelper.TwoPi;
}
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
{
angle -= MathHelper.TwoPi;
}
angle = referenceLimb.body.WrapAngleToSameNumberOfRevolutions(angle);
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
}
@@ -10,7 +10,7 @@ namespace Barotrauma
{
class HumanoidAnimController : AnimController
{
private const float SteepestWalkableSlopeAngleDegrees = 50f;
private const float SteepestWalkableSlopeAngleDegrees = 55f;
private const float SlowlyWalkableSlopeAngleDegrees = 30f;
private static readonly float SteepestWalkableSlopeNormalX =
@@ -36,7 +36,7 @@ namespace Barotrauma
{
if (_ragdollParams == null)
{
_ragdollParams = RagdollParams.GetDefaultRagdollParams<HumanRagdollParams>(character.SpeciesName);
_ragdollParams = HumanRagdollParams.GetDefaultRagdollParams(character);
}
return _ragdollParams;
}
@@ -248,12 +248,12 @@ namespace Barotrauma
GetLimb(footType).PullJointLocalAnchorA);
}
public override void UpdateAnim(float deltaTime)
protected override void UpdateAnim(float deltaTime)
{
if (Frozen) { return; }
if (MainLimb == null) { return; }
levitatingCollider = !IsHanging;
levitatingCollider = !IsHangingWithRope;
if (onGround && character.CanMove)
{
if ((character.SelectedItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
@@ -396,7 +396,9 @@ namespace Barotrauma
if (SimplePhysicsEnabled)
{
UpdateStandingSimple();
IsHanging = false;
StopHangingWithRope();
StopHoldingToRope();
StopGettingDraggedWithRope();
return;
}
@@ -490,7 +492,21 @@ namespace Barotrauma
aiming = false;
wasAimingMelee = aimingMelee;
aimingMelee = false;
IsHanging = IsHanging && character.IsRagdolled;
if (!shouldHangWithRope)
{
StopHangingWithRope();
}
if (!shouldHoldToRope)
{
StopHoldingToRope();
}
if (!shouldBeDraggedWithRope)
{
StopGettingDraggedWithRope();
}
shouldHoldToRope = false;
shouldHangWithRope = false;
shouldBeDraggedWithRope = false;
}
void UpdateStanding()
@@ -686,6 +702,16 @@ namespace Barotrauma
if (!onGround)
{
const float MaxFootVelocityDiff = 5.0f;
const float MaxFootVelocityDiffSqr = MaxFootVelocityDiff * MaxFootVelocityDiff;
//if the feet have a significantly different velocity from the main limb, try moving them back to a neutral pose below the torso
//this can happen e.g. when jumping over an obstacle: the feet can have a large upwards velocity during the walk/run animation,
//and just "letting go of the animations" would let them keep moving upwards, twisting the character to a weird pose
if ((leftFoot != null && (MainLimb.LinearVelocity - leftFoot.LinearVelocity).LengthSquared() > MaxFootVelocityDiffSqr) ||
(rightFoot != null && (MainLimb.LinearVelocity - rightFoot.LinearVelocity).LengthSquared() > MaxFootVelocityDiffSqr))
{
UpdateFallingProne(10.0f, moveHands: false, moveTorso: false, moveLegs: true);
}
return;
}
@@ -786,7 +812,12 @@ namespace Barotrauma
}
else
{
footPos = new Vector2(colliderPos.X + stepSize.X * i * 0.2f, colliderPos.Y - 0.1f);
float footPosX = stepSize.X * i * 0.2f;
if (CurrentGroundedParams.StepSizeWhenStanding != Vector2.Zero)
{
footPosX = Math.Sign(stepSize.X) * CurrentGroundedParams.StepSizeWhenStanding.X * i;
}
footPos = new Vector2(colliderPos.X + footPosX, colliderPos.Y - 0.1f);
}
if (Stairs == null && !onSlopeThatMakesSlow)
{
@@ -1519,6 +1550,8 @@ namespace Barotrauma
{
torso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
targetTorso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
//the pumping animation can sometimes cause impact damage, prevent that by briefly disabling it
target.DisableImpactDamageTimer = 0.15f;
cprPumpTimer = 0;
if (skill < CPRSettings.Active.DamageSkillThreshold)
@@ -1727,7 +1760,7 @@ namespace Barotrauma
if (targetLimb.type == LimbType.Torso || targetLimb == target.AnimController.MainLimb)
{
pullLimb.PullJointMaxForce = 5000.0f;
if (!character.HasAbilityFlag(AbilityFlags.MoveNormallyWhileDragging))
if (!character.CanRunWhileDragging())
{
targetMovement *= MathHelper.Clamp(Mass / target.Mass, 0.5f, 1.0f);
}
@@ -1811,7 +1844,7 @@ namespace Barotrauma
}
//limit movement if moving away from the target
if (!character.HasAbilityFlag(AbilityFlags.MoveNormallyWhileDragging) && Vector2.Dot(target.WorldPosition - WorldPosition, targetMovement) < 0)
if (!character.CanRunWhileDragging() && Vector2.Dot(target.WorldPosition - WorldPosition, targetMovement) < 0)
{
targetMovement *= MathHelper.Clamp(1.5f - dist, 0.0f, 1.0f);
}
@@ -70,8 +70,8 @@ namespace Barotrauma
public bool Frozen
{
get { return frozen; }
set
{
set
{
if (frozen == value) return;
frozen = value;
@@ -81,7 +81,7 @@ namespace Barotrauma
Collider.FarseerBody.IgnoreGravity = frozen;
//Collider.PhysEnabled = !frozen;
if (frozen && MainLimb != null) MainLimb.PullJointWorldAnchorB = MainLimb.SimPosition;
if (frozen && MainLimb != null) { MainLimb.PullJointWorldAnchorB = MainLimb.SimPosition; }
}
}
@@ -109,12 +109,12 @@ 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 float floorY, standOnFloorY;
protected Fixture floorFixture;
protected Vector2 floorNormal = Vector2.UnitY;
protected float surfaceY;
protected bool inWater, headInWater;
protected bool onGround;
public bool OnGround => onGround;
@@ -128,7 +128,7 @@ namespace Barotrauma
public float ColliderHeightFromFloor => ConvertUnits.ToSimUnits(RagdollParams.ColliderHeightFromFloor) * RagdollParams.JointScale;
public Structure Stairs;
protected Direction dir;
public Direction TargetDir;
@@ -153,7 +153,7 @@ namespace Barotrauma
collider = null;
try
{
collider = this.collider?[index];
collider = this.collider?[index];
return true;
}
catch
@@ -322,7 +322,7 @@ namespace Barotrauma
impactTolerance = RagdollParams.ImpactTolerance;
if (character.Params.VariantFile != null)
{
float? tolerance = character.Params.VariantFile.Root.GetChildElement("ragdoll")?.GetAttributeFloat("impacttolerance", impactTolerance.Value);
float? tolerance = character.Params.VariantFile.GetRootExcludingOverride().GetChildElement("ragdoll")?.GetAttributeFloat("impacttolerance", impactTolerance.Value);
if (tolerance.HasValue)
{
impactTolerance = tolerance;
@@ -334,7 +334,8 @@ namespace Barotrauma
}
public bool Draggable => RagdollParams.Draggable;
public bool CanEnterSubmarine => RagdollParams.CanEnterSubmarine;
public CanEnterSubmarine CanEnterSubmarine => RagdollParams.CanEnterSubmarine;
public float Dir => dir == Direction.Left ? -1.0f : 1.0f;
@@ -384,6 +385,10 @@ namespace Barotrauma
if (ragdollParams != null)
{
RagdollParams = ragdollParams;
if (!character.VariantOf.IsEmpty)
{
RagdollParams.TryApplyVariantScale(character.Params.VariantFile);
}
}
else
{
@@ -696,21 +701,34 @@ namespace Barotrauma
public bool OnLimbCollision(Fixture f1, Fixture f2, Contact contact)
{
if (f2.Body.UserData is Submarine && character.Submarine == (Submarine)f2.Body.UserData) { return false; }
if (f2.UserData is Hull && character.Submarine != null) { return false; }
if (f2.Body.UserData is Submarine submarine && character.Submarine == submarine) { return false; }
if (f2.UserData is Hull)
{
if (character.Submarine != null)
{
return false;
}
if (CanEnterSubmarine == CanEnterSubmarine.Partial)
{
//collider collides with hulls to prevent the character going fully inside the sub, limbs don't
return
f1.Body == Collider.FarseerBody ||
(f1.Body.UserData is Limb limb && !limb.Params.CanEnterSubmarine);
}
}
//using the velocity of the limb would make the impact damage more realistic,
//but would also make it harder to edit the animations because the forces/torques
//would all have to be balanced in a way that prevents the character from doing
//impact damage to itself
Vector2 velocity = Collider.LinearVelocity;
if (character.Submarine == null && f2.Body.UserData is Submarine)
if (character.Submarine == null && f2.Body.UserData is Submarine sub)
{
velocity -= ((Submarine)f2.Body.UserData).Velocity;
velocity -= sub.Velocity;
}
//always collides with bodies other than structures
if (!(f2.Body.UserData is Structure structure))
if (f2.Body.UserData is not Structure structure)
{
if (!f2.IsSensor)
{
@@ -1021,7 +1039,7 @@ namespace Barotrauma
{
foreach (Ragdoll r in list)
{
r.Update(deltaTime, cam);
r.UpdateRagdoll(deltaTime, cam);
}
}
@@ -1041,7 +1059,8 @@ namespace Barotrauma
if (newHull == currentHull) { return; }
if (!CanEnterSubmarine || (character.AIController != null && !character.AIController.CanEnterSubmarine))
if (CanEnterSubmarine == CanEnterSubmarine.False ||
(character.AIController != null && character.AIController.CanEnterSubmarine == CanEnterSubmarine.False))
{
//character is inside the sub even though it shouldn't be able to enter -> teleport it out
@@ -1066,6 +1085,11 @@ namespace Barotrauma
}
}
if (CanEnterSubmarine != CanEnterSubmarine.True)
{
return;
}
if (setSubmarine)
{
//in -> out
@@ -1075,13 +1099,13 @@ namespace Barotrauma
if (Gap.FindAdjacent(Gap.GapList.Where(g => g.Submarine == currentHull.Submarine), findPos, 150.0f) != null) { return; }
if (Limbs.Any(l => Gap.FindAdjacent(currentHull.ConnectedGaps, l.WorldPosition, ConvertUnits.ToDisplayUnits(l.body.GetSize().Combine())) != null)) { return; }
character.MemLocalState?.Clear();
Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity, detachProjectiles: false);
Teleport(ConvertUnits.ToSimUnits(currentHull.Submarine.Position), currentHull.Submarine.Velocity);
}
//out -> in
else if (currentHull == null && newHull.Submarine != null)
{
character.MemLocalState?.Clear();
Teleport(-ConvertUnits.ToSimUnits(newHull.Submarine.Position), -newHull.Submarine.Velocity, detachProjectiles: false);
Teleport(-ConvertUnits.ToSimUnits(newHull.Submarine.Position), -newHull.Submarine.Velocity);
}
//from one sub to another
else if (newHull != null && currentHull != null && newHull.Submarine != currentHull.Submarine)
@@ -1089,7 +1113,7 @@ namespace Barotrauma
character.MemLocalState?.Clear();
Vector2 newSubPos = newHull.Submarine == null ? Vector2.Zero : newHull.Submarine.Position;
Vector2 prevSubPos = currentHull.Submarine == null ? Vector2.Zero : currentHull.Submarine.Position;
Teleport(ConvertUnits.ToSimUnits(prevSubPos - newSubPos), Vector2.Zero, detachProjectiles: false);
Teleport(ConvertUnits.ToSimUnits(prevSubPos - newSubPos), Vector2.Zero);
}
}
@@ -1156,7 +1180,7 @@ namespace Barotrauma
character.DisableImpactDamageTimer = 0.25f;
SetPosition(Collider.SimPosition + moveAmount, detachProjectiles: detachProjectiles);
SetPosition(Collider.SimPosition + moveAmount);
character.CursorPosition += moveAmount;
Collider?.UpdateDrawPosition();
@@ -1221,7 +1245,7 @@ namespace Barotrauma
public bool forceStanding;
public bool forceNotStanding;
public void Update(float deltaTime, Camera cam)
public void UpdateRagdoll(float deltaTime, Camera cam)
{
if (!character.Enabled || character.Removed || Frozen || Invalid || Collider == null || Collider.Removed) { return; }
@@ -1389,7 +1413,8 @@ namespace Barotrauma
if (floorNormal.Y is > 0f and < 1f
&& Math.Sign(movement.X) == Math.Sign(floorNormal.X))
{
slopePull = Math.Abs(movement.X * floorNormal.X / floorNormal.Y) / LevitationSpeedMultiplier;
float steepness = Math.Abs(floorNormal.X);
slopePull = Math.Abs(movement.X * steepness) / LevitationSpeedMultiplier;
}
if (Math.Abs(Collider.SimPosition.Y - targetY - slopePull) > 0.01f)
@@ -1743,7 +1768,7 @@ namespace Barotrauma
return closestFraction;
}, rayStart, rayEnd, Physics.CollisionStairs | Physics.CollisionPlatform | Physics.CollisionWall | Physics.CollisionLevel);
if (standOnFloorFixture != null && !IsHanging)
if (standOnFloorFixture != null && !IsHangingWithRope)
{
floorFixture = standOnFloorFixture;
standOnFloorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * standOnFloorFraction;
@@ -1845,7 +1870,7 @@ namespace Barotrauma
return (surfaceY, ceilingY);
}
public void SetPosition(Vector2 simPosition, bool lerp = false, bool ignorePlatforms = true, bool forceMainLimbToCollider = false, bool detachProjectiles = true)
public void SetPosition(Vector2 simPosition, bool lerp = false, bool ignorePlatforms = true, bool forceMainLimbToCollider = false, bool moveLatchers = true)
{
if (!MathUtils.IsValid(simPosition))
{
@@ -1858,10 +1883,14 @@ namespace Barotrauma
}
if (MainLimb == null) { return; }
Vector2 limbMoveAmount = forceMainLimbToCollider ? simPosition - MainLimb.SimPosition : simPosition - Collider.SimPosition;
// A Work-around for an issue with teleporting the characters:
// Detach every latcher when either one of the latchers or the target is teleported,
// because otherwise all the characters are teleported to invalid positions.
if (Character.AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null && enemyAI.LatchOntoAI.IsAttached)
// because otherwise all the characters are teleported to invalid positions.
const float ForceDeattachThreshold = 10.0f;
if (limbMoveAmount.LengthSquared() > ForceDeattachThreshold * ForceDeattachThreshold &&
Character.AIController is EnemyAIController enemyAI && enemyAI.LatchOntoAI != null && enemyAI.LatchOntoAI.IsAttached)
{
var target = enemyAI.LatchOntoAI.TargetCharacter;
if (target != null)
@@ -1874,7 +1903,6 @@ namespace Barotrauma
Character.Latchers.ForEachMod(l => l?.DeattachFromBody(reset: true));
Character.Latchers.Clear();
Vector2 limbMoveAmount = forceMainLimbToCollider ? simPosition - MainLimb.SimPosition : simPosition - Collider.SimPosition;
if (lerp)
{
Collider.TargetPosition = simPosition;
@@ -1896,15 +1924,62 @@ namespace Barotrauma
}
}
}
/// <summary>
/// Is attached to something with a rope.
/// </summary>
public bool IsHoldingToRope { get; private set; }
protected bool shouldHoldToRope;
public bool IsHanging { get; protected set; }
/// <summary>
/// Is hanging to something with a rope, so that can reel towards it. Currently only possible in water.
/// </summary>
public bool IsHangingWithRope { get; private set; }
protected bool shouldHangWithRope;
/// <summary>
/// Has someone attached to the character with a rope?
/// </summary>
public bool IsDraggedWithRope { get; private set; }
protected bool shouldBeDraggedWithRope;
public void Hang()
public void HangWithRope()
{
shouldHangWithRope = true;
IsHangingWithRope = true;
ResetPullJoints();
onGround = false;
levitatingCollider = false;
IsHanging = true;
}
public void HoldToRope()
{
shouldHoldToRope = true;
IsHoldingToRope = true;
}
public void DragWithRope()
{
shouldBeDraggedWithRope = true;
IsDraggedWithRope = true;
}
protected void StopHangingWithRope()
{
shouldHangWithRope = false;
IsHangingWithRope = false;
}
protected void StopHoldingToRope()
{
shouldHoldToRope = false;
IsHoldingToRope = false;
}
protected void StopGettingDraggedWithRope()
{
shouldBeDraggedWithRope = false;
IsDraggedWithRope = false;
}
protected void TrySetLimbPosition(Limb limb, Vector2 original, Vector2 simPosition, float rotation, bool lerp = false, bool ignorePlatforms = true)