5 using Microsoft.Xna.Framework;
13 private const float SteepestWalkableSlopeAngleDegrees = 55f;
14 private const float SlowlyWalkableSlopeAngleDegrees = 30f;
16 private static readonly
float SteepestWalkableSlopeNormalX =
17 MathF.Sin(MathHelper.ToRadians(SteepestWalkableSlopeAngleDegrees));
18 private static readonly
float SlowlyWalkableSlopeNormalX =
19 MathF.Sin(MathHelper.ToRadians(SlowlyWalkableSlopeAngleDegrees));
21 private const float MaxSpeedOnStairs = 1.7f;
22 private const float SteepSlopePushMagnitude = MaxSpeedOnStairs;
39 if (_ragdollParams ==
null)
43 return _ragdollParams;
51 _ragdollParams = value;
65 if (_humanWalkParams ==
null)
69 return _humanWalkParams;
71 set { _humanWalkParams = value; }
79 if (_humanRunParams ==
null)
83 return _humanRunParams;
85 set { _humanRunParams = value; }
93 if (_humanCrouchParams ==
null)
97 return _humanCrouchParams;
99 set { _humanCrouchParams = value; }
107 if (_humanSwimSlowParams ==
null)
111 return _humanSwimSlowParams;
113 set { _humanSwimSlowParams = value; }
121 if (_humanSwimFastParams ==
null)
125 return _humanSwimFastParams;
127 set { _humanSwimFastParams = value; }
162 private float upperLegLength = 0.0f, lowerLegLength = 0.0f;
164 private readonly
float movementLerp;
166 private float cprAnimTimer, cprPumpTimer;
168 private float fallingProneAnimTimer;
169 const float FallingProneAnimDuration = 1.0f;
171 private bool swimming;
174 private float swimmingStateLockTimer;
189 shoulderHeight += 0.4f;
193 shoulderHeight -= 0.15f;
196 bool movingHorizontally = !MathUtils.NearlyEqual(
TargetMovement.X, 0.0f);
197 if (!movingHorizontally)
218 base.Recreate(ragdollParams);
219 CalculateLegLengths();
222 private void CalculateLegLengths()
230 Vector2 localAnchorWaist = Vector2.Zero;
231 Vector2 localAnchorKnee = Vector2.Zero;
232 if (waistJoint !=
null)
234 localAnchorWaist = waistJoint.LimbA.type == upperLegType ? waistJoint.LocalAnchorA : waistJoint.LocalAnchorB;
237 if (kneeJoint !=
null)
239 localAnchorKnee = kneeJoint.LimbA.type == upperLegType ? kneeJoint.LocalAnchorA : kneeJoint.LocalAnchorB;
241 upperLegLength = Vector2.Distance(localAnchorWaist, localAnchorKnee);
244 if (ankleJoint ==
null || kneeJoint ==
null) {
return; }
245 lowerLegLength = Vector2.Distance(
246 kneeJoint.LimbA.type == lowerLegType ? kneeJoint.LocalAnchorA : kneeJoint.LocalAnchorB,
247 ankleJoint.LimbA.type == lowerLegType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB);
248 lowerLegLength += Vector2.Distance(
249 ankleJoint.LimbA.type == footType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB,
250 GetLimb(footType).PullJointLocalAnchorA);
295 if (fallingProneAnimTimer < FallingProneAnimDuration &&
onGround)
297 fallingProneAnimTimer += deltaTime;
298 UpdateFallingProne(1.0f);
321 fallingProneAnimTimer = 0.0f;
358 if (Math.Abs(angleDiff) > 0.001f)
377 rightHand.Disabled =
true;
378 leftHand.Disabled =
true;
380 Vector2 midPos = waist.SimPosition;
381 Matrix torsoTransform = Matrix.CreateRotationZ(waist.Rotation);
383 midPos += Vector2.Transform(
new Vector2(-0.3f *
Dir, -0.2f), torsoTransform);
384 if (rightHand.PullJointEnabled) midPos = (midPos + rightHand.PullJointWorldAnchorB) / 2.0f;
403 UpdateStandingSimple();
427 UpdateUseItemTimer();
430 UpdateCPR(deltaTime);
434 UpdateUseItemTimer();
435 swimmingStateLockTimer -= deltaTime;
440 else if (swimming !=
inWater && swimmingStateLockTimer <= 0.0f)
445 swimmingStateLockTimer = 0.5f;
459 allowMovement:
false,
468 !controller.ControlCharacterPose || !controller.UserInCorrectPosition)
475 void UpdateUseItemTimer()
517 void UpdateStanding()
520 if (currentGroundedParams ==
null) {
return; }
536 bool onSlopeThatMakesSlow = Math.Abs(
floorNormal.X) > SlowlyWalkableSlopeNormalX;
538 bool onSlopeTooSteepToClimb = Math.Abs(
floorNormal.X) > SteepestWalkableSlopeNormalX;
540 float walkCycleMultiplier = 1.0f;
541 if (
Stairs !=
null || slowedDownBySlope)
544 walkCycleMultiplier *= 1.5f;
552 float slowdownAmount = 0.0f;
562 float maxSpeed = Math.Max(
TargetMovement.Length() - slowdownAmount, 1.0f);
566 float walkPosX = (float)Math.Cos(
WalkPos);
567 float walkPosY = (float)Math.Sin(
WalkPos);
570 stepSize.X *= walkPosX;
571 stepSize.Y *= walkPosY;
573 float footMid = colliderPos.X;
576 float herpesAmount = herpes ==
null ? 0 : herpes.
Strength / herpes.Prefab.MaxStrength;
578 float limpAmount = MathHelper.Lerp(0, 1, legDamage + herpesAmount);
579 if (limpAmount > 0.0f)
582 footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(
TargetMovement.X), 0.3f)) *
Dir;
596 if (head ==
null) {
return; }
597 if (torso ==
null) {
return; }
599 bool isNotRemote =
true;
612 getUpForce *= Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f);
614 torso.PullJointEnabled =
true;
615 head.PullJointEnabled =
true;
618 waist.PullJointEnabled =
true;
623 bool movingHorizontally = !MathUtils.NearlyEqual(
TargetMovement.X, 0.0f);
625 if (
Stairs ==
null && onSlopeTooSteepToClimb)
634 if (
Stairs !=
null || onSlope)
636 torso.PullJointWorldAnchorB =
new Vector2(
638 MathHelper.SmoothStep(torso.SimPosition.Y, colliderPos.Y +
TorsoPosition.Value - Math.Abs(walkPosX * 0.05f), getUpForce * 2.0f));
640 head.PullJointWorldAnchorB =
new Vector2(
642 MathHelper.SmoothStep(head.SimPosition.Y, colliderPos.Y +
HeadPosition.Value - Math.Abs(walkPosX * 0.05f), getUpForce * 2.0f));
646 waist.PullJointWorldAnchorB = waist.SimPosition -
movement * 0.06f;
657 (float)Math.Sin(
WalkPos * currentGroundedParams.StepLiftFrequency + MathHelper.Pi * currentGroundedParams.StepLiftOffset) * (currentGroundedParams.StepLiftAmount / 100);
659 float y = colliderPos.Y + stepLift;
665 torso.PullJointWorldAnchorB =
666 MathUtils.SmoothStep(torso.SimPosition,
672 y = colliderPos.Y + stepLift * currentGroundedParams.StepLiftHeadMultiplier;
675 head.PullJointWorldAnchorB =
676 MathUtils.SmoothStep(head.SimPosition,
680 if (waist !=
null && !waist.Disabled)
682 waist.PullJointWorldAnchorB = waist.SimPosition +
movement * 0.06f;
691 torsoAngle -= herpesStrength / 150.0f;
692 torso.body.SmoothRotate(torsoAngle *
Dir, currentGroundedParams.TorsoTorque);
696 if (!
Aiming && currentGroundedParams.FixedHeadAngle &&
HeadAngle.HasValue)
700 head.body.SmoothRotate(headAngle *
Dir, currentGroundedParams.HeadTorque);
710 const float MaxFootVelocityDiff = 5.0f;
711 const float MaxFootVelocityDiffSqr = MaxFootVelocityDiff * MaxFootVelocityDiff;
715 if ((leftFoot !=
null && (
MainLimb.
LinearVelocity - leftFoot.LinearVelocity).LengthSquared() > MaxFootVelocityDiffSqr) ||
716 (rightFoot !=
null && (
MainLimb.
LinearVelocity - rightFoot.LinearVelocity).LengthSquared() > MaxFootVelocityDiffSqr))
718 UpdateFallingProne(10.0f, moveHands:
false, moveTorso:
false, moveLegs:
true);
723 Vector2 waistPos = waist !=
null ? waist.SimPosition : torso.SimPosition;
725 if (movingHorizontally)
730 for (
int i = -1; i < 2; i += 2)
732 Limb foot = i == -1 ? leftFoot : rightFoot;
733 if (foot ==
null) {
continue; }
735 Vector2 footPos = stepSize * -i;
738 if (footPos.Y < 0.0f) { footPos.Y = -0.15f; }
742 footPos.X *= MathHelper.Lerp(1.0f, 0.75f, MathHelper.Clamp(footAfflictionStrength / 50.0f, 0.0f, 1.0f));
744 if (currentGroundedParams.FootLiftHorizontalFactor > 0)
749 float min = MathUtils.InverseLerp(1, 0, currentGroundedParams.FootLiftHorizontalFactor);
750 float max = 1 + MathUtils.InverseLerp(0, 1, currentGroundedParams.FootLiftHorizontalFactor);
752 footPos.Y *= xFactor;
755 if (onSlope &&
Stairs ==
null)
759 footPos.Y = Math.Min(waistPos.Y - colliderPos.Y - 0.4f, footPos.Y);
762 if ((i == 1 && Math.Sign(Math.Sin(
WalkPos)) > 0 && Math.Sign(walkPosY) < 0) ||
763 (i == -1 && Math.Sign(Math.Sin(
WalkPos)) < 0 && Math.Sign(walkPosY) > 0))
772 foot.DebugRefPos = colliderPos;
773 foot.DebugTargetPos = colliderPos + footPos;
774 MoveLimb(foot, colliderPos + footPos, currentGroundedParams.FootMoveStrength);
775 FootIK(foot, colliderPos + footPos,
776 currentGroundedParams.LegBendTorque, currentGroundedParams.FootTorque, currentGroundedParams.FootAngleInRadians);
781 handPos = torso.SimPosition;
782 handPos.X = -walkPosX * currentGroundedParams.HandMoveAmount.X;
784 float lowerY = currentGroundedParams.HandClampY;
786 handPos.Y = lowerY + (float)(Math.Abs(Math.Sin(
WalkPos - Math.PI * 1.5f) * currentGroundedParams.HandMoveAmount.Y));
790 if (rightHand !=
null && !rightHand.Disabled)
793 torso.SimPosition + posAddition +
new Vector2(-handPos.X, (Math.Sign(walkPosX) == Math.Sign(
Dir)) ? handPos.Y : lowerY),
794 currentGroundedParams.ArmMoveStrength, currentGroundedParams.HandMoveStrength);
796 if (leftHand !=
null && !leftHand.Disabled)
799 torso.SimPosition + posAddition +
new Vector2(handPos.X, (Math.Sign(walkPosX) == Math.Sign(-
Dir)) ? handPos.Y : lowerY),
800 currentGroundedParams.ArmMoveStrength, currentGroundedParams.HandMoveStrength);
805 for (
int i = -1; i < 2; i += 2)
807 Vector2 footPos = colliderPos;
810 footPos =
new Vector2(Math.Sign(stepSize.X * i) *
Dir * 0.35f, colliderPos.Y);
811 if (Math.Sign(footPos.X) != Math.Sign(
Dir))
816 footPos.X += colliderPos.X;
820 float footPosX = stepSize.X * i * 0.2f;
825 footPos =
new Vector2(colliderPos.X + footPosX, colliderPos.Y - 0.1f);
827 if (
Stairs ==
null && !onSlopeThatMakesSlow)
829 footPos.Y = Math.Max(Math.Min(
FloorY, footPos.Y + 0.5f), footPos.Y);
831 var foot = i == -1 ? rightFoot : leftFoot;
832 if (foot !=
null && !foot.Disabled)
834 foot.DebugRefPos = colliderPos;
835 foot.DebugTargetPos = footPos;
836 float footMoveForce = currentGroundedParams.FootMoveStrength;
837 float legBendTorque = currentGroundedParams.LegBendTorque;
844 MoveLimb(foot, footPos, footMoveForce);
845 FootIK(foot, footPos, legBendTorque, currentGroundedParams.FootTorque, currentGroundedParams.FootAngleInRadians);
849 for (
int i = 0; i < 2; i++)
851 var hand = i == 0 ? rightHand : leftHand;
852 if (hand ==
null || hand.Disabled) {
continue; }
859 if (arm !=
null && Math.Abs(arm.body.AngularVelocity) < 10.0f)
861 arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.5f, 0.5f), arm.Mass * 50.0f * currentGroundedParams.ArmMoveStrength);
865 if (Math.Abs(hand.body.AngularVelocity) < 10.0f)
867 var forearm =
GetLimb(foreArmType) ?? hand;
871 float diff = elbow.JointAngle - (
Dir > 0 ? elbow.LowerLimit : elbow.UpperLimit);
872 forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f * currentGroundedParams.ArmMoveStrength);
879 hand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * hand.Mass * 100f * currentGroundedParams.HandMoveStrength);
885 void UpdateStandingSimple()
905 private float handCyclePos;
906 private float legCyclePos;
907 void UpdateSwimming()
912 Vector2 footPos, handPos;
914 float surfaceLimiter = 1.0f;
918 if (head ==
null) {
return; }
919 if (torso ==
null) {
return; }
925 surfaceLimiter = Math.Max(1.0f, surfaceThreshold - surfacePos);
935 rotation = MathHelper.ToDegrees(rotation);
947 if (rotation > 20 && rotation < 170)
951 else if (rotation > 190 && rotation < 340)
960 Vector2 diff = (mousePos - torso.SimPosition) *
Dir;
961 if (diff.LengthSquared() > MathUtils.Pow2(0.4f))
963 float newRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 *
Dir);
967 else if (targetSpeed > 0.1f)
969 float newRotation = MathUtils.VectorToAngle(
TargetMovement) - MathHelper.PiOver2;
995 const float DisableMovementAboveSurfaceThreshold = 50.0f;
1002 head.body.SmoothRotate(0.0f, 5.0f);
1013 head.body.ApplyTorque(
Dir);
1015 movement.Y *= Math.Max(0, 1.0f - ((surfaceLimiter - 1.0f) / DisableMovementAboveSurfaceThreshold));
1019 bool isNotRemote =
true;
1024 float t = movementLerp;
1027 Vector2 forward = VectorExtensions.Forward(
Collider.
Rotation + MathHelper.PiOver2);
1028 float dot = Vector2.Dot(forward, Vector2.Normalize(
movement));
1032 t = MathHelper.Clamp((1 + dot) / 10, 0.01f, 0.1f);
1037 if (surfaceLimiter > DisableMovementAboveSurfaceThreshold)
1048 float legMoveMultiplier = 1.0f;
1049 if (
movement.LengthSquared() < 0.001f)
1052 legMoveMultiplier = 0.3f;
1053 legCyclePos += 0.4f;
1054 handCyclePos += 0.1f;
1058 footPos = waist ==
null ? Vector2.Zero : waist.SimPosition -
new Vector2((
float)Math.Sin(-
Collider.
Rotation), (
float)Math.Cos(-
Collider.
Rotation)) * (upperLegLength + lowerLegLength);
1060 transformedFootPos = Vector2.Transform(transformedFootPos, Matrix.CreateRotationZ(
Collider.
Rotation));
1063 if (rightFoot !=
null && !rightFoot.Disabled)
1067 if (leftFoot !=
null && !leftFoot.Disabled)
1072 handPos = (torso.SimPosition + head.SimPosition) / 2.0f;
1078 handPos += MathUtils.RotatePoint(Vector2.UnitX *
Dir * 0.2f, torso.Rotation);
1080 float wobbleAmount = 0.1f;
1082 if (rightHand !=
null && !rightHand.Disabled)
1085 handPos.X + (
float)Math.Sin(handCyclePos / 1.5f) * wobbleAmount,
1089 if (leftHand !=
null && !leftHand.Disabled)
1092 handPos.X + (
float)Math.Sin(handCyclePos / 2.0f) * wobbleAmount,
1099 handPos += head.LinearVelocity.ClampLength(1.0f) * 0.1f;
1107 Matrix rotationMatrix = Matrix.CreateRotationZ(torso.Rotation);
1109 if (rightHand !=
null && !rightHand.Disabled)
1111 Vector2 rightHandPos =
new Vector2(-handPosX, -handPosY) + handMoveOffset;
1112 rightHandPos.X = (
Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
1113 rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
1117 speedMultiplier = Math.Min(speedMultiplier, 0.1f);
1124 rightHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * rightHand.Mass * 100f *
CurrentSwimParams.
HandMoveStrength);
1128 if (leftHand !=
null && !leftHand.Disabled)
1130 Vector2 leftHandPos =
new Vector2(handPosX, handPosY) + handMoveOffset;
1131 leftHandPos.X = (
Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
1132 leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
1136 speedMultiplier = Math.Min(speedMultiplier, 0.1f);
1143 leftHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * leftHand.Mass * 100f *
CurrentSwimParams.
HandMoveStrength);
1148 private float prevFootPos;
1150 void UpdateClimbing()
1158 else if (ladder ==
null)
1170 tempTargetMovement.Y = climbFast ?
1171 Math.Min(tempTargetMovement.Y, 2.0f) :
1172 Math.Min(tempTargetMovement.Y, 1.0f);
1184 if (leftHand ==
null || rightHand ==
null || head ==
null || torso ==
null) {
return; }
1186 Vector2 ladderSimPos = ConvertUnits.ToSimUnits(
1187 ladder.Item.Rect.X + ladder.Item.Rect.Width / 2.0f,
1188 ladder.Item.Rect.Y);
1190 Vector2 ladderSimSize = ConvertUnits.ToSimUnits(ladder.Item.Rect.Size.ToVector2());
1192 float lowestLadderSimPos = ladderSimPos.Y - ladderSimPos.Y;
1193 var lowestNearbyLadder = GetLowestNearbyLadder(ladder);
1194 if (lowestNearbyLadder !=
null && lowestNearbyLadder != ladder)
1196 ladderSimSize.Y = ConvertUnits.ToSimUnits(ladder.Item.WorldRect.Y - (lowestNearbyLadder.Item.WorldRect.Y - lowestNearbyLadder.Item.Rect.Size.Y));
1199 float stepHeight = ConvertUnits.ToSimUnits(30.0f);
1200 if (climbFast) { stepHeight *= 2; }
1204 ladderSimPos += ladder.Item.Submarine.SimPosition;
1217 MoveLimb(torso,
new Vector2(ladderSimPos.X - 0.35f *
Dir, bottomPos + torsoPos), 10.5f);
1219 MoveLimb(head,
new Vector2(ladderSimPos.X - 0.2f *
Dir, bottomPos + headPos), 10.5f);
1223 Vector2 handPos =
new Vector2(
1225 bottomPos + torsoPos +
movement.Y * 0.1f - ladderSimPos.Y);
1226 if (climbFast) { handPos.Y -= stepHeight; }
1229 handPos.Y = Math.Min(-0.5f, handPos.Y);
1233 new Vector2(slide ? handPos.X + ladderSimSize.X * 0.5f : handPos.X,
1234 (slide ? handPos.Y : MathUtils.Round(handPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y),
1236 rightHand.body.ApplyTorque(
Dir * 2.0f);
1241 new Vector2(handPos.X - ladderSimSize.X * 0.5f,
1242 (slide ? handPos.Y : MathUtils.Round(handPos.Y - stepHeight, stepHeight * 2.0f) + stepHeight) + ladderSimPos.Y),
1244 leftHand.body.ApplyTorque(
Dir * 2.0f);
1247 Vector2 footPos =
new Vector2(
1248 handPos.X -
Dir * 0.05f,
1250 if (climbFast) { footPos.Y += stepHeight; }
1258 if (footPos.Y > -ladderSimSize.Y - 0.2f && leftFoot !=
null && rightFoot !=
null)
1261 bool leftLegBackwards = Math.Abs(leftLeg.body.Rotation - refLimb.body.Rotation) > MathHelper.Pi;
1262 bool rightLegBackwards = Math.Abs(rightLeg.body.Rotation - refLimb.body.Rotation) > MathHelper.Pi;
1266 if (!leftLegBackwards) {
MoveLimb(leftFoot,
new Vector2(footPos.X - ladderSimSize.X * 0.5f, footPos.Y + ladderSimPos.Y), 15.5f,
true); }
1267 if (!rightLegBackwards) {
MoveLimb(rightFoot,
new Vector2(footPos.X, footPos.Y + ladderSimPos.Y), 15.5f,
true); }
1271 float leftFootPos = MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight;
1272 float prevLeftFootPos = MathUtils.Round(prevFootPos + stepHeight, stepHeight * 2.0f) - stepHeight;
1273 if (!leftLegBackwards) {
MoveLimb(leftFoot,
new Vector2(footPos.X, leftFootPos + ladderSimPos.Y), 15.5f,
true); }
1275 float rightFootPos = MathUtils.Round(footPos.Y, stepHeight * 2.0f);
1276 float prevRightFootPos = MathUtils.Round(prevFootPos, stepHeight * 2.0f);
1277 if (!rightLegBackwards) {
MoveLimb(rightFoot,
new Vector2(footPos.X, rightFootPos + ladderSimPos.Y), 15.5f,
true); }
1279 if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
1281 SoundPlayer.PlaySound(
"footstep_armor_heavy", leftFoot.WorldPosition, hullGuess:
currentHull);
1282 leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
1284 if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
1286 SoundPlayer.PlaySound(
"footstep_armor_heavy", rightFoot.WorldPosition, hullGuess:
currentHull);
1287 rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
1290 prevFootPos = footPos.Y;
1293 if (!leftLegBackwards) { leftLeg.body.ApplyTorque(
Dir * -8.0f); }
1294 if (!rightLegBackwards) { rightLeg.body.ApplyTorque(
Dir * -8.0f); }
1297 float movementFactor = (handPos.Y / stepHeight) * (
float)Math.PI;
1298 movementFactor = 0.8f + (float)Math.Abs(Math.Sin(movementFactor));
1301 ? Vector2.Zero : ladder.Item.
Submarine.Velocity;
1304 Vector2 climbForce =
new Vector2(0.0f,
movement.Y) * movementFactor;
1306 if (!
InWater) { climbForce.Y += 0.3f * movementFactor; }
1312 !
floorFixture.CollisionCategories.HasFlag(Physics.CollisionStairs) &&
1313 !
floorFixture.CollisionCategories.HasFlag(Physics.CollisionPlatform) &&
1316 climbForce.Y = MathHelper.Clamp((standOnFloorY + minHeightFromFloor -
character.
SimPosition.Y) * 5.0f, climbForce.Y, 1.0f);
1327 Vector2 diff = (selectedItem.WorldPosition - head.WorldPosition) *
Dir;
1328 float targetRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 *
Dir);
1337 if (ladder.Item.Prefab.Triggers.None())
1343 Rectangle trigger = ladder.Item.Prefab.Triggers.FirstOrDefault();
1344 trigger = ladder.Item.TransformTrigger(trigger);
1346 bool isRemote =
false;
1347 bool isClimbing =
true;
1348 if (GameMain.NetworkMember !=
null && GameMain.NetworkMember.IsClient)
1373 Ladder GetLowestNearbyLadder(
Ladder currentLadder,
float threshold = 16.0f)
1387 void UpdateFallingProne(
float strength,
bool moveHands =
true,
bool moveTorso =
true,
bool moveLegs =
true)
1389 if (strength <= 0.0f) {
return; }
1394 if (moveHands && head !=
null && head.LinearVelocity.LengthSquared() > 1.0f && !head.IsSevered)
1401 Vector2 protectPos = head.SimPosition + Vector2.Normalize(head.LinearVelocity);
1402 if (rightHand !=
null && !rightHand.IsSevered)
1404 HandIK(rightHand, protectPos, strength * 0.1f);
1406 if (leftHand !=
null && !leftHand.IsSevered)
1408 HandIK(leftHand, protectPos, strength * 0.1f);
1412 if (torso ==
null) {
return; }
1421 float fallDirection = Math.Sign(torso.body.AngularVelocity - torso.body.LinearVelocity.X -
Dir * 0.01f);
1422 float torque = MathF.Cos(torso.Rotation) * fallDirection * 5.0f * strength;
1423 torso.body.ApplyTorque(torque * torso.body.Mass);
1429 for (
int i = 0; i < 2; i++)
1432 if (thigh ==
null) {
continue; }
1433 if (thigh.IsSevered) {
continue; }
1434 float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
1435 float diff = torso.
Rotation - thigh.Rotation;
1436 if (MathUtils.IsValid(diff))
1438 float thighTorque = thighDiff * thigh.Mass * Math.Sign(diff) * 5.0f;
1439 thigh.body.ApplyTorque(thighTorque * strength);
1443 if (leg ==
null || leg.IsSevered) {
continue; }
1444 float legDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, leg.Rotation));
1445 diff = torso.
Rotation - leg.Rotation;
1446 if (MathUtils.IsValid(diff))
1448 float legTorque = legDiff * leg.Mass * Math.Sign(diff) * 5.0f;
1449 leg.body.ApplyTorque(legTorque * strength);
1455 private float lastReviveTime;
1457 private void UpdateCPR(
float deltaTime)
1470 Vector2 offset = Vector2.UnitX * -
Dir * 0.75f;
1472 Limb targetHead = target.AnimController.GetLimb(
LimbType.Head);
1473 Limb targetTorso = target.AnimController.GetLimb(
LimbType.Torso);
1474 if (targetTorso ==
null)
1485 const float CloseEnough = 0.1f;
1494 if (cprAnimTimer <= 0.0f && target.AnimController.Direction ==
TargetDir)
1496 target.AnimController.Flip();
1498 (target.AnimController as
HumanoidAnimController)?.UpdateFallingProne(strength: 1.0f, moveHands:
false, moveTorso:
false);
1500 head.Disabled =
true;
1501 torso.Disabled =
true;
1505 Vector2 handPos = targetTorso.SimPosition + Vector2.UnitY * 0.2f;
1507 Grab(handPos, handPos);
1511 float prevVitality = target.Vitality;
1512 bool wasCritical = prevVitality < 0.0f;
1514 if (GameMain.NetworkMember ==
null || !GameMain.NetworkMember.IsClient)
1516 target.Oxygen += deltaTime * 0.5f;
1523 if (GameMain.NetworkMember is not { IsClient:
true })
1528 target.Oxygen = Math.Max(target.Oxygen, -10.0f);
1533 if (GameMain.NetworkMember is not { IsClient:
true })
1535 if (target.Oxygen < -10.0f)
1538 float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
1539 stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
1540 character.Oxygen -= 1.0f / stabilizationAmount * deltaTime;
1541 if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; }
1545 if (targetHead !=
null && head !=
null)
1547 head.PullJointWorldAnchorB =
new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
1548 head.PullJointEnabled =
true;
1551 torso.PullJointWorldAnchorB =
new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.1f));
1552 torso.PullJointEnabled =
true;
1554 if (cprPumpTimer >= 1)
1556 torso.body.ApplyLinearImpulse(
new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
1557 targetTorso.body.ApplyLinearImpulse(
new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
1559 target.DisableImpactDamageTimer = 0.15f;
1562 if (skill < CPRSettings.Active.DamageSkillThreshold)
1564 target.LastDamageSource =
null;
1566 targetTorso.WorldPosition, targetTorso,
1567 new[] { CPRSettings.Active.InsufficientSkillAffliction.Instantiate((CPRSettings.Active.DamageSkillThreshold - skill) * CPRSettings.Active.DamageSkillMultiplier, source: character) },
1570 attackImpulse: Vector2.Zero,
1575 if (cprAnimTimer > 2.0f && GameMain.NetworkMember is not { IsClient: true })
1577 float reviveChance = skill * CPRSettings.Active.ReviveChancePerSkill;
1578 reviveChance = (float)Math.Pow(reviveChance, CPRSettings.Active.ReviveChanceExponent);
1579 reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.Active.ReviveChanceMin, CPRSettings.Active.ReviveChanceMax);
1580 reviveChance *= 1f + cprBoost;
1582 if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) <= reviveChance)
1586 target.Oxygen = Math.Max(target.Oxygen + 10.0f, 10.0f);
1587 GameMain.LuaCs.Hook.Call(
"human.CPRSuccess",
this);
1591 GameMain.LuaCs.Hook.Call(
"human.CPRFailed",
this);
1595 cprPumpTimer += deltaTime;
1596 cprAnimTimer += deltaTime;
1603 target.CharacterHealth.CalculateVitality();
1604 if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
1606 character.Info?.ApplySkillGain(Tags.MedicalSkill, SkillSettings.Current.SkillIncreasePerCprRevive);
1607 AchievementManager.OnCharacterRevived(target, character);
1608 lastReviveTime = (float)Timing.TotalTime;
1610 GameMain.Server?.KarmaManager?.OnCharacterHealthChanged(target, character, damage: Math.Min(prevVitality - target.Vitality, 0.0f), stun: 0.0f);
1614 target.ForgiveAttacker(character);
1621 if (target ==
null) {
return; }
1627 Limb targetLeftHand =
1632 Limb targetRightHand =
1642 bool targetPoseControlled =
1651 character.DeselectCharacter();
1661 if (character.Submarine ==
null && target.
Submarine !=
null)
1665 else if (character.Submarine !=
null && target.
Submarine ==
null)
1667 transformedTorsoPos += character.Submarine.SimPosition;
1671 transformedTorsoPos += character.Submarine.SimPosition;
1694 Collider.ResetDynamics();
1708 for (
int i = 0; i < 2; i++)
1715 targetLimb = targetLeftHand;
1719 targetLimb = targetRightHand;
1726 targetLimb = targetRightHand;
1730 targetLimb = targetLeftHand;
1734 Limb pullLimb = i == 0 ? leftHand : rightHand;
1741 if (character.Submarine !=
null && character.SelectedCharacter.Submarine ==
null)
1743 targetSimPos -= character.Submarine.SimPosition;
1745 else if (character.Submarine ==
null && character.SelectedCharacter.Submarine !=
null)
1747 sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
1749 else if (character.Submarine !=
null && character.SelectedCharacter.Submarine !=
null && character.Submarine != character.SelectedCharacter.Submarine)
1751 targetSimPos += character.SelectedCharacter.Submarine.SimPosition;
1752 targetSimPos -= character.Submarine.SimPosition;
1757 character.DeselectCharacter();
1763 if (i > 0 && inWater) {
continue; }
1767 Vector2 targetAnchor;
1773 if (!character.CanRunWhileDragging())
1775 targetMovement *= MathHelper.Clamp(Mass / target.
Mass, 0.5f, 1.0f);
1778 Vector2 shoulderPos = rightShoulder.WorldAnchorA;
1779 float targetDist = Vector2.Distance(targetLimb.
SimPosition, shoulderPos);
1780 Vector2 dragDir = (targetLimb.
SimPosition - shoulderPos) / targetDist;
1781 if (!MathUtils.IsValid(dragDir)) { dragDir = -Vector2.UnitY; }
1785 dragDir = Vector2.Lerp(dragDir, -Vector2.UnitY, 0.5f);
1788 Vector2 pullLimbAnchor = shoulderPos + dragDir * Math.Min(targetDist, (upperArmLength + forearmLength) * 2);
1789 targetAnchor = shoulderPos + dragDir * (upperArmLength + forearmLength);
1790 targetForce = 200.0f;
1793 if (character.Submarine ==
null)
1800 pullLimbAnchor -= character.Submarine.SimPosition;
1801 targetAnchor += character.Submarine.SimPosition;
1806 pullLimbAnchor += character.Submarine.SimPosition;
1807 targetAnchor -= character.Submarine.SimPosition;
1817 $
"Attempted to move the anchor B of a limb's pull joint extremely far from the limb in {nameof(DragCharacter)}. " +
1818 $
"Character in sub: {character.Submarine != null}, target in sub: {target.Submarine != null}.";
1819 GameAnalyticsManager.AddErrorEventOnce(
"DragCharacter:PullJointTooFar", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1821 DebugConsole.ThrowError(errorMsg);
1834 targetForce = 5000.0f;
1837 if (!targetPoseControlled)
1847 float dist = ConvertUnits.ToSimUnits(Vector2.Distance(target.
WorldPosition, WorldPosition));
1852 character.DeselectCharacter();
1857 if (!character.CanRunWhileDragging() && Vector2.Dot(target.
WorldPosition - WorldPosition, targetMovement) < 0)
1859 targetMovement *= MathHelper.Clamp(1.5f - dist, 0.0f, 1.0f);
1879 Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Math.Sign(target.
SimPosition.X - character.SimPosition.X)) - target.
SimPosition;
1885 private void RotateHead(
Limb head)
1887 Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
1888 Vector2 dir = (mousePos - head.
SimPosition) * Dir;
1889 float rot = MathUtils.VectorToAngle(dir);
1891 if (neckJoint !=
null)
1893 float offset = MathUtils.WrapAnglePi(GetLimb(
LimbType.Torso).body.Rotation);
1894 float lowerLimit = neckJoint.LowerLimit + offset;
1895 float upperLimit = neckJoint.UpperLimit + offset;
1896 float min = Math.Min(lowerLimit, upperLimit);
1897 float max = Math.Max(lowerLimit, upperLimit);
1898 rot = Math.Clamp(rot, min, max);
1903 private void FootIK(Limb foot, Vector2 pos,
float legTorque,
float footTorque,
float footAngle)
1905 if (!MathUtils.IsValid(pos))
1907 string errorMsg =
"Invalid foot position in FootIK (" + pos +
")\n" + Environment.StackTrace.CleanupStackTrace();
1909 DebugConsole.ThrowError(errorMsg);
1911 GameAnalyticsManager.AddErrorEventOnce(
"FootIK:InvalidPos", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1915 Limb upperLeg, lowerLeg;
1916 if (foot.type ==
LimbType.LeftFoot)
1918 upperLeg = GetLimb(
LimbType.LeftThigh);
1919 lowerLeg = GetLimb(
LimbType.LeftLeg);
1923 upperLeg = GetLimb(
LimbType.RightThigh);
1924 lowerLeg = GetLimb(
LimbType.RightLeg);
1926 Limb torso = GetLimb(
LimbType.Torso);
1927 LimbJoint waistJoint = GetJointBetweenLimbs(
LimbType.Waist, upperLeg.type) ?? GetJointBetweenLimbs(
LimbType.Torso, upperLeg.type);
1928 Vector2 waistPos = Vector2.Zero;
1929 if (waistJoint !=
null)
1931 waistPos = waistJoint.LimbA == upperLeg ? waistJoint.WorldAnchorA : waistJoint.WorldAnchorB;
1935 float c = Vector2.Distance(pos, waistPos);
1936 c = Math.Max(c, Math.Abs(upperLegLength - lowerLegLength));
1938 float legAngle = MathUtils.VectorToAngle(pos - waistPos) + MathHelper.PiOver2;
1939 if (!MathUtils.IsValid(legAngle))
1941 string errorMsg =
"Invalid leg angle (" + legAngle +
") in FootIK. Waist pos: " + waistPos +
", target pos: " + pos +
"\n" + Environment.StackTrace.CleanupStackTrace();
1943 DebugConsole.ThrowError(errorMsg);
1945 GameAnalyticsManager.AddErrorEventOnce(
"FootIK:InvalidAngle", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1951 while (torso.Rotation - legAngle > MathHelper.Pi)
1953 legAngle += MathHelper.TwoPi;
1955 while (torso.Rotation - legAngle < -MathHelper.Pi)
1957 legAngle -= MathHelper.TwoPi;
1961 float upperLegAngle = c >= upperLegLength + lowerLegLength ? 0.0f : MathUtils.SolveTriangleSSS(lowerLegLength, upperLegLength, c);
1962 float lowerLegAngle = c >= upperLegLength + lowerLegLength ? 0.0f : MathUtils.SolveTriangleSSS(upperLegLength, lowerLegLength, c);
1964 upperLeg.body.SmoothRotate((legAngle + upperLegAngle * Dir), upperLeg.Mass * legTorque, wrapAngle:
false);
1965 lowerLeg.body.SmoothRotate((legAngle - lowerLegAngle * Dir), lowerLeg.Mass * legTorque, wrapAngle:
false);
1966 foot.body.SmoothRotate((legAngle - (lowerLegAngle + footAngle) * Dir), foot.Mass * footTorque, wrapAngle:
false);
1973 LogAccessedRemovedCharacterError();
1982 if (torso ==
null) {
return; }
1984 Matrix torsoTransform = Matrix.CreateRotationZ(torso.
Rotation);
1987 foreach (
Item heldItem
in character.HeldItems)
1989 if (heldItem?.body !=
null && !heldItem.
Removed && heldItem.GetComponent<
Holdable>() !=
null)
1991 heldItem.
FlipX(relativeToSub:
false);
1995 foreach (
Limb limb
in Limbs)
1999 bool mirror =
false;
2000 bool flipAngle =
false;
2001 bool wrapAngle =
false;
2019 mirror = Crouching && !inWater;
2020 flipAngle = (limb.
DoesFlip || Crouching) && !inWater;
2021 wrapAngle = !inWater;
2024 flipAngle = limb.
DoesFlip && !inWater;
2025 wrapAngle = !inWater;
2034 difference = Vector2.Transform(difference, torsoTransform);
2035 difference.Y = -difference.Y;
2037 position = torso.SimPosition + Vector2.Transform(difference, -torsoTransform);
2043 if (wrapAngle) { angle = MathUtils.WrapAnglePi(angle); }
2045 TrySetLimbPosition(limb, Collider.
SimPosition, position, angle);
2055 DebugConsole.ThrowError($
"{character.SpeciesName} cannot crouch!");
2060 return base.GetSpeed(type);
void HandIK(Limb hand, Vector2 pos, float armTorque=1.0f, float handTorque=1.0f, float maxAngularVelocity=float.PositiveInfinity)
AnimationType ForceSelectAnimationType
override? float HeadPosition
virtual ? Vector2 StepSize
AnimationParams? CurrentAnimationParams
override? float TorsoAngle
LimbJoint GetJointBetweenLimbs(LimbType limbTypeA, LimbType limbTypeB)
override? float TorsoPosition
void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
void LockFlipping(float time=0.2f)
override? float HeadAngle
void Grab(Vector2 rightHandPos, Vector2 leftHandPos)
float GetAfflictionStrength(Identifier afflictionType, Limb limb, bool requireLimbSpecific)
Get the total strength of the afflictions of a specific type attached to a specific limb
float GetAfflictionStrengthByType(Identifier afflictionType, bool allowLimbAfflictions=true)
Affliction GetAffliction(string identifier, bool allowLimbAfflictions=true)
float GetSkillLevel(string skillIdentifier)
CharacterHealth CharacterHealth
bool IsRemotelyControlled
Is the character controlled remotely (either by another player, or a server-side AIController)
void SetStun(float newStun, bool allowStunDecrease=false, bool isNetworkMessage=false)
float GetLegPenalty(float startSum=0)
override Vector2? SimPosition
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
float GetStatValue(StatTypes statType, bool includeSaved=true)
float SpeedMultiplier
Can be used to modify the character's speed via StatusEffects
float GetLeftHandPenalty()
Character SelectedCharacter
CharacterInventory Inventory
float GetRightHandPenalty()
static Character? Controlled
Item SelectedSecondaryItem
The secondary selected item. It's an item other than a device (see SelectedItem), e....
bool IsKeyDown(InputType inputType)
void ReleaseSecondaryItem()
readonly AnimController AnimController
Item GetItemInLimbSlot(InvSlotType limbSlot)
float GetAttributeFloat(string key, float def)
virtual ContentXElement? MainElement
virtual Vector2 WorldPosition
static NetworkMember NetworkMember
float BackwardsMovementMultiplier
float MoveDownAmountWhenStationary
float ExtraHeadAngleWhenStationary
static HumanCrouchParams GetDefaultAnimParams(Character character)
float ExtraTorsoAngleWhenStationary
Vector2 StepSizeWhenStanding
static HumanRagdollParams GetDefaultRagdollParams(Character character)
static HumanRunParams GetDefaultAnimParams(Character character)
static HumanSwimFastParams GetDefaultAnimParams(Character character)
static HumanSwimSlowParams GetDefaultAnimParams(Character character)
static HumanWalkParams GetDefaultAnimParams(Character character)
HumanSwimSlowParams HumanSwimSlowParams
HumanRunParams HumanRunParams
HumanoidAnimController(Character character, string seed, HumanRagdollParams ragdollParams=null)
override Vector2 AimSourceSimPos
override void Recreate(RagdollParams ragdollParams=null)
Call this to create the ragdoll from the RagdollParams.
HumanSwimFastParams HumanSwimFastParams
IHumanAnimation CurrentHumanAnimParams
override SwimParams SwimFastParams
HumanWalkParams HumanWalkParams
new HumanSwimParams CurrentSwimParams
override void DragCharacter(Character target, float deltaTime)
const float BreakFromGrabDistance
override SwimParams SwimSlowParams
HumanRagdollParams HumanRagdollParams
override GroundedMovementParams RunParams
override GroundedMovementParams WalkParams
override void UpdateAnim(float deltaTime)
HumanCrouchParams HumanCrouchParams
override RagdollParams RagdollParams
override float GetSpeed(AnimationType type)
new HumanGroundedParams CurrentGroundedParams
Inventory ParentInventory
override void FlipX(bool relativeToSub)
Flip the entity horizontally
bool IsInteractable(Character character)
Returns interactibility based on whether the character is on a player team
bool ControlCharacterPose
static List< Ladder > List
Vector2 PullJointWorldAnchorB
Vector2 PullJointWorldAnchorA
void ApplyForce(Vector2 force, float maxVelocity=NetConfig.MaxPhysicsBodyVelocity)
bool SetTransform(Vector2 simPosition, float rotation, bool setPrevTransform=true)
void MoveToPos(Vector2 simPosition, float force, Vector2? pullPos=null)
bool SetTransformIgnoreContacts(Vector2 simPosition, float rotation, bool setPrevTransform=true)
void SmoothRotate(float targetRotation, float force=10.0f, bool wrapAngle=true)
Rotate the body towards the target rotation in the "shortest direction", taking into account the curr...
bool shouldBeDraggedWithRope
void ResetPullJoints(Func< Limb, bool > condition=null)
void StopHangingWithRope()
Vector2 GetColliderBottom()
void PlayImpactSound(Limb limb)
bool IsHangingWithRope
Is hanging to something with a rope, so that can reel towards it. Currently only possible in water.
bool? SimplePhysicsEnabled
Vector2 overrideTargetMovement
Limb GetLimb(LimbType limbType, bool excludeSevered=true)
Note that if there are multiple limbs of the same type, only the first (valid) limb is returned.
float GetSurfaceY()
Get the position of the surface of water at the position of the character, in display units (taking i...
float ColliderHeightFromFloor
In sim units. Joint scale applied.
void StopGettingDraggedWithRope()
void MoveLimb(Limb limb, Vector2 pos, float amount, bool pullFromCenter=false)
if false, force is applied to the position of pullJoint
Submarine(SubmarineInfo info, bool showErrorMessages=true, Func< Submarine, List< MapEntity >> loadEntities=null, IdRemap linkedRemap=null)
static Body CheckVisibility(Vector2 rayStart, Vector2 rayEnd, bool ignoreLevel=false, bool ignoreSubs=false, bool ignoreSensors=true, bool ignoreDisabledWalls=true, bool ignoreBranches=true, Predicate< Fixture > blocksVisibilityPredicate=null)
Check visibility between two points (in sim units).
override Vector2 SimPosition
@ InWater
Executes continuously when the entity is submerged. Valid for items and characters.
StatTypes
StatTypes are used to alter several traits of a character. They are mostly used by talents.