Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop
This commit is contained in:
@@ -552,7 +552,8 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
if (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)");
|
||||
DebugConsole.AddWarning($"Aim position for the item {item.Name} may be incorrect (further than the length of the character's arm)",
|
||||
item.Prefab.ContentPackage);
|
||||
}
|
||||
#endif
|
||||
HandIK(
|
||||
|
||||
+159
-103
@@ -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
|
||||
{
|
||||
|
||||
@@ -438,7 +438,18 @@ namespace Barotrauma
|
||||
foreach (var huskAppendage in mainElement.GetChildElements("huskappendage"))
|
||||
{
|
||||
if (!inEditor && huskAppendage.GetAttributeBool("onlyfromafflictions", false)) { continue; }
|
||||
AfflictionHusk.AttachHuskAppendage(character, huskAppendage.GetAttributeIdentifier("affliction", Identifier.Empty), huskAppendage, ragdoll: this);
|
||||
|
||||
Identifier afflictionIdentifier = huskAppendage.GetAttributeIdentifier("affliction", Identifier.Empty);
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(afflictionIdentifier, out AfflictionPrefab affliction) ||
|
||||
affliction is not AfflictionPrefabHusk matchingAffliction)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not find an affliction of type 'huskinfection' that matches the affliction '{afflictionIdentifier}'!",
|
||||
contentPackage: huskAppendage.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
AfflictionHusk.AttachHuskAppendage(character, matchingAffliction, huskAppendage, ragdoll: this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -562,6 +573,10 @@ namespace Barotrauma
|
||||
|
||||
public void AddJoint(JointParams jointParams)
|
||||
{
|
||||
if (!checkLimbIndex(jointParams.Limb2, "Limb1") || !checkLimbIndex(jointParams.Limb2, "Limb2"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
LimbJoint joint = new LimbJoint(Limbs[jointParams.Limb1], Limbs[jointParams.Limb2], jointParams, this);
|
||||
GameMain.World.Add(joint.Joint);
|
||||
for (int i = 0; i < LimbJoints.Length; i++)
|
||||
@@ -572,6 +587,21 @@ namespace Barotrauma
|
||||
}
|
||||
Array.Resize(ref LimbJoints, LimbJoints.Length + 1);
|
||||
LimbJoints[LimbJoints.Length - 1] = joint;
|
||||
|
||||
bool checkLimbIndex(int index, string debugName)
|
||||
{
|
||||
if (index < 0 || index >= limbs.Length)
|
||||
{
|
||||
string errorMsg = $"Failed to add a joint to character {character.Name}. {debugName} out of bounds (index: {index}, limbs: {limbs.Length}.";
|
||||
DebugConsole.ThrowError(errorMsg, contentPackage: jointParams.Element?.ContentPackage);
|
||||
if (jointParams.Element?.ContentPackage == GameMain.VanillaContent)
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce("Ragdoll.AddJoint:IndexOutOfRange", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddLimb(LimbParams limbParams)
|
||||
@@ -658,6 +688,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private enum LimbStairCollisionResponse
|
||||
{
|
||||
DontClimbStairs,
|
||||
ClimbWithoutLimbCollision,
|
||||
ClimbWithLimbCollision
|
||||
}
|
||||
|
||||
public bool OnLimbCollision(Fixture f1, Fixture f2, Contact contact)
|
||||
{
|
||||
if (f2.Body.UserData is Submarine && character.Submarine == (Submarine)f2.Body.UserData) { return false; }
|
||||
@@ -700,37 +737,53 @@ namespace Barotrauma
|
||||
}
|
||||
else if (structure.StairDirection != Direction.None)
|
||||
{
|
||||
Stairs = null;
|
||||
|
||||
//don't collider with stairs if
|
||||
|
||||
//1. bottom of the collider is at the bottom of the stairs and the character isn't trying to move upwards
|
||||
float stairBottomPos = ConvertUnits.ToSimUnits(structure.Rect.Y - structure.Rect.Height + 10);
|
||||
if (colliderBottom.Y < stairBottomPos && targetMovement.Y < 0.5f) { return false; }
|
||||
|
||||
//2. bottom of the collider is at the top of the stairs and the character isn't trying to move downwards
|
||||
if (targetMovement.Y >= 0.0f && colliderBottom.Y >= ConvertUnits.ToSimUnits(structure.Rect.Y - Submarine.GridSize.Y * 5)) { return false; }
|
||||
|
||||
//3. collided with the stairs from below
|
||||
if (contact.Manifold.LocalNormal.Y < 0.0f) { return false; }
|
||||
|
||||
//4. contact points is above the bottom half of the collider
|
||||
contact.GetWorldManifold(out Vector2 normal, out FarseerPhysics.Common.FixedArray2<Vector2> points);
|
||||
if (points[0].Y > Collider.SimPosition.Y) { return false; }
|
||||
|
||||
//5. in water
|
||||
if (inWater && targetMovement.Y < 0.5f) { return false; }
|
||||
|
||||
//---------------
|
||||
|
||||
//set stairs to that of the one dragging us
|
||||
if (character.SelectedBy != null)
|
||||
{
|
||||
Stairs = character.SelectedBy.AnimController.Stairs;
|
||||
}
|
||||
else
|
||||
Stairs = structure;
|
||||
{
|
||||
var collisionResponse = handleLimbStairCollision();
|
||||
if (collisionResponse == LimbStairCollisionResponse.ClimbWithLimbCollision)
|
||||
{
|
||||
Stairs = structure;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (collisionResponse == LimbStairCollisionResponse.DontClimbStairs) { Stairs = null; }
|
||||
|
||||
if (Stairs == null)
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LimbStairCollisionResponse handleLimbStairCollision()
|
||||
{
|
||||
//don't collide with stairs if
|
||||
|
||||
//1. bottom of the collider is at the bottom of the stairs and the character isn't trying to move upwards
|
||||
float stairBottomPos = ConvertUnits.ToSimUnits(structure.Rect.Y - structure.Rect.Height + 10);
|
||||
if (colliderBottom.Y < stairBottomPos && targetMovement.Y < 0.5f) { return LimbStairCollisionResponse.DontClimbStairs; }
|
||||
|
||||
//2. bottom of the collider is at the top of the stairs and the character isn't trying to move downwards
|
||||
if (targetMovement.Y >= 0.0f && colliderBottom.Y >= ConvertUnits.ToSimUnits(structure.Rect.Y - Submarine.GridSize.Y * 5)) { return LimbStairCollisionResponse.DontClimbStairs; }
|
||||
|
||||
//3. collided with the stairs from below
|
||||
if (contact.Manifold.LocalNormal.Y < 0.0f)
|
||||
{
|
||||
return Stairs != structure
|
||||
? LimbStairCollisionResponse.DontClimbStairs
|
||||
: LimbStairCollisionResponse.ClimbWithoutLimbCollision;
|
||||
}
|
||||
|
||||
//4. contact points is above the bottom half of the collider
|
||||
contact.GetWorldManifold(out _, out FarseerPhysics.Common.FixedArray2<Vector2> points);
|
||||
if (points[0].Y > Collider.SimPosition.Y) { return LimbStairCollisionResponse.DontClimbStairs; }
|
||||
|
||||
//5. in water
|
||||
if (inWater && targetMovement.Y < 0.5f) { return LimbStairCollisionResponse.DontClimbStairs; }
|
||||
|
||||
return LimbStairCollisionResponse.ClimbWithLimbCollision;
|
||||
}
|
||||
}
|
||||
|
||||
lock (impactQueue)
|
||||
@@ -1332,17 +1385,28 @@ namespace Barotrauma
|
||||
if (onGround && Collider.LinearVelocity.Y > -ImpactTolerance)
|
||||
{
|
||||
float targetY = standOnFloorY + ((float)Math.Abs(Math.Cos(Collider.Rotation)) * Collider.Height * 0.5f) + Collider.Radius + ColliderHeightFromFloor;
|
||||
if (Math.Abs(Collider.SimPosition.Y - targetY) > 0.01f)
|
||||
|
||||
const float LevitationSpeedMultiplier = 5f;
|
||||
|
||||
// If the character is walking down a slope, target a position that moves along it
|
||||
float slopePull = 0f;
|
||||
if (floorNormal.Y is > 0f and < 1f
|
||||
&& Math.Sign(movement.X) == Math.Sign(floorNormal.X))
|
||||
{
|
||||
if (Stairs != null)
|
||||
slopePull = Math.Abs(movement.X * floorNormal.X / floorNormal.Y) / LevitationSpeedMultiplier;
|
||||
}
|
||||
|
||||
if (Math.Abs(Collider.SimPosition.Y - targetY - slopePull) > 0.01f)
|
||||
{
|
||||
float yVelocity = (targetY - Collider.SimPosition.Y) * LevitationSpeedMultiplier;
|
||||
if (Stairs != null && targetY < Collider.SimPosition.Y)
|
||||
{
|
||||
Collider.LinearVelocity = new Vector2(Collider.LinearVelocity.X,
|
||||
(targetY < Collider.SimPosition.Y ? Math.Sign(targetY - Collider.SimPosition.Y) : (targetY - Collider.SimPosition.Y)) * 5.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collider.LinearVelocity = new Vector2(Collider.LinearVelocity.X, (targetY - Collider.SimPosition.Y) * 5.0f);
|
||||
yVelocity = Math.Sign(yVelocity);
|
||||
}
|
||||
|
||||
yVelocity -= slopePull * LevitationSpeedMultiplier;
|
||||
|
||||
Collider.LinearVelocity = new Vector2(Collider.LinearVelocity.X, yVelocity);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1590,10 +1654,10 @@ namespace Barotrauma
|
||||
// Force check floor y at least once a second so that we'll drop through gaps that we are standing upon.
|
||||
private const float FloorYStaleTime = 1;
|
||||
private float floorYCheckTimer;
|
||||
private void RefreshFloorY(float deltaTime, Limb refLimb = null, bool ignoreStairs = false)
|
||||
private void RefreshFloorY(float deltaTime, bool ignoreStairs = false)
|
||||
{
|
||||
floorYCheckTimer -= deltaTime;
|
||||
PhysicsBody refBody = refLimb == null ? Collider : refLimb.body;
|
||||
PhysicsBody refBody = Collider;
|
||||
if (floorYCheckTimer < 0 ||
|
||||
lastFloorCheckIgnoreStairs != ignoreStairs ||
|
||||
lastFloorCheckIgnorePlatforms != IgnorePlatforms ||
|
||||
@@ -1618,7 +1682,7 @@ namespace Barotrauma
|
||||
if (HeadPosition.HasValue && MathUtils.IsValid(HeadPosition.Value)) { height = Math.Max(height, HeadPosition.Value); }
|
||||
if (TorsoPosition.HasValue && MathUtils.IsValid(TorsoPosition.Value)) { height = Math.Max(height, TorsoPosition.Value); }
|
||||
|
||||
Vector2 rayEnd = rayStart - new Vector2(0.0f, height);
|
||||
Vector2 rayEnd = rayStart - new Vector2(0.0f, height * 2f);
|
||||
Vector2 colliderBottomDisplay = ConvertUnits.ToDisplayUnits(GetColliderBottom());
|
||||
|
||||
Fixture standOnFloorFixture = null;
|
||||
@@ -1697,7 +1761,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (closestFraction == 1) //raycast didn't hit anything
|
||||
if (closestFraction >= 1) //raycast didn't hit anything
|
||||
{
|
||||
floorNormal = Vector2.UnitY;
|
||||
if (CurrentHull == null)
|
||||
|
||||
Reference in New Issue
Block a user