diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs index 723963e509..be307ce5af 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Animation/AnimController.cs @@ -319,14 +319,24 @@ public void LockFlipping(float time = 0.2f) protected void UpdateConstantTorque(float deltaTime) { + if (character.IsIncapacitated) { return; } + if (character.Stun > 0.01f) { return; } foreach (var limb in Limbs) { if (limb.IsSevered) { continue; } - if (Math.Abs(limb.Params.ConstantTorque) > 0) + float angleToApply = limb.Params.ConstantAngle; + float torqueToApply = limb.Params.ConstantTorque; + float movementFactor = Math.Max(character.AnimController.Collider.LinearVelocity.Length() * 0.5f, 1); + if (CurrentAnimationParams.LimbConstantAnglesData.ContainsKey(limb.Params.ID)) { - // 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); + var constantAngleData = CurrentAnimationParams.LimbConstantAnglesData[limb.Params.ID]; + + angleToApply = constantAngleData.ConstantAngle; + torqueToApply = constantAngleData.ConstantTorque; + } + if (torqueToApply > 0f) + { + limb.body.SmoothRotate(MainLimb.Rotation + MathHelper.ToRadians(angleToApply) * Dir, limb.Mass * torqueToApply * movementFactor, wrapAngle: true); } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Animation/AnimationParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Animation/AnimationParams.cs index 3a2562d48d..9efed3a00d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Animation/AnimationParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/Animation/AnimationParams.cs @@ -170,6 +170,29 @@ public float TorsoAngle [Serialize(50.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the torso to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)] public float TorsoTorque { get; set; } + public struct LimbConstantAngleData + { + public float ConstantAngle { get; set; } + public float ConstantTorque { get; set; } + + public LimbConstantAngleData(float constantAngle, float constantTorque) + { + ConstantAngle = constantAngle; + ConstantTorque = constantTorque; + } + } + /// + /// Key = limb id, value = LimbConstantAngleData struct with two properties: constant angle and constant torque + /// + public Dictionary LimbConstantAnglesData { get; set; } = new Dictionary(); + + [Serialize(null, IsPropertySaveable.Yes), Editable] + public string LimbConstantAngles + { + get => ParseLimbConstantAngles(LimbConstantAnglesData); + set => SetLimbConstantAngles(LimbConstantAnglesData, value); + } + [Header("Legs")] [Serialize(25.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the feet to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)] public float FootTorque { get; set; } @@ -486,6 +509,12 @@ protected static string ParseFootAngles(Dictionary footAngles) return string.Join(",", footAngles.Select(kv => kv.Key + ": " + kv.Value.ToString("G", CultureInfo.InvariantCulture)).ToArray()); } + protected static string ParseLimbConstantAngles(Dictionary limbAngles) + { + //convert to the format "id1:angle;torque,id2:angle;torque" + return string.Join(",", limbAngles.Select(kv => $"{kv.Key}: {kv.Value.ConstantAngle.ToString("G", CultureInfo.InvariantCulture)};" + $"{kv.Value.ConstantTorque.ToString("G", CultureInfo.InvariantCulture)}")); + } + protected static void SetFootAngles(Dictionary footAngles, string value) { footAngles.Clear(); @@ -509,6 +538,42 @@ protected static void SetFootAngles(Dictionary footAngles, string va } } + protected static void SetLimbConstantAngles(Dictionary limbAngles, string value) + { + limbAngles.Clear(); + if (string.IsNullOrEmpty(value)) + { + return; + } + + string[] keyValuePairs = value.Split(','); + foreach (string joinedKvp in keyValuePairs) + { + string[] keyValuePair = joinedKvp.Split(':'); + if (keyValuePair.Length != 2 || + !int.TryParse(keyValuePair[0].Trim(), out int limbIndex)) + { + DebugConsole.ThrowError("Failed to parse limb constant angles (" + value + ")"); + continue; + } + + string[] dataValues = keyValuePair[1].Split(';'); + if (dataValues.Length != 2 || + !float.TryParse(dataValues[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float constantAngle) || + !float.TryParse(dataValues[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float constantTorque)) + { + DebugConsole.ThrowError($"Failed to parse angle or torque for limb {limbIndex} ({keyValuePair[1]})"); + continue; + } + + limbAngles[limbIndex] = new LimbConstantAngleData + { + ConstantAngle = constantAngle, + ConstantTorque = constantTorque + }; + } + } + public static Type GetParamTypeFromAnimType(AnimationType type, bool isHumanoid) { if (isHumanoid)