Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs
T
Alex Noir e56f5c4946 what started as a fix of https://github.com/Regalis11/Barotrauma/issues/103 ended up being a huge CPR overhaul. oops.
CPR:
There's now a difference between CPR on bleeding, CPR on hurting and CPR on oxygen deprivation.
If you try to CPR bleeding people, it will make bloody sounds and particles while hurting them. So don't.
If you CPR people with less than 0 health, it will do RNG based on your skill level to bring them back to life with 2 HP.
Otherwise if you CPR their oxygen back, chest pumps will simply prevent oxygen deprivation and mouth-to-mouth will bring back their oxygen while taking yours based on the skill level.

Crit:
Changed it so you always lose oxygen when critical. Your heart stopped either way!
2017-12-19 22:11:45 +03:00

1360 lines
54 KiB
C#

using Barotrauma.Items.Components;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class HumanoidAnimController : AnimController
{
public bool Crouching;
private bool aiming;
private float walkAnimSpeed;
private float movementLerp;
private float thighTorque;
private float cprAnimState;
private float cprPump;
private float inWaterTimer;
private bool swimming;
protected override float TorsoPosition
{
get
{
return Crouching ? base.TorsoPosition - base.HeadPosition * 0.3f : base.TorsoPosition;
}
}
protected override float TorsoAngle
{
get
{
return Crouching ? base.TorsoAngle + 0.5f : base.TorsoAngle;
}
}
public override Vector2 AimSourceSimPos
{
get
{
float shoulderHeight = Collider.height / 2.0f - 0.1f;
if (inWater)
{
shoulderHeight += 0.4f;
}
else if (Crouching)
{
shoulderHeight -= 0.15f;
}
return Collider.SimPosition + new Vector2(
(float)Math.Sin(-Collider.Rotation),
(float)Math.Cos(-Collider.Rotation)) * shoulderHeight;
}
}
public HumanoidAnimController(Character character, XElement element)
: base(character, element)
{
walkAnimSpeed = element.GetAttributeFloat("walkanimspeed", 4.0f);
walkAnimSpeed = MathHelper.ToRadians(walkAnimSpeed);
movementLerp = element.GetAttributeFloat("movementlerp", 0.4f);
thighTorque = element.GetAttributeFloat("thightorque", -5.0f);
}
public override void UpdateAnim(float deltaTime)
{
if (Frozen) return;
levitatingCollider = true;
ColliderIndex = Crouching ? 1 : 0;
if (!Crouching && ColliderIndex == 1) Crouching = true;
if (!character.AllowInput)
{
levitatingCollider = false;
Collider.FarseerBody.FixedRotation = false;
if (Math.Abs(Collider.Rotation-GetLimb(LimbType.Torso).Rotation)>Math.PI*0.6f)
{
Collider.SetTransform(Collider.SimPosition, MathHelper.WrapAngle(Collider.Rotation + (float)Math.PI));
}
Collider.SmoothRotate(GetLimb(LimbType.Torso).Rotation);
Collider.LinearVelocity = (GetLimb(LimbType.Waist).SimPosition - Collider.SimPosition) * 20.0f;
return;
}
//stun (= disable the animations) if the ragdoll receives a large enough impact
if (strongestImpact > 0.0f)
{
character.SetStun(MathHelper.Min(strongestImpact * 0.5f, 5.0f));
strongestImpact = 0.0f;
return;
}
//re-enable collider
if (!Collider.FarseerBody.Enabled)
{
var lowestLimb = FindLowestLimb();
Collider.SetTransform(new Vector2(
Collider.SimPosition.X,
Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)),
Collider.Rotation);
Collider.FarseerBody.ResetDynamics();
Collider.FarseerBody.Enabled = true;
}
if (swimming)
{
Collider.FarseerBody.FixedRotation = false;
}
else if (!Collider.FarseerBody.FixedRotation)
{
if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, 0.0f)) > 0.001f)
{
//rotate collider back upright
Collider.AngularVelocity = MathUtils.GetShortestAngle(Collider.Rotation, 0.0f) * 10.0f;
Collider.FarseerBody.FixedRotation = false;
}
else
{
Collider.FarseerBody.FixedRotation = true;
}
}
if (character.LockHands)
{
var leftHand = GetLimb(LimbType.LeftHand);
var rightHand = GetLimb(LimbType.RightHand);
var waist = GetLimb(LimbType.Waist);
rightHand.Disabled = true;
leftHand.Disabled = true;
Vector2 midPos = waist.SimPosition;
Matrix torsoTransform = Matrix.CreateRotationZ(waist.Rotation);
midPos += Vector2.Transform(new Vector2(-0.3f * Dir, -0.2f), torsoTransform);
if (rightHand.pullJoint.Enabled) midPos = (midPos + rightHand.pullJoint.WorldAnchorB) / 2.0f;
HandIK(rightHand, midPos);
HandIK(leftHand, midPos);
}
else
{
if (Anim != Animation.UsingConstruction) ResetPullJoints();
}
if (SimplePhysicsEnabled)
{
UpdateStandingSimple();
return;
}
switch (Anim)
{
case Animation.Climbing:
levitatingCollider = false;
UpdateClimbing();
break;
case Animation.CPR:
UpdateCPR(deltaTime);
break;
case Animation.UsingConstruction:
default:
if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter);
//0.5 second delay for switching between swimming and walking
//prevents rapid switches between swimming/walking if the water level is fluctuating around the minimum swimming depth
if (inWater)
{
inWaterTimer = Math.Max(inWaterTimer + deltaTime, 0.5f);
if (inWaterTimer >= 1.0f) swimming = true;
}
else
{
inWaterTimer = Math.Min(inWaterTimer - deltaTime, 0.5f);
if (inWaterTimer <= 0.0f) swimming = false;
}
if (swimming)
{
UpdateSwimming();
}
else
{
UpdateStanding();
}
break;
}
if (TargetDir != dir) Flip();
foreach (Limb limb in Limbs)
{
limb.Disabled = false;
}
aiming = false;
if (character.IsRemotePlayer && GameMain.Server == null) Collider.LinearVelocity = Vector2.Zero;
}
void UpdateStanding()
{
Vector2 handPos;
//if you're allergic to magic numbers, stop reading now
Limb leftFoot = GetLimb(LimbType.LeftFoot);
Limb rightFoot = GetLimb(LimbType.RightFoot);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Limb waist = GetLimb(LimbType.Waist);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
Limb leftLeg = GetLimb(LimbType.LeftLeg);
Limb rightLeg = GetLimb(LimbType.RightLeg);
float getUpSpeed = 0.3f;
float walkCycleSpeed = movement.X * walkAnimSpeed;
if (Stairs != null)
{
TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -1.5f, 1.5f), TargetMovement.Y);
/*if ((TargetMovement.X > 0.0f && stairs.StairDirection == Direction.Right) ||
TargetMovement.X < 0.0f && stairs.StairDirection == Direction.Left)
{
TargetMovement *= 1.7f;
//walkCycleSpeed *= 1.0f;
}*/
}
Vector2 colliderPos = GetColliderBottom();
if (Math.Abs(TargetMovement.X) > 1.0f)
{
float slowdownAmount = 0.0f;
if (currentHull != null)
{
//full slowdown (1.5f) when water is up to the torso
surfaceY = ConvertUnits.ToSimUnits(currentHull.Surface);
slowdownAmount = MathHelper.Clamp((surfaceY - colliderPos.Y) / torsoPosition, 0.0f, 1.0f) * 1.5f;
}
float maxSpeed = Math.Max(TargetMovement.Length() - slowdownAmount, 1.0f);
TargetMovement = Vector2.Normalize(TargetMovement) * maxSpeed;
}
float walkPosX = (float)Math.Cos(walkPos);
float walkPosY = (float)Math.Sin(walkPos);
float runningModifier = (float)Math.Max(Math.Min(Math.Abs(TargetMovement.X), 3.0f) / 1.5f, 1.0);
Vector2 stepSize = new Vector2(
this.stepSize.X * walkPosX * runningModifier,
this.stepSize.Y * walkPosY * runningModifier * runningModifier);
if (Crouching) stepSize *= 0.5f;
float footMid = colliderPos.X;// (leftFoot.SimPosition.X + rightFoot.SimPosition.X) / 2.0f;
movement = overrideTargetMovement == Vector2.Zero ?
MathUtils.SmoothStep(movement, TargetMovement * walkSpeed, movementLerp) :
overrideTargetMovement;
if (Math.Abs(movement.X) < 0.005f)
{
movement.X = 0.0f;
}
movement.Y = 0.0f;
for (int i = 0; i < 2; i++)
{
Limb leg = GetLimb((i == 0) ? LimbType.LeftThigh : LimbType.RightThigh);// : leftLeg;
float shortestAngle = leg.Rotation - torso.Rotation;
if (Math.Abs(shortestAngle) < 2.5f) continue;
if (Math.Abs(shortestAngle) > 5.0f)
{
TargetDir = TargetDir == Direction.Right ? Direction.Left : Direction.Right;
}
else
{
leg.body.ApplyTorque(shortestAngle * 10.0f);
leg = GetLimb((i == 0) ? LimbType.LeftLeg : LimbType.RightLeg);
leg.body.ApplyTorque(-shortestAngle * 10.0f);
}
}
if (onGround && (!character.IsRemotePlayer || GameMain.Server != null))
{
//move slower if collider isn't upright
float rotationFactor = (float)Math.Abs(Math.Cos(Collider.Rotation));
Collider.LinearVelocity = new Vector2(
movement.X * rotationFactor,
Collider.LinearVelocity.Y > 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y);
}
getUpSpeed = getUpSpeed * Math.Max(head.SimPosition.Y - colliderPos.Y, 0.5f);
torso.pullJoint.Enabled = true;
head.pullJoint.Enabled = true;
waist.pullJoint.Enabled = true;
float floorPos = GetFloorY(colliderPos + new Vector2(Math.Sign(movement.X) * 0.5f, 1.0f));
bool onSlope = floorPos > GetColliderBottom().Y + 0.05f;
if (Stairs != null || onSlope)
{
torso.pullJoint.WorldAnchorB = new Vector2(
MathHelper.SmoothStep(torso.SimPosition.X, footMid + movement.X * 0.25f, getUpSpeed * 0.8f),
MathHelper.SmoothStep(torso.SimPosition.Y, colliderPos.Y + TorsoPosition - Math.Abs(walkPosX * 0.05f), getUpSpeed * 2.0f));
head.pullJoint.WorldAnchorB = new Vector2(
MathHelper.SmoothStep(head.SimPosition.X, footMid + movement.X * (Crouching ? 0.6f : 0.25f), getUpSpeed * 0.8f),
MathHelper.SmoothStep(head.SimPosition.Y, colliderPos.Y + HeadPosition - Math.Abs(walkPosX * 0.05f), getUpSpeed * 2.0f));
waist.pullJoint.WorldAnchorB = waist.SimPosition;// +movement * 0.3f;
}
else
{
if (!onGround) movement = Vector2.Zero;
torso.pullJoint.WorldAnchorB =
MathUtils.SmoothStep(torso.SimPosition,
new Vector2(footMid + movement.X * 0.2f, colliderPos.Y + TorsoPosition), getUpSpeed);
head.pullJoint.WorldAnchorB =
MathUtils.SmoothStep(head.SimPosition,
new Vector2(footMid + movement.X * (Crouching && Math.Sign(movement.X) == Math.Sign(Dir) ? 0.6f : 0.2f), colliderPos.Y + HeadPosition), getUpSpeed * 1.2f);
waist.pullJoint.WorldAnchorB = waist.SimPosition + movement * 0.06f;
}
if (!onGround)
{
Vector2 move = torso.pullJoint.WorldAnchorB - torso.SimPosition;
foreach (Limb limb in Limbs)
{
MoveLimb(limb, limb.SimPosition+move, 15.0f, true);
}
return;
}
//moving horizontally
if (TargetMovement.X != 0.0f)
{
//progress the walking animation
walkPos -= (walkCycleSpeed / runningModifier) * 0.8f;
for (int i = -1; i < 2; i += 2)
{
Limb foot = i == -1 ? leftFoot : rightFoot;
Limb leg = i == -1 ? leftLeg : rightLeg;
Vector2 footPos = stepSize * -i;
if (stepSize.Y < 0.0f) stepSize.Y = -0.15f;
if (onSlope && Stairs == null)
{
footPos.Y *= 2.0f;
}
footPos.Y = Math.Min(waist.SimPosition.Y - colliderPos.Y - 0.4f, footPos.Y);
MoveLimb(foot, footPos + colliderPos, 15.0f, true);
foot.body.SmoothRotate(leg.body.Rotation + MathHelper.PiOver2 * Dir * 1.6f, 20.0f * runningModifier);
}
if (runningModifier > 1.0f)
{
if (walkPosY > 0.0f)
{
GetLimb(LimbType.LeftThigh).body.ApplyTorque(-walkPosY * Dir * Math.Abs(movement.X) * thighTorque);
}
else
{
GetLimb(LimbType.RightThigh).body.ApplyTorque(walkPosY * Dir * Math.Abs(movement.X) * thighTorque);
}
}
if (legTorque > 0.0f)
{
if (Math.Sign(walkPosX) != Math.Sign(movement.X))
{
GetLimb(LimbType.LeftLeg).body.ApplyTorque(-walkPosY * Dir * Math.Abs(movement.X) * legTorque / runningModifier);
}
else
{
GetLimb(LimbType.RightLeg).body.ApplyTorque(walkPosY * Dir * Math.Abs(movement.X) * legTorque / runningModifier);
}
}
//calculate the positions of hands
handPos = torso.SimPosition;
handPos.X = -walkPosX * 0.4f;
float lowerY = -1.0f + (runningModifier - 1.0f) * 0.8f;
handPos.Y = lowerY + (float)(Math.Abs(Math.Sin(walkPos - Math.PI * 1.5f) * 0.15 * runningModifier));
Vector2 posAddition = new Vector2(-movement.X * 0.015f * runningModifier, 0.0f);
if (!rightHand.Disabled)
{
HandIK(rightHand, torso.SimPosition + posAddition +
new Vector2(
-handPos.X,
(Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY), 0.7f * runningModifier);
}
if (!leftHand.Disabled)
{
HandIK(leftHand, torso.SimPosition + posAddition +
new Vector2(
handPos.X,
(Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY), 0.7f * runningModifier);
}
}
else
{
//float movementFactor = (movement.X / 4.0f) * movement.X * Math.Sign(movement.X);
for (int i = -1; i < 2; i += 2)
{
Vector2 footPos = colliderPos;
if (Crouching)
{
footPos = new Vector2(
waist.SimPosition.X + Math.Sign(stepSize.X * i) * Dir * 0.3f,
colliderPos.Y - 0.1f);
}
else
{
footPos = new Vector2(GetCenterOfMass().X + stepSize.X * i * 0.2f, colliderPos.Y - 0.1f);
}
if (Stairs == null)
{
footPos.Y = Math.Max(Math.Min(floorPos, footPos.Y + 0.5f), footPos.Y);
}
var foot = i == -1 ? rightFoot : leftFoot;
MoveLimb(foot, footPos, Math.Abs(foot.SimPosition.X - footPos.X) * 100.0f, true);
}
leftFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 50.0f);
rightFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 50.0f);
if (!rightHand.Disabled)
{
rightHand.body.SmoothRotate(0.0f, 5.0f);
var rightArm = GetLimb(LimbType.RightArm);
rightArm.body.SmoothRotate(0.0f, 20.0f);
}
if (!leftHand.Disabled)
{
leftHand.body.SmoothRotate(0.0f, 5.0f);
var leftArm = GetLimb(LimbType.LeftArm);
leftArm.body.SmoothRotate(0.0f, 20.0f);
}
}
}
void UpdateStandingSimple()
{
movement = MathUtils.SmoothStep(movement, TargetMovement, movementLerp);
if (inWater && movement.LengthSquared() > 0.00001f)
{
movement = Vector2.Normalize(movement);
}
if (Math.Abs(movement.X)<0.005f)
{
movement.X = 0.0f;
}
}
private void ClimbOverObstacles()
{
if (Collider.FarseerBody.ContactList == null || Math.Abs(movement.X) < 0.01f) return;
//check if the collider is touching a suitable obstacle to climb over
Vector2? handle = null;
FarseerPhysics.Dynamics.Contacts.ContactEdge ce = Collider.FarseerBody.ContactList;
while (ce != null && ce.Contact != null)
{
if (ce.Contact.Enabled && ce.Contact.IsTouching && ce.Contact.FixtureA.CollisionCategories.HasFlag(Physics.CollisionWall))
{
Vector2 contactNormal;
FarseerPhysics.Common.FixedArray2<Vector2> contactPos;
ce.Contact.GetWorldManifold(out contactNormal, out contactPos);
//only climb if moving towards the obstacle
if (Math.Sign(contactPos[0].X - Collider.SimPosition.X) == Math.Sign(movement.X) &&
(handle == null || contactPos[0].Y > ((Vector2)handle).Y))
{
handle = contactPos[0];
}
}
ce = ce.Next;
}
if (handle == null) return;
float colliderBottomY = GetColliderBottom().Y;
//the contact point should be higher than the bottom of the collider
if (((Vector2)handle).Y < colliderBottomY + 0.01f ||
((Vector2)handle).Y > Collider.SimPosition.Y) return;
//find the height of the floor below the torso
//(if moving towards towards an obstacle that's low enough to climb over, the torso should be above it)
float obstacleY = GetFloorY(GetLimb(LimbType.Torso));
if (obstacleY > colliderBottomY)
{
//higher vertical velocity for taller obstacles
Collider.LinearVelocity += Vector2.UnitY * (((Vector2)handle).Y - colliderBottomY + 0.01f) * 50;
onGround = true;
}
}
void UpdateSwimming()
{
IgnorePlatforms = true;
Vector2 footPos, handPos;
float surfaceLimiter = 1.0f;
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
if (currentHull != null && (currentHull.Rect.Y - currentHull.Surface > 50.0f))
{
surfaceLimiter = (ConvertUnits.ToDisplayUnits(Collider.SimPosition.Y + 0.4f) - surfaceY);
surfaceLimiter = Math.Max(1.0f, surfaceLimiter);
if (surfaceLimiter > 50.0f) return;
}
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
Limb leftFoot = GetLimb(LimbType.LeftFoot);
Limb rightFoot = GetLimb(LimbType.RightFoot);
float rotation = MathHelper.WrapAngle(Collider.Rotation);
rotation = MathHelper.ToDegrees(rotation);
if (rotation < 0.0f) rotation += 360;
if (!character.IsRemotePlayer && !aiming && Anim != Animation.UsingConstruction)
{
if (rotation > 20 && rotation < 170)
TargetDir = Direction.Left;
else if (rotation > 190 && rotation < 340)
TargetDir = Direction.Right;
}
float targetSpeed = TargetMovement.Length();
if (targetSpeed > 0.1f)
{
if (!aiming)
{
float newRotation = MathUtils.VectorToAngle(TargetMovement) - MathHelper.PiOver2;
Collider.SmoothRotate(newRotation, 5.0f);
//torso.body.SmoothRotate(newRotation);
}
}
else
{
if (aiming)
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
Vector2 diff = (mousePos - torso.SimPosition) * Dir;
TargetMovement = new Vector2(0.0f, -0.1f);
float newRotation = MathUtils.VectorToAngle(diff);
Collider.SmoothRotate(newRotation, 5.0f);
}
}
torso.body.SmoothRotate(Collider.Rotation);
torso.body.MoveToPos(Collider.SimPosition + new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation))*0.4f, 5.0f);
if (TargetMovement == Vector2.Zero) return;
movement = MathUtils.SmoothStep(movement, TargetMovement, 0.3f);
//dont try to move upwards if head is already out of water
if (surfaceLimiter > 1.0f && TargetMovement.Y > 0.0f)
{
if (TargetMovement.X == 0.0f)
{
//pull head above water
head.body.SmoothRotate(0.0f, 5.0f);
walkPos += 0.05f;
}
else
{
TargetMovement = new Vector2(
(float)Math.Sqrt(targetSpeed * targetSpeed - TargetMovement.Y * TargetMovement.Y)
* Math.Sign(TargetMovement.X),
Math.Max(TargetMovement.Y, TargetMovement.Y * 0.2f));
//turn head above the water
head.body.ApplyTorque(Dir);
}
movement.Y = movement.Y - (surfaceLimiter - 1.0f) * 0.01f;
}
if (!character.IsRemotePlayer || GameMain.Server != null)
{
Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement * swimSpeed, movementLerp);
}
walkPos += movement.Length() * 0.2f;
footPos = Collider.SimPosition - new Vector2((float)Math.Sin(-Collider.Rotation), (float)Math.Cos(-Collider.Rotation)) * 0.4f;
for (int i = -1; i<2; i+=2)
{
var thigh = i == -1 ? GetLimb(LimbType.LeftThigh) : GetLimb(LimbType.RightThigh);
var leg = i == -1 ? GetLimb(LimbType.LeftLeg) : GetLimb(LimbType.RightLeg);
float thighDiff = Math.Abs(MathUtils.GetShortestAngle(torso.Rotation, thigh.Rotation));
if (thighDiff > MathHelper.PiOver2)
{
//thigh bent too close to the torso -> force the leg to extend
float thighTorque = thighDiff * thigh.Mass * Math.Sign(torso.Rotation - thigh.Rotation) * 10.0f;
thigh.body.ApplyTorque(thighTorque);
leg.body.ApplyTorque(thighTorque);
}
else
{
thigh.body.SmoothRotate(torso.Rotation + (float)Math.Sin(walkPos) * i * 0.3f, 2.0f);
}
}
Vector2 transformedFootPos = new Vector2((float)Math.Sin(walkPos) * 0.5f, 0.0f);
transformedFootPos = Vector2.Transform(
transformedFootPos,
Matrix.CreateRotationZ(Collider.Rotation));
MoveLimb(rightFoot, footPos - transformedFootPos, 1.0f);
MoveLimb(leftFoot, footPos + transformedFootPos, 1.0f);
handPos = (torso.SimPosition + head.SimPosition) / 2.0f;
//at the surface, not moving sideways -> hands just float around
if (!headInWater && TargetMovement.X == 0.0f && TargetMovement.Y > 0)
{
handPos.X = handPos.X + Dir * 0.6f;
float wobbleAmount = 0.1f;
if (!rightHand.Disabled)
{
MoveLimb(rightHand, new Vector2(
handPos.X + (float)Math.Sin(walkPos / 1.5f) * wobbleAmount,
handPos.Y + (float)Math.Sin(walkPos / 3.5f) * wobbleAmount - 0.25f), 1.5f);
}
if (!leftHand.Disabled)
{
MoveLimb(leftHand, new Vector2(
handPos.X + (float)Math.Sin(walkPos / 2.0f) * wobbleAmount,
handPos.Y + (float)Math.Sin(walkPos / 3.0f) * wobbleAmount - 0.25f), 1.5f);
}
return;
}
handPos += head.LinearVelocity * 0.1f;
float handCyclePos = walkPos / 2.0f * -Dir;
float handPosX = (float)Math.Cos(handCyclePos) * 0.4f;
float handPosY = (float)Math.Sin(handCyclePos) * 1.0f;
handPosY = MathHelper.Clamp(handPosY, -0.8f, 0.8f);
Matrix rotationMatrix = Matrix.CreateRotationZ(torso.Rotation);
if (!rightHand.Disabled)
{
Vector2 rightHandPos = new Vector2(-handPosX, -handPosY);
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
HandIK(rightHand, handPos + rightHandPos, 0.5f);
}
if (!leftHand.Disabled)
{
Vector2 leftHandPos = new Vector2(handPosX, handPosY);
leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
HandIK(leftHand, handPos + leftHandPos, 0.5f);
}
}
void UpdateClimbing()
{
if (character.SelectedConstruction == null || character.SelectedConstruction.GetComponent<Ladder>() == null)
{
Anim = Animation.None;
return;
}
onGround = false;
IgnorePlatforms = true;
Vector2 tempTargetMovement = TargetMovement;
tempTargetMovement.Y = Math.Min(tempTargetMovement.Y, 1.0f);
movement = MathUtils.SmoothStep(movement, tempTargetMovement, 0.3f);
Limb leftFoot = GetLimb(LimbType.LeftFoot);
Limb rightFoot = GetLimb(LimbType.RightFoot);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Limb waist = GetLimb(LimbType.Waist);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
Vector2 ladderSimPos = ConvertUnits.ToSimUnits(
character.SelectedConstruction.Rect.X + character.SelectedConstruction.Rect.Width / 2.0f,
character.SelectedConstruction.Rect.Y);
float stepHeight = ConvertUnits.ToSimUnits(30.0f);
if (currentHull == null && character.SelectedConstruction.Submarine != null)
{
ladderSimPos += character.SelectedConstruction.Submarine.SimPosition;
}
else if (currentHull.Submarine != null && currentHull.Submarine != character.SelectedConstruction.Submarine)
{
ladderSimPos += character.SelectedConstruction.Submarine.SimPosition - currentHull.Submarine.SimPosition;
}
MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.9f - colliderHeightFromFloor), 10.5f);
MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, Collider.SimPosition.Y + 0.7f - colliderHeightFromFloor), 10.5f);
MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, Collider.SimPosition.Y + 0.6f - colliderHeightFromFloor), 10.5f);
Collider.MoveToPos(new Vector2(ladderSimPos.X - 0.2f * Dir, Collider.SimPosition.Y), 10.5f);
bool slide = targetMovement.Y < -1.1f;
Vector2 handPos = new Vector2(
ladderSimPos.X,
Collider.SimPosition.Y + 0.8f + movement.Y * 0.1f - ladderSimPos.Y);
handPos.Y = Math.Min(-0.2f, handPos.Y) - colliderHeightFromFloor;
MoveLimb(leftHand,
new Vector2(handPos.X,
(slide ? handPos.Y : MathUtils.Round(handPos.Y - stepHeight, stepHeight * 2.0f) + stepHeight) + ladderSimPos.Y),
5.2f);
MoveLimb(rightHand,
new Vector2(handPos.X,
(slide ? handPos.Y : MathUtils.Round(handPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y),
5.2f);
leftHand.body.ApplyTorque(Dir * 2.0f);
rightHand.body.ApplyTorque(Dir * 2.0f);
Vector2 footPos = new Vector2(
handPos.X - Dir * 0.05f,
Collider.SimPosition.Y + 0.9f - colliderHeightFromFloor - stepHeight * 2.7f - ladderSimPos.Y - 0.7f);
//if (movement.Y < 0) footPos.Y += 0.05f;
MoveLimb(leftFoot,
new Vector2(footPos.X,
(slide ? footPos.Y : MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight) + ladderSimPos.Y),
15.5f, true);
MoveLimb(rightFoot,
new Vector2(footPos.X,
(slide ? footPos.Y : MathUtils.Round(footPos.Y, stepHeight * 2.0f)) + ladderSimPos.Y),
15.5f, true);
//apply torque to the legs to make the knees bend
Limb leftLeg = GetLimb(LimbType.LeftLeg);
Limb rightLeg = GetLimb(LimbType.RightLeg);
leftLeg.body.ApplyTorque(Dir * -8.0f);
rightLeg.body.ApplyTorque(Dir * -8.0f);
float movementFactor = (handPos.Y / stepHeight) * (float)Math.PI;
movementFactor = 0.8f + (float)Math.Abs(Math.Sin(movementFactor));
Vector2 subSpeed = currentHull != null || character.SelectedConstruction.Submarine == null
? Vector2.Zero : character.SelectedConstruction.Submarine.Velocity;
Vector2 climbForce = new Vector2(0.0f, movement.Y + 0.3f) * movementFactor;
//if (climbForce.Y > 0.5f) climbForce.Y = Math.Max(climbForce.Y, 1.3f);
//apply forces to the collider to move the Character up/down
Collider.ApplyForce((climbForce * 20.0f + subSpeed * 50.0f) * Collider.Mass);
head.body.SmoothRotate(0.0f);
if (!character.SelectedConstruction.Prefab.Triggers.Any())
{
character.SelectedConstruction = null;
return;
}
Rectangle trigger = character.SelectedConstruction.Prefab.Triggers.FirstOrDefault();
trigger = character.SelectedConstruction.TransformTrigger(trigger);
bool notClimbing = false;
if (character.IsRemotePlayer && GameMain.Server == null)
{
notClimbing = character.IsKeyDown(InputType.Left) || character.IsKeyDown(InputType.Right);
}
else
{
notClimbing = Math.Abs(targetMovement.X) > 0.05f ||
(TargetMovement.Y < 0.0f && ConvertUnits.ToSimUnits(trigger.Height) + handPos.Y < HeadPosition) ||
(TargetMovement.Y > 0.0f && handPos.Y > 0.1f);
}
if (notClimbing)
{
Anim = Animation.None;
character.SelectedConstruction = null;
IgnorePlatforms = false;
}
else if (character.SelectedCharacter != null && !character.SelectedCharacter.AllowInput)
{
Limb targetLeftHand = character.SelectedCharacter.AnimController.GetLimb(LimbType.LeftHand);
Limb targetRightHand = character.SelectedCharacter.AnimController.GetLimb(LimbType.RightHand);
Limb targetTorso = character.SelectedCharacter.AnimController.GetLimb(LimbType.Torso);
if (character.SelectedCharacter.AnimController.Dir != Dir)
character.SelectedCharacter.AnimController.Flip();
targetTorso.pullJoint.Enabled = true;
targetTorso.pullJoint.WorldAnchorB = torso.SimPosition + (Vector2.UnitX * -Dir) * 0.2f;
targetTorso.pullJoint.MaxForce = 5000.0f;
if (!targetLeftHand.IsSevered)
{
targetLeftHand.pullJoint.Enabled = true;
targetLeftHand.pullJoint.WorldAnchorB = torso.SimPosition + (new Vector2(1 * Dir, 1)) * 0.2f;
targetLeftHand.pullJoint.MaxForce = 5000.0f;
}
if (!targetRightHand.IsSevered)
{
targetRightHand.pullJoint.Enabled = true;
targetRightHand.pullJoint.WorldAnchorB = torso.SimPosition + (new Vector2(1 * Dir, 1)) * 0.2f;
targetRightHand.pullJoint.MaxForce = 5000.0f;
}
character.SelectedCharacter.AnimController.IgnorePlatforms = true;
}
}
private void UpdateCPR(float deltaTime)
{
if (character.SelectedCharacter == null)
{
Anim = Animation.None;
return;
}
Character target = character.SelectedCharacter;
Crouching = true;
Vector2 diff = target.SimPosition - character.SimPosition;
Limb targetHead = target.AnimController.GetLimb(LimbType.Head);
Limb targetTorso = target.AnimController.GetLimb(LimbType.Torso);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Vector2 headDiff = targetHead == null ? diff : targetHead.SimPosition - character.SimPosition;
targetMovement = new Vector2(diff.X, 0.0f);
TargetDir = headDiff.X > 0.0f ? Direction.Right : Direction.Left;
UpdateStanding();
Vector2 handPos = targetTorso.SimPosition + Vector2.UnitY * 0.2f;
Grab(handPos, handPos);
Vector2 colliderPos = GetColliderBottom();
if (GameMain.Client == null) //Serverside code
{
if (target.Bleeding <= 0.5f && target.Oxygen <= 0.0f) //If they're bleeding too hard CPR will hurt them
{
target.Oxygen += deltaTime * 0.5f; //Stabilize them
}
}
int skill = character.GetSkillLevel("Medical");
if (cprAnimState % 17 > 15.0f)
{
float yPos = (float)Math.Sin(cprAnimState) * 0.2f;
head.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.3f + yPos);
head.pullJoint.Enabled = true;
torso.pullJoint.WorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition - 0.2f));
torso.pullJoint.Enabled = true;
if (GameMain.Client == null) //Serverside code
{
float cpr = skill / 2.0f; //Max possible oxygen addition is 20 per second
character.Oxygen -= (30.0f - cpr) * deltaTime; //Worse skill = more oxygen required
if (character.Oxygen > 0.0f) //we didn't suffocate yet did we
target.Oxygen += cpr * deltaTime;
//DebugConsole.NewMessage("CPR Us: " + character.Oxygen + " Them: " + target.Oxygen + " How good we are: restore " + cpr + " use " + (30.0f - cpr), Color.Aqua);
}
}
else
{
head.pullJoint.WorldAnchorB = new Vector2(targetHead.SimPosition.X, targetHead.SimPosition.Y + 0.8f);
head.pullJoint.Enabled = true;
torso.pullJoint.WorldAnchorB = new Vector2(torso.SimPosition.X, colliderPos.Y + (TorsoPosition - 0.1f));
torso.pullJoint.Enabled = true;
if (cprPump >= 1)
{
torso.body.ApplyForce(new Vector2(0, -1000f));
targetTorso.body.ApplyForce(new Vector2(0, -1000f));
cprPump = 0;
if (target.Bleeding <= 0.5f && target.Health <= 0.0f && !target.IsDead) //Have a chance to revive them to 2 HP if they were damaged.
{
if (GameMain.Client == null) //Serverside code
{
float reviveChance = (cprAnimState % 17) * (skill / 50.0f); //~5% max chance for 10 skill, ~50% max chance for 100 skill
float rng = Rand.Int(100, Rand.RandSync.Server);
//DebugConsole.NewMessage("CPR Pump cprAnimState: " + (cprAnimState % 17) + " revive chance: " + reviveChance + " rng: " + rng, Color.Aqua);
if (rng <= reviveChance) //HOLY CRAP YOU SAVED HIM!!!
{
target.Oxygen = Math.Max(target.Oxygen, 10.0f);
target.Health = 2.0f;
Anim = Animation.None;
return;
}
}
}
else if (target.Bleeding > 0.5f || skill < 50) //We will hurt them if they're bleeding or we suck
{
//If not bleeding: 10% skill causes 0.8 damage per pump, 40% skill causes only 0.2
if (target.Bleeding <= 0.5f)
target.AddDamage(CauseOfDeath.Damage, (50 - skill) * 0.02f, character);
else //If bleeding: 2 HP damage per pump. Basically speeds up their death. Don't pump bleeding people!
{
target.AddDamage(CauseOfDeath.Bloodloss, 1.0f, character);
#if CLIENT
SoundPlayer.PlayDamageSound(DamageSoundType.LimbBlunt, 25.0f, targetTorso.body);
float bloodParticleAmount = 4;
float bloodParticleSize = 1.0f;
for (int i = 0; i < bloodParticleAmount; i++)
{
var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", targetTorso.WorldPosition, Rand.Vector(10.0f), 0.0f, target.AnimController.CurrentHull);
if (blood != null)
{
blood.Size *= bloodParticleSize;
}
}
#endif
}
}
}
cprPump += deltaTime;
}
cprAnimState += deltaTime;
}
public override void DragCharacter(Character target)
{
if (target == null) return;
Limb torso = GetLimb(LimbType.Torso);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
Limb targetLeftHand = target.AnimController.GetLimb(LimbType.LeftHand);
Limb targetRightHand = target.AnimController.GetLimb(LimbType.RightHand);
//only grab with one hand when swimming
leftHand.Disabled = true;
if (!inWater) rightHand.Disabled = true;
for (int i = 0; i < 2; i++)
{
Limb targetLimb = target.AnimController.GetLimb(GrabLimb);
if (targetLimb == null || targetLimb.IsSevered)
{
targetLimb = target.AnimController.GetLimb(LimbType.Torso);
if (i == 0)
{
if (!targetLeftHand.IsSevered)
{
targetLimb = targetLeftHand;
}
else if (!targetRightHand.IsSevered)
{
targetLimb = targetRightHand;
}
}
else
{
if (!targetRightHand.IsSevered)
{
targetLimb = targetRightHand;
}
else if (!targetLeftHand.IsSevered)
{
targetLimb = targetLeftHand;
}
}
}
Limb pullLimb = i == 0 ? leftHand : rightHand;
if (i == 1 && inWater)
{
targetLimb.pullJoint.Enabled = false;
}
else
{
Vector2 diff = ConvertUnits.ToSimUnits(targetLimb.WorldPosition - pullLimb.WorldPosition);
pullLimb.pullJoint.Enabled = true;
if (targetLimb.type == LimbType.Torso)
{
pullLimb.pullJoint.WorldAnchorB = targetLimb.SimPosition;
pullLimb.pullJoint.MaxForce = 5000.0f;
targetMovement *= 0.7f; //Carrying people like that takes a lot of effort.
if (target.AnimController.Dir != Dir)
target.AnimController.Flip();
}
else
{
pullLimb.pullJoint.WorldAnchorB = pullLimb.SimPosition + diff;
pullLimb.pullJoint.MaxForce = 5000.0f;
}
targetLimb.pullJoint.Enabled = true;
if (targetLimb.type == LimbType.Torso)
{
targetLimb.pullJoint.WorldAnchorB = torso.SimPosition + (Vector2.UnitX * Dir) * 0.6f;
targetLimb.pullJoint.MaxForce = 300.0f;
}
else
{
targetLimb.pullJoint.WorldAnchorB = targetLimb.SimPosition - diff;
targetLimb.pullJoint.MaxForce = 5000.0f;
}
target.AnimController.movement = -diff;
}
}
float dist = Vector2.Distance(target.SimPosition, Collider.SimPosition);
//limit movement if moving away from the target
if (Vector2.Dot(target.SimPosition - Collider.SimPosition, targetMovement)<0)
{
targetMovement *= MathHelper.Clamp(2.0f - dist, 0.0f, 1.0f);
}
target.AnimController.IgnorePlatforms = IgnorePlatforms;
if (!target.AllowInput)
{
target.AnimController.TargetMovement = TargetMovement;
}
else if (target is AICharacter)
{
target.AnimController.TargetMovement = Vector2.Lerp(
target.AnimController.TargetMovement,
(character.SimPosition + Vector2.UnitX * Dir) - target.SimPosition, 0.5f);
}
//if on stairs, make the dragged character "climb up" (= collide with stairs)
//if (Stairs != null) target.AnimController.TargetMovement = new Vector2 (target.AnimController.TargetMovement.X, 1.0f);
}
public void Grab(Vector2 rightHandPos, Vector2 leftHandPos)
{
for (int i = 0; i < 2; i++)
{
Limb pullLimb = (i == 0) ? GetLimb(LimbType.LeftHand) : GetLimb(LimbType.RightHand);
pullLimb.Disabled = true;
pullLimb.pullJoint.Enabled = true;
pullLimb.pullJoint.WorldAnchorB = (i == 0) ? rightHandPos : leftHandPos;
pullLimb.pullJoint.MaxForce = 500.0f;
}
}
public override void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, bool aim, float holdAngle)
{
if (character.IsUnconscious || character.Stun > 0.0f) aim = false;
//calculate the handle positions
Matrix itemTransfrom = Matrix.CreateRotationZ(item.body.Rotation);
Vector2[] transformedHandlePos = new Vector2[2];
transformedHandlePos[0] = Vector2.Transform(handlePos[0], itemTransfrom);
transformedHandlePos[1] = Vector2.Transform(handlePos[1], itemTransfrom);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
Vector2 itemPos = aim ? aimPos : holdPos;
bool usingController = character.SelectedConstruction != null && character.SelectedConstruction.GetComponent<Controller>() != null;
float itemAngle;
if (Anim != Animation.Climbing && !usingController && character.Stun <= 0.0f && aim && itemPos != Vector2.Zero)
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
Vector2 diff = (mousePos - AimSourceSimPos) * Dir;
holdAngle = MathUtils.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torso.body.Rotation * Dir;
itemAngle = (torso.body.Rotation + holdAngle * Dir);
Holdable holdable = item.GetComponent<Holdable>();
if (holdable.ControlPose)
{
head.body.SmoothRotate(itemAngle);
if (TargetMovement == Vector2.Zero && inWater)
{
torso.body.AngularVelocity -= torso.body.AngularVelocity * 0.1f;
torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f);
}
aiming = true;
}
}
else
{
itemAngle = (torso.body.Rotation + holdAngle * Dir);
}
Vector2 shoulderPos = LimbJoints[2].WorldAnchorA;
Vector2 transformedHoldPos = shoulderPos;
if (itemPos == Vector2.Zero || Anim == Animation.Climbing || usingController)
{
if (character.SelectedItems[1] == item)
{
transformedHoldPos = leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1];
itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
}
if (character.SelectedItems[0] == item)
{
transformedHoldPos = rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0];
itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
}
}
else
{
if (character.SelectedItems[0] == item)
{
rightHand.Disabled = true;
}
if (character.SelectedItems[1] == item)
{
leftHand.Disabled = true;
}
itemPos.X = itemPos.X * Dir;
transformedHoldPos += Vector2.Transform(itemPos, Matrix.CreateRotationZ(itemAngle));
}
item.body.ResetDynamics();
Vector2 currItemPos = (character.SelectedItems[0] == item) ?
rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0] :
leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1];
item.SetTransform(currItemPos, itemAngle);
//item.SetTransform(MathUtils.SmoothStep(item.body.SimPosition, transformedHoldPos + bodyVelocity, 0.5f), itemAngle);
if (Anim == Animation.Climbing) return;
for (int i = 0; i < 2; i++)
{
if (character.SelectedItems[i] != item) continue;
if (itemPos == Vector2.Zero) continue;
Limb hand = (i == 0) ? rightHand : leftHand;
HandIK(hand, transformedHoldPos + transformedHandlePos[i]);
}
}
private void HandIK(Limb hand, Vector2 pos, float force = 1.0f)
{
Vector2 shoulderPos = LimbJoints[2].WorldAnchorA;
Limb arm = (hand.type == LimbType.LeftHand) ? GetLimb(LimbType.LeftArm) : GetLimb(LimbType.RightArm);
//hand length
float a = 37.0f;
//arm length
float b = 28.0f;
//distance from shoulder to holdpos
float c = ConvertUnits.ToDisplayUnits(Vector2.Distance(pos, shoulderPos));
c = MathHelper.Clamp(a + b - 1, b - a, c);
float ang2 = MathUtils.VectorToAngle(pos - shoulderPos) + MathHelper.PiOver2;
float armAngle = MathUtils.SolveTriangleSSS(a, b, c);
float handAngle = MathUtils.SolveTriangleSSS(b, a, c);
arm.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f * force);
hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f * force);
}
public override void Flip()
{
base.Flip();
walkPos = -walkPos;
Limb torso = GetLimb(LimbType.Torso);
Vector2 difference;
Matrix torsoTransform = Matrix.CreateRotationZ(torso.Rotation);
for (int i = 0; i < character.SelectedItems.Length; i++)
{
if (character.SelectedItems[i] != null && character.SelectedItems[i].body != null)
{
difference = character.SelectedItems[i].body.SimPosition - torso.SimPosition;
difference = Vector2.Transform(difference, torsoTransform);
difference.Y = -difference.Y;
character.SelectedItems[i].body.SetTransform(
torso.SimPosition + Vector2.Transform(difference, -torsoTransform),
MathUtils.WrapAngleTwoPi(-character.SelectedItems[i].body.Rotation));
}
}
foreach (Limb limb in Limbs)
{
bool mirror = false;
bool flipAngle = false;
bool wrapAngle = false;
switch (limb.type)
{
case LimbType.LeftHand:
case LimbType.LeftArm:
case LimbType.RightHand:
case LimbType.RightArm:
mirror = true;
flipAngle = true;
break;
case LimbType.LeftThigh:
case LimbType.LeftLeg:
case LimbType.LeftFoot:
case LimbType.RightThigh:
case LimbType.RightLeg:
case LimbType.RightFoot:
mirror = Crouching && !inWater;
flipAngle = (limb.DoesFlip || Crouching) && !inWater;
wrapAngle = !inWater;
break;
default:
flipAngle = limb.DoesFlip && !inWater;
wrapAngle = !inWater;
break;
}
Vector2 position = limb.SimPosition;
if ((limb.pullJoint == null || !limb.pullJoint.Enabled) && mirror)
{
difference = limb.body.SimPosition - torso.SimPosition;
difference = Vector2.Transform(difference, torsoTransform);
difference.Y = -difference.Y;
position = torso.SimPosition + Vector2.Transform(difference, -torsoTransform);
//TrySetLimbPosition(limb, limb.SimPosition, );
}
float angle = flipAngle ? -limb.body.Rotation : limb.body.Rotation;
if (wrapAngle) angle = MathUtils.WrapAnglePi(angle);
TrySetLimbPosition(limb, Collider.SimPosition, position);
limb.body.SetTransform(limb.body.SimPosition, angle);
}
}
}
}