5 using Microsoft.Xna.Framework;
13 private const float SteepestWalkableSlopeAngleDegrees = 55f;
14 private const float SlowlyWalkableSlopeAngleDegrees = 30f;
16 private static readonly
float SteepestWalkableSlopeNormalX = MathF.Sin(MathHelper.ToRadians(SteepestWalkableSlopeAngleDegrees));
17 private static readonly
float SlowlyWalkableSlopeNormalX = MathF.Sin(MathHelper.ToRadians(SlowlyWalkableSlopeAngleDegrees));
19 private const float MaxSpeedOnStairs = 1.7f;
20 private const float SteepSlopePushMagnitude = MaxSpeedOnStairs;
37 if (_ragdollParams ==
null)
41 return _ragdollParams;
49 _ragdollParams = value;
63 if (_humanWalkParams ==
null)
67 return _humanWalkParams;
69 set { _humanWalkParams = value; }
77 if (_humanRunParams ==
null)
81 return _humanRunParams;
83 set { _humanRunParams = value; }
91 if (_humanCrouchParams ==
null)
95 return _humanCrouchParams;
97 set { _humanCrouchParams = value; }
105 if (_humanSwimSlowParams ==
null)
109 return _humanSwimSlowParams;
111 set { _humanSwimSlowParams = value; }
119 if (_humanSwimFastParams ==
null)
123 return _humanSwimFastParams;
125 set { _humanSwimFastParams = value; }
160 private float upperLegLength = 0.0f, lowerLegLength = 0.0f;
162 private readonly
float movementLerp;
164 private float cprAnimTimer, cprPumpTimer;
166 private float fallingProneAnimTimer;
167 const float FallingProneAnimDuration = 1.0f;
169 private bool swimming;
172 private float swimmingStateLockTimer;
187 shoulderHeight += 0.4f;
191 shoulderHeight -= 0.15f;
194 bool movingHorizontally = !MathUtils.NearlyEqual(
TargetMovement.X, 0.0f);
195 if (!movingHorizontally)
216 base.Recreate(ragdollParams);
217 CalculateLegLengths();
220 private void CalculateLegLengths()
228 Vector2 localAnchorWaist = Vector2.Zero;
229 Vector2 localAnchorKnee = Vector2.Zero;
230 if (waistJoint !=
null)
232 localAnchorWaist = waistJoint.LimbA.type == upperLegType ? waistJoint.LocalAnchorA : waistJoint.LocalAnchorB;
235 if (kneeJoint !=
null)
237 localAnchorKnee = kneeJoint.LimbA.type == upperLegType ? kneeJoint.LocalAnchorA : kneeJoint.LocalAnchorB;
239 upperLegLength = Vector2.Distance(localAnchorWaist, localAnchorKnee);
242 if (ankleJoint ==
null || kneeJoint ==
null) {
return; }
243 lowerLegLength = Vector2.Distance(
244 kneeJoint.LimbA.type == lowerLegType ? kneeJoint.LocalAnchorA : kneeJoint.LocalAnchorB,
245 ankleJoint.LimbA.type == lowerLegType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB);
246 lowerLegLength += Vector2.Distance(
247 ankleJoint.LimbA.type == footType ? ankleJoint.LocalAnchorA : ankleJoint.LocalAnchorB,
248 GetLimb(footType).PullJointLocalAnchorA);
294 if (fallingProneAnimTimer < FallingProneAnimDuration &&
onGround)
296 fallingProneAnimTimer += deltaTime;
297 UpdateFallingProne(1.0f);
320 fallingProneAnimTimer = 0.0f;
357 if (Math.Abs(angleDiff) > 0.001f)
376 rightHand.Disabled =
true;
377 leftHand.Disabled =
true;
379 Vector2 midPos = waist.SimPosition;
380 Matrix torsoTransform = Matrix.CreateRotationZ(waist.Rotation);
382 midPos += Vector2.Transform(
new Vector2(-0.3f *
Dir, -0.2f), torsoTransform);
383 if (rightHand.PullJointEnabled) midPos = (midPos + rightHand.PullJointWorldAnchorB) / 2.0f;
402 UpdateStandingSimple();
426 UpdateUseItemTimer();
429 UpdateCPR(deltaTime);
433 UpdateUseItemTimer();
434 swimmingStateLockTimer -= deltaTime;
439 else if (swimming !=
inWater && swimmingStateLockTimer <= 0.0f)
444 swimmingStateLockTimer = 0.5f;
458 allowMovement:
false,
467 !controller.ControlCharacterPose || !controller.UserInCorrectPosition)
474 void UpdateUseItemTimer()
516 void UpdateStanding()
519 if (currentGroundedParams ==
null) {
return; }
535 bool onSlopeThatMakesSlow = Math.Abs(
floorNormal.X) > SlowlyWalkableSlopeNormalX;
537 bool onSlopeTooSteepToClimb = Math.Abs(
floorNormal.X) > SteepestWalkableSlopeNormalX;
539 float walkCycleMultiplier = 1.0f;
540 if (
Stairs !=
null || slowedDownBySlope)
543 walkCycleMultiplier *= 1.5f;
551 float slowdownAmount = 0.0f;
561 float maxSpeed = Math.Max(
TargetMovement.Length() - slowdownAmount, 1.0f);
565 float walkPosX = (float)Math.Cos(
WalkPos);
566 float walkPosY = (float)Math.Sin(
WalkPos);
569 stepSize.X *= walkPosX;
570 stepSize.Y *= walkPosY;
572 float footMid = colliderPos.X;
575 float herpesAmount = herpes ==
null ? 0 : herpes.
Strength / herpes.Prefab.MaxStrength;
577 float limpAmount = MathHelper.Lerp(0, 1, legDamage + herpesAmount);
578 if (limpAmount > 0.0f)
581 footMid += (Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(
TargetMovement.X), 0.3f)) *
Dir;
595 if (head ==
null) {
return; }
596 if (torso ==
null) {
return; }
598 bool isNotRemote =
true;
611 getUpForce *= Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f);
613 torso.PullJointEnabled =
true;
614 head.PullJointEnabled =
true;
617 waist.PullJointEnabled =
true;
622 bool movingHorizontally = !MathUtils.NearlyEqual(
TargetMovement.X, 0.0f);
624 if (
Stairs ==
null && onSlopeTooSteepToClimb)
633 if (
Stairs !=
null || onSlope)
635 torso.PullJointWorldAnchorB =
new Vector2(
637 MathHelper.SmoothStep(torso.SimPosition.Y, colliderPos.Y +
TorsoPosition.Value - Math.Abs(walkPosX * 0.05f), getUpForce * 2.0f));
639 head.PullJointWorldAnchorB =
new Vector2(
641 MathHelper.SmoothStep(head.SimPosition.Y, colliderPos.Y +
HeadPosition.Value - Math.Abs(walkPosX * 0.05f), getUpForce * 2.0f));
645 waist.PullJointWorldAnchorB = waist.SimPosition -
movement * 0.06f;
655 float offset = MathHelper.Pi * currentGroundedParams.StepLiftOffset;
658 offset += MathHelper.Pi * currentGroundedParams.StepLiftFrequency;
661 (float)Math.Sin(
WalkPos *
Dir * currentGroundedParams.StepLiftFrequency + offset) * (currentGroundedParams.StepLiftAmount / 100);
663 float y = colliderPos.Y + stepLift;
669 torso.PullJointWorldAnchorB =
670 MathUtils.SmoothStep(torso.SimPosition,
676 y = colliderPos.Y + stepLift * currentGroundedParams.StepLiftHeadMultiplier;
679 head.PullJointWorldAnchorB =
680 MathUtils.SmoothStep(head.SimPosition,
684 if (waist !=
null && !waist.Disabled)
686 waist.PullJointWorldAnchorB = waist.SimPosition +
movement * 0.06f;
695 torsoAngle -= herpesStrength / 150.0f;
696 torso.body.SmoothRotate(torsoAngle *
Dir, currentGroundedParams.TorsoTorque);
700 if (!
Aiming && currentGroundedParams.FixedHeadAngle &&
HeadAngle.HasValue)
704 head.body.SmoothRotate(headAngle *
Dir, currentGroundedParams.HeadTorque);
714 const float MaxFootVelocityDiff = 5.0f;
715 const float MaxFootVelocityDiffSqr = MaxFootVelocityDiff * MaxFootVelocityDiff;
719 if ((leftFoot !=
null && (
MainLimb.
LinearVelocity - leftFoot.LinearVelocity).LengthSquared() > MaxFootVelocityDiffSqr) ||
720 (rightFoot !=
null && (
MainLimb.
LinearVelocity - rightFoot.LinearVelocity).LengthSquared() > MaxFootVelocityDiffSqr))
722 UpdateFallingProne(10.0f, moveHands:
false, moveTorso:
false, moveLegs:
true);
727 Vector2 waistPos = waist !=
null ? waist.SimPosition : torso.SimPosition;
729 if (movingHorizontally)
734 for (
int i = -1; i < 2; i += 2)
736 Limb foot = i == -1 ? leftFoot : rightFoot;
737 if (foot ==
null) {
continue; }
739 Vector2 footPos = stepSize * -i;
742 if (footPos.Y < 0.0f) { footPos.Y = -0.15f; }
746 footPos.X *= MathHelper.Lerp(1.0f, 0.75f, MathHelper.Clamp(footAfflictionStrength / 50.0f, 0.0f, 1.0f));
748 if (currentGroundedParams.FootLiftHorizontalFactor > 0)
753 float min = MathUtils.InverseLerp(1, 0, currentGroundedParams.FootLiftHorizontalFactor);
754 float max = 1 + MathUtils.InverseLerp(0, 1, currentGroundedParams.FootLiftHorizontalFactor);
756 footPos.Y *= xFactor;
759 if (onSlope &&
Stairs ==
null)
763 footPos.Y = Math.Min(waistPos.Y - colliderPos.Y - 0.4f, footPos.Y);
766 if ((i == 1 && Math.Sign(Math.Sin(
WalkPos)) > 0 && Math.Sign(walkPosY) < 0) ||
767 (i == -1 && Math.Sign(Math.Sin(
WalkPos)) < 0 && Math.Sign(walkPosY) > 0))
776 foot.DebugRefPos = colliderPos;
777 foot.DebugTargetPos = colliderPos + footPos;
778 MoveLimb(foot, colliderPos + footPos, currentGroundedParams.FootMoveStrength);
779 FootIK(foot, colliderPos + footPos,
780 currentGroundedParams.LegBendTorque, currentGroundedParams.FootTorque, currentGroundedParams.FootAngleInRadians);
785 handPos = torso.SimPosition;
786 handPos.X = -walkPosX * currentGroundedParams.HandMoveAmount.X;
788 float lowerY = currentGroundedParams.HandClampY;
790 handPos.Y = lowerY + (float)(Math.Abs(Math.Sin(
WalkPos - Math.PI * 1.5f) * currentGroundedParams.HandMoveAmount.Y));
794 if (rightHand !=
null && !rightHand.Disabled)
797 torso.SimPosition + posAddition +
new Vector2(-handPos.X, (Math.Sign(walkPosX) == Math.Sign(
Dir)) ? handPos.Y : lowerY),
798 currentGroundedParams.ArmMoveStrength, currentGroundedParams.HandMoveStrength);
800 if (leftHand !=
null && !leftHand.Disabled)
803 torso.SimPosition + posAddition +
new Vector2(handPos.X, (Math.Sign(walkPosX) == Math.Sign(-
Dir)) ? handPos.Y : lowerY),
804 currentGroundedParams.ArmMoveStrength, currentGroundedParams.HandMoveStrength);
809 for (
int i = -1; i < 2; i += 2)
811 Vector2 footPos = colliderPos;
814 footPos =
new Vector2(Math.Sign(stepSize.X * i) *
Dir * 0.35f, colliderPos.Y);
815 if (Math.Sign(footPos.X) != Math.Sign(
Dir))
820 footPos.X += colliderPos.X;
824 float footPosX = stepSize.X * i * 0.2f;
829 footPos =
new Vector2(colliderPos.X + footPosX, colliderPos.Y - 0.1f);
831 if (
Stairs ==
null && !onSlopeThatMakesSlow)
833 footPos.Y = Math.Max(Math.Min(
FloorY, footPos.Y + 0.5f), footPos.Y);
835 var foot = i == -1 ? rightFoot : leftFoot;
836 if (foot !=
null && !foot.Disabled)
838 foot.DebugRefPos = colliderPos;
839 foot.DebugTargetPos = footPos;
840 float footMoveForce = currentGroundedParams.FootMoveStrength;
841 float legBendTorque = currentGroundedParams.LegBendTorque;
848 MoveLimb(foot, footPos, footMoveForce);
849 FootIK(foot, footPos, legBendTorque, currentGroundedParams.FootTorque, currentGroundedParams.FootAngleInRadians);
853 for (
int i = 0; i < 2; i++)
855 var hand = i == 0 ? rightHand : leftHand;
856 if (hand ==
null || hand.Disabled) {
continue; }
863 if (arm !=
null && Math.Abs(arm.body.AngularVelocity) < 10.0f)
865 arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.5f, 0.5f), arm.Mass * 50.0f * currentGroundedParams.ArmMoveStrength);
869 if (Math.Abs(hand.body.AngularVelocity) < 10.0f)
871 var forearm =
GetLimb(foreArmType) ?? hand;
875 float diff = elbow.JointAngle - (
Dir > 0 ? elbow.LowerLimit : elbow.UpperLimit);
876 forearm.body.ApplyTorque(MathHelper.Clamp(-diff, -MathHelper.PiOver2, MathHelper.PiOver2) * forearm.Mass * 100.0f * currentGroundedParams.ArmMoveStrength);
883 hand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * hand.Mass * 100f * currentGroundedParams.HandMoveStrength);
889 void UpdateStandingSimple()
909 private float handCyclePos;
910 private float legCyclePos;
911 void UpdateSwimming()
916 Vector2 footPos, handPos;
918 float surfaceLimiter = 1.0f;
922 if (head ==
null) {
return; }
923 if (torso ==
null) {
return; }
929 surfaceLimiter = Math.Max(1.0f, surfaceThreshold - surfacePos);
939 rotation = MathHelper.ToDegrees(rotation);
951 if (rotation > 20 && rotation < 170)
955 else if (rotation > 190 && rotation < 340)
964 Vector2 diff = (mousePos - torso.SimPosition) *
Dir;
965 if (diff.LengthSquared() > MathUtils.Pow2(0.4f))
967 float newRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 *
Dir);
971 else if (targetSpeed > 0.1f)
973 float newRotation = MathUtils.VectorToAngle(
TargetMovement) - MathHelper.PiOver2;
999 const float DisableMovementAboveSurfaceThreshold = 50.0f;
1006 head.body.SmoothRotate(0.0f, 5.0f);
1017 head.body.ApplyTorque(
Dir);
1019 movement.Y *= Math.Max(0, 1.0f - ((surfaceLimiter - 1.0f) / DisableMovementAboveSurfaceThreshold));
1023 bool isNotRemote =
true;
1028 float t = movementLerp;
1031 Vector2 forward = VectorExtensions.Forward(
Collider.
Rotation + MathHelper.PiOver2);
1032 float dot = Vector2.Dot(forward, Vector2.Normalize(
movement));
1036 t = MathHelper.Clamp((1 + dot) / 10, 0.01f, 0.1f);
1041 if (surfaceLimiter > DisableMovementAboveSurfaceThreshold)
1052 float legMoveMultiplier = 1.0f;
1053 if (
movement.LengthSquared() < 0.001f)
1056 legMoveMultiplier = 0.3f;
1057 legCyclePos += 0.4f;
1058 handCyclePos += 0.1f;
1062 footPos = waist ==
null ? Vector2.Zero : waist.SimPosition -
new Vector2((
float)Math.Sin(-
Collider.
Rotation), (
float)Math.Cos(-
Collider.
Rotation)) * (upperLegLength + lowerLegLength);
1064 transformedFootPos = Vector2.Transform(transformedFootPos, Matrix.CreateRotationZ(
Collider.
Rotation));
1067 if (rightFoot !=
null && !rightFoot.Disabled)
1071 if (leftFoot !=
null && !leftFoot.Disabled)
1076 handPos = (torso.SimPosition + head.SimPosition) / 2.0f;
1082 handPos += MathUtils.RotatePoint(Vector2.UnitX *
Dir * 0.2f, torso.Rotation);
1084 float wobbleAmount = 0.1f;
1086 if (rightHand !=
null && !rightHand.Disabled)
1089 handPos.X + (
float)Math.Sin(handCyclePos / 1.5f) * wobbleAmount,
1093 if (leftHand !=
null && !leftHand.Disabled)
1096 handPos.X + (
float)Math.Sin(handCyclePos / 2.0f) * wobbleAmount,
1103 handPos += head.LinearVelocity.ClampLength(1.0f) * 0.1f;
1111 Matrix rotationMatrix = Matrix.CreateRotationZ(torso.Rotation);
1113 if (rightHand !=
null && !rightHand.Disabled)
1115 Vector2 rightHandPos =
new Vector2(-handPosX, -handPosY) + handMoveOffset;
1116 rightHandPos.X = (
Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
1117 rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
1121 speedMultiplier = Math.Min(speedMultiplier, 0.1f);
1128 rightHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * rightHand.Mass * 100f *
CurrentSwimParams.
HandMoveStrength);
1132 if (leftHand !=
null && !leftHand.Disabled)
1134 Vector2 leftHandPos =
new Vector2(handPosX, handPosY) + handMoveOffset;
1135 leftHandPos.X = (
Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
1136 leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
1140 speedMultiplier = Math.Min(speedMultiplier, 0.1f);
1147 leftHand.body.ApplyTorque(MathHelper.Clamp(-wrist.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * leftHand.Mass * 100f *
CurrentSwimParams.
HandMoveStrength);
1152 void UpdateFallingProne(
float strength,
bool moveHands =
true,
bool moveTorso =
true,
bool moveLegs =
true)
1154 if (strength <= 0.0f) {
return; }
1159 if (moveHands && head !=
null && head.LinearVelocity.LengthSquared() > 1.0f && !head.IsSevered)
1166 Vector2 protectPos = head.SimPosition + Vector2.Normalize(head.LinearVelocity);
1167 if (rightHand !=
null && !rightHand.IsSevered)
1169 HandIK(rightHand, protectPos, strength * 0.1f);
1171 if (leftHand !=
null && !leftHand.IsSevered)
1173 HandIK(leftHand, protectPos, strength * 0.1f);
1177 if (torso ==
null) {
return; }
1186 float fallDirection = Math.Sign(torso.body.AngularVelocity - torso.body.LinearVelocity.X -
Dir * 0.01f);
1187 float torque = MathF.Cos(torso.Rotation) * fallDirection * 5.0f * strength;
1188 torso.body.ApplyTorque(torque * torso.body.Mass);
1194 for (
int i = 0; i < 2; i++)
1197 if (thigh ==
null) {
continue; }
1198 if (thigh.IsSevered) {
continue; }
1199 float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
1200 float diff = torso.
Rotation - thigh.Rotation;
1201 if (MathUtils.IsValid(diff))
1203 float thighTorque = thighDiff * thigh.Mass * Math.Sign(diff) * 5.0f;
1204 thigh.body.ApplyTorque(thighTorque * strength);
1208 if (leg ==
null || leg.IsSevered) {
continue; }
1209 float legDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, leg.Rotation));
1210 diff = torso.
Rotation - leg.Rotation;
1211 if (MathUtils.IsValid(diff))
1213 float legTorque = legDiff * leg.Mass * Math.Sign(diff) * 5.0f;
1214 leg.body.ApplyTorque(legTorque * strength);
1220 private float lastReviveTime;
1222 private void UpdateCPR(
float deltaTime)
1235 Vector2 offset = Vector2.UnitX * -
Dir * 0.75f;
1237 Limb targetHead = target.AnimController.GetLimb(
LimbType.Head);
1238 Limb targetTorso = target.AnimController.GetLimb(
LimbType.Torso);
1239 if (targetTorso ==
null)
1250 const float CloseEnough = 0.1f;
1259 if (cprAnimTimer <= 0.0f && target.AnimController.Direction ==
TargetDir)
1261 target.AnimController.Flip();
1263 (target.AnimController as
HumanoidAnimController)?.UpdateFallingProne(strength: 1.0f, moveHands:
false, moveTorso:
false);
1265 head.Disabled =
true;
1266 torso.Disabled =
true;
1270 Vector2 handPos = targetTorso.SimPosition + Vector2.UnitY * 0.2f;
1272 Grab(handPos, handPos);
1276 float prevVitality = target.Vitality;
1277 bool wasCritical = prevVitality < 0.0f;
1279 if (GameMain.NetworkMember ==
null || !GameMain.NetworkMember.IsClient)
1281 target.Oxygen += deltaTime * 0.5f;
1288 if (GameMain.NetworkMember is not { IsClient:
true })
1293 target.Oxygen = Math.Max(target.Oxygen, -10.0f);
1298 if (GameMain.NetworkMember is not { IsClient:
true })
1300 if (target.Oxygen < -10.0f)
1303 float stabilizationAmount = skill * CPRSettings.Active.StabilizationPerSkill;
1304 stabilizationAmount = MathHelper.Clamp(stabilizationAmount, CPRSettings.Active.StabilizationMin, CPRSettings.Active.StabilizationMax);
1305 character.Oxygen -= 1.0f / stabilizationAmount * deltaTime;
1306 if (character.Oxygen > 0.0f) { target.Oxygen += stabilizationAmount * deltaTime; }
1310 if (targetHead !=
null && head !=
null)
1312 head.PullJointWorldAnchorB =
new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
1313 head.PullJointEnabled =
true;
1316 torso.PullJointWorldAnchorB =
new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition.Value - 0.1f));
1317 torso.PullJointEnabled =
true;
1319 if (cprPumpTimer >= 1)
1321 torso.body.ApplyLinearImpulse(
new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
1322 targetTorso.body.ApplyLinearImpulse(
new Vector2(0, -20f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
1324 target.DisableImpactDamageTimer = 0.15f;
1327 if (skill < CPRSettings.Active.DamageSkillThreshold)
1329 target.LastDamageSource =
null;
1331 targetTorso.WorldPosition, targetTorso,
1332 new[] { CPRSettings.Active.InsufficientSkillAffliction.Instantiate((CPRSettings.Active.DamageSkillThreshold - skill) * CPRSettings.Active.DamageSkillMultiplier, source: character) },
1335 attackImpulse: Vector2.Zero,
1340 if (cprAnimTimer > 2.0f && GameMain.NetworkMember is not { IsClient: true })
1342 float reviveChance = skill * CPRSettings.Active.ReviveChancePerSkill;
1343 reviveChance = (float)Math.Pow(reviveChance, CPRSettings.Active.ReviveChanceExponent);
1344 reviveChance = MathHelper.Clamp(reviveChance, CPRSettings.Active.ReviveChanceMin, CPRSettings.Active.ReviveChanceMax);
1345 reviveChance *= 1f + cprBoost;
1347 if (Rand.Range(0.0f, 1.0f, Rand.RandSync.ServerAndClient) <= reviveChance)
1351 target.Oxygen = Math.Max(target.Oxygen + 10.0f, 10.0f);
1352 GameMain.LuaCs.Hook.Call(
"human.CPRSuccess",
this);
1356 GameMain.LuaCs.Hook.Call(
"human.CPRFailed",
this);
1360 cprPumpTimer += deltaTime;
1361 cprAnimTimer += deltaTime;
1368 target.CharacterHealth.RecalculateVitality();
1369 if (wasCritical && target.Vitality > 0.0f && Timing.TotalTime > lastReviveTime + 10.0f)
1371 character.Info?.ApplySkillGain(Tags.MedicalSkill, SkillSettings.Current.SkillIncreasePerCprRevive);
1372 AchievementManager.OnCharacterRevived(target, character);
1373 lastReviveTime = (float)Timing.TotalTime;
1375 GameMain.Server?.KarmaManager?.OnCharacterHealthChanged(target, character, damage: Math.Min(prevVitality - target.Vitality, 0.0f), stun: 0.0f);
1379 target.ForgiveAttacker(character);
1386 if (target ==
null) {
return; }
1392 Limb targetLeftHand =
1397 Limb targetRightHand =
1407 bool targetPoseControlled =
1416 character.DeselectCharacter();
1426 if (character.Submarine ==
null && target.
Submarine !=
null)
1430 else if (character.Submarine !=
null && target.
Submarine ==
null)
1432 transformedTorsoPos += character.Submarine.SimPosition;
1436 transformedTorsoPos += character.Submarine.SimPosition;
1459 Collider.ResetDynamics();
1473 for (
int i = 0; i < 2; i++)
1480 targetLimb = targetLeftHand;
1484 targetLimb = targetRightHand;
1491 targetLimb = targetRightHand;
1495 targetLimb = targetLeftHand;
1499 Limb pullLimb = i == 0 ? leftHand : rightHand;
1506 if (character.Submarine !=
null && character.SelectedCharacter.Submarine ==
null)
1508 targetSimPos -= character.Submarine.SimPosition;
1510 else if (character.Submarine ==
null && character.SelectedCharacter.Submarine !=
null)
1512 sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
1514 else if (character.Submarine !=
null && character.SelectedCharacter.Submarine !=
null && character.Submarine != character.SelectedCharacter.Submarine)
1516 targetSimPos += character.SelectedCharacter.Submarine.SimPosition;
1517 targetSimPos -= character.Submarine.SimPosition;
1522 character.DeselectCharacter();
1528 if (i > 0 && inWater) {
continue; }
1532 Vector2 targetAnchor;
1538 if (!character.CanRunWhileDragging())
1540 targetMovement *= MathHelper.Clamp(Mass / target.
Mass, 0.5f, 1.0f);
1543 Vector2 shoulderPos = rightShoulder.WorldAnchorA;
1544 float targetDist = Vector2.Distance(targetLimb.
SimPosition, shoulderPos);
1545 Vector2 dragDir = (targetLimb.
SimPosition - shoulderPos) / targetDist;
1546 if (!MathUtils.IsValid(dragDir)) { dragDir = -Vector2.UnitY; }
1550 dragDir = Vector2.Lerp(dragDir, -Vector2.UnitY, 0.5f);
1553 Vector2 pullLimbAnchor = shoulderPos + dragDir * Math.Min(targetDist, (upperArmLength + forearmLength) * 2);
1554 targetAnchor = shoulderPos + dragDir * (upperArmLength + forearmLength);
1555 targetForce = 200.0f;
1558 if (character.Submarine ==
null)
1565 pullLimbAnchor -= character.Submarine.SimPosition;
1566 targetAnchor += character.Submarine.SimPosition;
1571 pullLimbAnchor += character.Submarine.SimPosition;
1572 targetAnchor -= character.Submarine.SimPosition;
1582 $
"Attempted to move the anchor B of a limb's pull joint extremely far from the limb in {nameof(DragCharacter)}. " +
1583 $
"Character in sub: {character.Submarine != null}, target in sub: {target.Submarine != null}.";
1584 GameAnalyticsManager.AddErrorEventOnce(
"DragCharacter:PullJointTooFar", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1586 DebugConsole.ThrowError(errorMsg);
1599 targetForce = 5000.0f;
1602 if (!targetPoseControlled)
1612 float dist = ConvertUnits.ToSimUnits(Vector2.Distance(target.
WorldPosition, WorldPosition));
1617 character.DeselectCharacter();
1622 if (!character.CanRunWhileDragging() && Vector2.Dot(target.
WorldPosition - WorldPosition, targetMovement) < 0)
1624 targetMovement *= MathHelper.Clamp(1.5f - dist, 0.0f, 1.0f);
1644 Vector2 movement = (character.SimPosition + Vector2.UnitX * 0.5f * Math.Sign(target.
SimPosition.X - character.SimPosition.X)) - target.
SimPosition;
1653 character.SetInput(
InputType.Crouch, hit:
false, held:
true);
1656 private void FootIK(
Limb foot, Vector2 pos,
float legTorque,
float footTorque,
float footAngle)
1658 if (!MathUtils.IsValid(pos))
1660 string errorMsg =
"Invalid foot position in FootIK (" + pos +
")\n" + Environment.StackTrace.CleanupStackTrace();
1662 DebugConsole.ThrowError(errorMsg);
1664 GameAnalyticsManager.AddErrorEventOnce(
"FootIK:InvalidPos", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1668 Limb upperLeg, lowerLeg;
1669 if (foot.
type == LimbType.LeftFoot)
1671 upperLeg = GetLimb(LimbType.LeftThigh);
1672 lowerLeg = GetLimb(LimbType.LeftLeg);
1676 upperLeg = GetLimb(
LimbType.RightThigh);
1677 lowerLeg = GetLimb(
LimbType.RightLeg);
1679 Limb torso = GetLimb(
LimbType.Torso);
1680 LimbJoint waistJoint = GetJointBetweenLimbs(
LimbType.Waist, upperLeg.type) ?? GetJointBetweenLimbs(
LimbType.Torso, upperLeg.type);
1681 Vector2 waistPos = Vector2.Zero;
1682 if (waistJoint !=
null)
1684 waistPos = waistJoint.LimbA == upperLeg ? waistJoint.WorldAnchorA : waistJoint.WorldAnchorB;
1688 float c = Vector2.Distance(pos, waistPos);
1689 c = Math.Max(c, Math.Abs(upperLegLength - lowerLegLength));
1691 float legAngle = MathUtils.VectorToAngle(pos - waistPos) + MathHelper.PiOver2;
1692 if (!MathUtils.IsValid(legAngle))
1694 string errorMsg =
"Invalid leg angle (" + legAngle +
") in FootIK. Waist pos: " + waistPos +
", target pos: " + pos +
"\n" + Environment.StackTrace.CleanupStackTrace();
1696 DebugConsole.ThrowError(errorMsg);
1698 GameAnalyticsManager.AddErrorEventOnce(
"FootIK:InvalidAngle", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1704 while (torso.Rotation - legAngle > MathHelper.Pi)
1706 legAngle += MathHelper.TwoPi;
1708 while (torso.Rotation - legAngle < -MathHelper.Pi)
1710 legAngle -= MathHelper.TwoPi;
1714 float upperLegAngle = c >= upperLegLength + lowerLegLength ? 0.0f : MathUtils.SolveTriangleSSS(lowerLegLength, upperLegLength, c);
1715 float lowerLegAngle = c >= upperLegLength + lowerLegLength ? 0.0f : MathUtils.SolveTriangleSSS(upperLegLength, lowerLegLength, c);
1717 upperLeg.body.SmoothRotate((legAngle + upperLegAngle * Dir), upperLeg.Mass * legTorque, wrapAngle:
false);
1718 lowerLeg.body.SmoothRotate((legAngle - lowerLegAngle * Dir), lowerLeg.Mass * legTorque, wrapAngle:
false);
1719 foot.
body.
SmoothRotate((legAngle - (lowerLegAngle + footAngle) * Dir), foot.
Mass * footTorque, wrapAngle:
false);
1726 LogAccessedRemovedCharacterError();
1735 if (torso ==
null) {
return; }
1737 Matrix torsoTransform = Matrix.CreateRotationZ(torso.
Rotation);
1740 foreach (
Item heldItem
in character.HeldItems)
1742 if (heldItem?.body !=
null && !heldItem.
Removed && heldItem.GetComponent<
Holdable>() !=
null)
1744 heldItem.
FlipX(relativeToSub:
false);
1748 foreach (
Limb limb
in Limbs)
1752 bool mirror =
false;
1753 bool flipAngle =
false;
1754 bool wrapAngle =
false;
1772 mirror = Crouching && !inWater;
1773 flipAngle = (limb.
DoesFlip || Crouching) && !inWater;
1774 wrapAngle = !inWater;
1777 flipAngle = limb.
DoesFlip && !inWater;
1778 wrapAngle = !inWater;
1787 difference = Vector2.Transform(difference, torsoTransform);
1788 difference.Y = -difference.Y;
1790 position = torso.SimPosition + Vector2.Transform(difference, -torsoTransform);
1796 if (wrapAngle) { angle = MathUtils.WrapAnglePi(angle); }
1798 TrySetLimbPosition(limb, Collider.
SimPosition, position, angle);
1808 DebugConsole.ThrowError($
"{character.SpeciesName} cannot crouch!");
1813 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
void UpdateBlink(float deltaTime)
void UpdateConstantTorque(float deltaTime)
void RotateHead(Limb head)
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)
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 GetSkillLevel(Identifier skillIdentifier)
Get the character's current skill level, taking into account any temporary boosts from wearables and ...
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)
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 ControlCharacterPose
Vector2 PullJointWorldAnchorB
Vector2 PullJointWorldAnchorA
bool SetTransform(Vector2 simPosition, float rotation, bool setPrevTransform=true)
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.
Limb GetLimb(LimbType limbType, bool excludeSevered=true, bool excludeLimbsWithSecondaryType=false, bool useSecondaryType=false)
Note that if there are multiple limbs of the same type, only the first (valid) limb is returned.
bool? SimplePhysicsEnabled
Vector2 overrideTargetMovement
readonly Character character
float GetSurfaceY()
Get the position of the surface of water at the position of the character, in display units (taking i...
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.