Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop

This commit is contained in:
EvilFactory
2023-12-14 11:56:39 -03:00
376 changed files with 7775 additions and 2879 deletions
@@ -10,6 +10,17 @@ namespace Barotrauma
{
class HumanoidAnimController : AnimController
{
private const float SteepestWalkableSlopeAngleDegrees = 50f;
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 const float MaxSpeedOnStairs = 1.7f;
private const float SteepSlopePushMagnitude = MaxSpeedOnStairs;
public override RagdollParams RagdollParams
{
get { return HumanRagdollParams; }
@@ -150,7 +161,7 @@ namespace Barotrauma
private readonly float movementLerp;
private float cprAnimTimer,cprPump;
private float cprAnimTimer, cprPumpTimer;
private float fallingProneAnimTimer;
const float FallingProneAnimDuration = 1.0f;
@@ -243,14 +254,17 @@ namespace Barotrauma
if (MainLimb == null) { return; }
levitatingCollider = !IsHanging;
if ((character.SelectedItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
(character.SelectedSecondaryItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
character.SelectedSecondaryItem?.GetComponent<Ladder>() != null ||
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
if (onGround && character.CanMove)
{
Crouching = false;
if ((character.SelectedItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
(character.SelectedSecondaryItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
character.SelectedSecondaryItem?.GetComponent<Ladder>() != null ||
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
{
Crouching = false;
}
ColliderIndex = Crouching && !swimming ? 1 : 0;
}
ColliderIndex = Crouching && !swimming ? 1 : 0;
//stun (= disable the animations) if the ragdoll receives a large enough impact
if (strongestImpact > 0.0f)
@@ -276,7 +290,7 @@ namespace Barotrauma
if (!character.CanMove)
{
if (fallingProneAnimTimer < FallingProneAnimDuration)
if (fallingProneAnimTimer < FallingProneAnimDuration && onGround)
{
fallingProneAnimTimer += deltaTime;
UpdateFallingProne(1.0f);
@@ -285,7 +299,12 @@ namespace Barotrauma
Collider.FarseerBody.FixedRotation = false;
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
Collider.Enabled = 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
@@ -386,6 +405,12 @@ namespace Barotrauma
DragCharacter(character.SelectedCharacter, deltaTime);
}
if (Anim != Animation.CPR)
{
cprAnimTimer = 0.0f;
cprPumpTimer = 0.0f;
}
switch (Anim)
{
case Animation.Climbing:
@@ -487,10 +512,14 @@ namespace Barotrauma
Limb leftLeg = GetLimb(LimbType.LeftLeg);
Limb rightLeg = GetLimb(LimbType.RightLeg);
bool onSlopeThatMakesSlow = Math.Abs(floorNormal.X) > SlowlyWalkableSlopeNormalX;
bool slowedDownBySlope = onSlopeThatMakesSlow && Math.Sign(floorNormal.X) == -Math.Sign(TargetMovement.X);
bool onSlopeTooSteepToClimb = Math.Abs(floorNormal.X) > SteepestWalkableSlopeNormalX;
float walkCycleMultiplier = 1.0f;
if (Stairs != null)
if (Stairs != null || slowedDownBySlope)
{
TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.7f, 1.7f), TargetMovement.Y);
TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -MaxSpeedOnStairs, MaxSpeedOnStairs), TargetMovement.Y);
walkCycleMultiplier *= 1.5f;
}
@@ -572,6 +601,15 @@ namespace Barotrauma
bool movingHorizontally = !MathUtils.NearlyEqual(TargetMovement.X, 0.0f);
if (Stairs == null && onSlopeTooSteepToClimb)
{
if (Math.Sign(targetMovement.X) != Math.Sign(floorNormal.X))
{
targetMovement.X = Math.Sign(floorNormal.X) * SteepSlopePushMagnitude;
movement = targetMovement;
}
}
if (Stairs != null || onSlope)
{
torso.PullJointWorldAnchorB = new Vector2(
@@ -648,14 +686,6 @@ namespace Barotrauma
if (!onGround)
{
Vector2 move = torso.PullJointWorldAnchorB - torso.SimPosition;
foreach (Limb limb in Limbs)
{
if (limb.IsSevered) { continue; }
MoveLimb(limb, limb.SimPosition + move, 15.0f, true);
}
return;
}
@@ -758,7 +788,7 @@ namespace Barotrauma
{
footPos = new Vector2(colliderPos.X + stepSize.X * i * 0.2f, colliderPos.Y - 0.1f);
}
if (Stairs == null)
if (Stairs == null && !onSlopeThatMakesSlow)
{
footPos.Y = Math.Max(Math.Min(FloorY, footPos.Y + 0.5f), footPos.Y);
}
@@ -1318,14 +1348,14 @@ namespace Barotrauma
}
}
void UpdateFallingProne(float strength)
void UpdateFallingProne(float strength, bool moveHands = true, bool moveTorso = true, bool moveLegs = true)
{
if (strength <= 0.0f) { return; }
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
if (head != null && head.LinearVelocity.LengthSquared() > 1.0f && !head.IsSevered)
if (moveHands && 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);
@@ -1347,7 +1377,7 @@ namespace Barotrauma
//make the torso tip over
//otherwise it tends to just drop straight down, pinning the characters legs in a weird pose
if (!InWater)
if (moveTorso && !InWater)
{
//prefer tipping over in the same direction the torso is rotating
//or moving
@@ -1358,27 +1388,30 @@ namespace Barotrauma
}
//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++)
if (moveLegs)
{
var thigh = i == 0 ? GetLimb(LimbType.LeftThigh) : GetLimb(LimbType.RightThigh);
if (thigh == null) { continue; }
if (thigh.IsSevered) { continue; }
float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
float diff = torso.Rotation - thigh.Rotation;
if (MathUtils.IsValid(diff))
for (int i = 0; i < 2; i++)
{
float thighTorque = thighDiff * thigh.Mass * Math.Sign(diff) * 5.0f;
thigh.body.ApplyTorque(thighTorque * strength);
}
var thigh = i == 0 ? GetLimb(LimbType.LeftThigh) : GetLimb(LimbType.RightThigh);
if (thigh == null) { continue; }
if (thigh.IsSevered) { continue; }
float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
float diff = torso.Rotation - thigh.Rotation;
if (MathUtils.IsValid(diff))
{
float thighTorque = thighDiff * thigh.Mass * Math.Sign(diff) * 5.0f;
thigh.body.ApplyTorque(thighTorque * strength);
}
var leg = i == 0 ? GetLimb(LimbType.LeftLeg) : GetLimb(LimbType.RightLeg);
if (leg == null || leg.IsSevered) { continue; }
float legDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, leg.Rotation));
diff = torso.Rotation - leg.Rotation;
if (MathUtils.IsValid(diff))
{
float legTorque = legDiff * leg.Mass * Math.Sign(diff) * 5.0f;
leg.body.ApplyTorque(legTorque * strength);
var leg = i == 0 ? GetLimb(LimbType.LeftLeg) : GetLimb(LimbType.RightLeg);
if (leg == null || leg.IsSevered) { continue; }
float legDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, leg.Rotation));
diff = torso.Rotation - leg.Rotation;
if (MathUtils.IsValid(diff))
{
float legTorque = legDiff * leg.Mass * Math.Sign(diff) * 5.0f;
leg.body.ApplyTorque(legTorque * strength);
}
}
}
}
@@ -1398,7 +1431,8 @@ namespace Barotrauma
Crouching = true;
Vector2 diff = target.SimPosition - character.SimPosition;
Vector2 offset = Vector2.UnitX * -Dir * 0.75f;
Vector2 diff = (target.SimPosition + offset) - character.SimPosition;
Limb targetHead = target.AnimController.GetLimb(LimbType.Head);
Limb targetTorso = target.AnimController.GetLimb(LimbType.Torso);
if (targetTorso == null)
@@ -1412,7 +1446,23 @@ namespace Barotrauma
Vector2 headDiff = targetHead == null ? diff : targetHead.SimPosition - character.SimPosition;
targetMovement = new Vector2(diff.X, 0.0f);
const float CloseEnough = 0.1f;
if (Math.Abs(targetMovement.X) < CloseEnough)
{
targetMovement.X = 0.0f;
}
TargetDir = headDiff.X > 0.0f ? Direction.Right : Direction.Left;
//if the target's in some weird pose, we may not be able to flip it so it's facing up,
//so let's only try it once so we don't end up constantly flipping it
if (cprAnimTimer <= 0.0f && target.AnimController.Direction == TargetDir)
{
target.AnimController.Flip();
}
(target.AnimController as HumanoidAnimController)?.UpdateFallingProne(strength: 1.0f, moveHands: false, moveTorso: false);
head.Disabled = true;
torso.Disabled = true;
UpdateStanding();
@@ -1443,79 +1493,69 @@ namespace Barotrauma
}
}
//pump for 15 seconds (cprAnimTimer 0-15), then do mouth-to-mouth for 2 seconds (cprAnimTimer 15-17)
if (cprAnimTimer > 15.0f && targetHead != null && head != null)
//Serverside code
if (GameMain.NetworkMember is not { IsClient: true })
{
float yPos = (float)Math.Sin(cprAnimTimer) * 0.2f;
head.PullJointWorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.3f + yPos);
if (target.Oxygen < -10.0f)
{
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
}
}
if (targetHead != null && head != null)
{
head.PullJointWorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
head.PullJointEnabled = true;
torso.PullJointWorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.2f));
torso.PullJointEnabled = true;
//Serverside code
if (GameMain.NetworkMember is not { IsClient: true })
{
if (target.Oxygen < -10.0f)
{
//stabilize the oxygen level but don't allow it to go positive and revive the character yet
float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
character.Oxygen -= 1.0f / stabilizationAmount * deltaTime; //Worse skill = more oxygen required
if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; } //we didn't suffocate yet did we
}
}
}
else
torso.PullJointWorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.1f));
torso.PullJointEnabled = true;
if (cprPumpTimer >= 1)
{
if (targetHead != null && head != null)
torso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
targetTorso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
cprPumpTimer = 0;
if (skill < CPRSettings.Active.DamageSkillThreshold)
{
head.PullJointWorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
head.PullJointEnabled = true;
target.LastDamageSource = null;
target.DamageLimb(
targetTorso.WorldPosition, targetTorso,
new[] { CPRSettings.Active.InsufficientSkillAffliction.Instantiate((CPRSettings.Active.DamageSkillThreshold - skill) * CPRSettings.Active.DamageSkillMultiplier, source: character) },
stun: 0.0f,
playSound: true,
attackImpulse: Vector2.Zero,
attacker: null);
}
torso.PullJointWorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.1f));
torso.PullJointEnabled = true;
if (cprPump >= 1)
//need to CPR for at least a couple of seconds before the target can be revived
//(reviving the target when the CPR has barely started looks strange)
if (cprAnimTimer > 2.0f && GameMain.NetworkMember is not { IsClient: true })
{
torso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
targetTorso.body.ApplyLinearImpulse(new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
cprPump = 0;
float reviveChance = skill * CPRSettings.Active.ReviveChancePerSkill;
reviveChance = (float)Math.Pow(reviveChance, CPRSettings.Active.ReviveChanceExponent);
reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.Active.ReviveChanceMin, CPRSettings.Active.ReviveChanceMax);
reviveChance *= 1f + cprBoost;
if (skill < CPRSettings.Active.DamageSkillThreshold)
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) <= reviveChance)
{
target.LastDamageSource = null;
target.DamageLimb(
targetTorso.WorldPosition, targetTorso,
new[] { CPRSettings.Active.InsufficientSkillAffliction.Instantiate((CPRSettings.Active.DamageSkillThreshold - skill) * CPRSettings.Active.DamageSkillMultiplier, source: character) },
0.0f, true, 0.0f, attacker: null);
//increase oxygen and clamp it above zero
// -> the character should be revived if there are no major afflictions in addition to lack of oxygen
target.Oxygen = Math.Max(target.Oxygen + 10.0f, 10.0f);
GameMain.LuaCs.Hook.Call("human.CPRSuccess", this);
}
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) //Serverside code
else
{
float reviveChance = skill * CPRSettings.Active.ReviveChancePerSkill;
reviveChance = (float)Math.Pow(reviveChance, CPRSettings.Active.ReviveChanceExponent);
reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.Active.ReviveChanceMin, CPRSettings.Active.ReviveChanceMax);
reviveChance *= 1f + cprBoost;
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) <= reviveChance)
{
//increase oxygen and clamp it above zero
// -> the character should be revived if there are no major afflictions in addition to lack of oxygen
target.Oxygen = Math.Max(target.Oxygen + 10.0f, 10.0f);
GameMain.LuaCs.Hook.Call("human.CPRSuccess", this);
}
else
{
GameMain.LuaCs.Hook.Call("human.CPRFailed", this);
}
GameMain.LuaCs.Hook.Call("human.CPRFailed", this);
}
}
cprPump += deltaTime;
}
cprAnimTimer = (cprAnimTimer + deltaTime) % 17;
cprPumpTimer += deltaTime;
cprAnimTimer += deltaTime;
//got the character back into a non-critical state, increase medical skill
//BUT only if it has been more than 10 seconds since the character revived someone
@@ -1525,7 +1565,7 @@ namespace Barotrauma
target.CharacterHealth.CalculateVitality();
if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
{
character.Info?.IncreaseSkillLevel("medical".ToIdentifier(), SkillSettings.Current.SkillIncreasePerCprRevive);
character.Info?.ApplySkillGain(Tags.MedicalSkill, SkillSettings.Current.SkillIncreasePerCprRevive);
SteamAchievementManager.OnCharacterRevived(target, character);
lastReviveTime = (float)Timing.TotalTime;
#if SERVER
@@ -1730,7 +1770,23 @@ namespace Barotrauma
targetAnchor += target.Submarine.SimPosition;
}
}
pullLimb.PullJointWorldAnchorB = pullLimbAnchor;
if (Vector2.DistanceSquared(pullLimb.PullJointWorldAnchorA, pullLimbAnchor) > 50.0f * 50.0f)
{
//there's a similar error check in the PullJointWorldAnchorB setter, but we seem to be getting quite a lot of
//errors specifically from this method, so let's use a more consistent error message here to prevent clogging GA with
//different error messages that all include a different coordinate
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);
#if DEBUG
DebugConsole.ThrowError(errorMsg);
#endif
}
else
{
pullLimb.PullJointWorldAnchorB = pullLimbAnchor;
}
}
else
{