Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/Source/Characters/Animation/FishAnimController.cs
juanjp600 1cb39f7fd5 Fixed some inconsistencies and oddities in physics-handling code
I'm not sure if this fixes or breaks anything, but it's worth a shot.
2017-12-15 18:11:00 -03:00

416 lines
15 KiB
C#

using FarseerPhysics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;
namespace Barotrauma
{
class FishAnimController : AnimController
{
//amplitude and wave length of the "sine wave" swimming animation
//if amplitude = 0, sine wave animation isn't used
private float waveAmplitude;
private float waveLength;
private float steerTorque;
private bool rotateTowardsMovement;
private bool mirror, flip;
private float flipTimer;
private float? footRotation;
private float deathAnimTimer, deathAnimDuration = 5.0f;
public FishAnimController(Character character, XElement element)
: base(character, element)
{
waveAmplitude = ConvertUnits.ToSimUnits(element.GetAttributeFloat("waveamplitude", 0.0f));
waveLength = ConvertUnits.ToSimUnits(element.GetAttributeFloat("wavelength", 0.0f));
steerTorque = element.GetAttributeFloat("steertorque", 25.0f);
flip = element.GetAttributeBool("flip", true);
mirror = element.GetAttributeBool("mirror", false);
float footRot = element.GetAttributeFloat("footrotation", float.NaN);
if (float.IsNaN(footRot))
{
footRotation = null;
}
else
{
footRotation = MathHelper.ToRadians(footRot);
}
rotateTowardsMovement = element.GetAttributeBool("rotatetowardsmovement", true);
}
public override void UpdateAnim(float deltaTime)
{
if (Frozen) return;
if (character.IsDead || character.IsUnconscious || character.Stun > 0.0f)
{
Collider.FarseerBody.FixedRotation = false;
if (character.IsRemotePlayer)
{
MainLimb.pullJoint.WorldAnchorB = Collider.SimPosition;
MainLimb.pullJoint.Enabled = true;
}
else
{
Collider.LinearVelocity = (MainLimb.SimPosition - Collider.SimPosition) * 60.0f;
Collider.SmoothRotate(MainLimb.Rotation);
}
if (character.IsDead && deathAnimTimer < deathAnimDuration)
{
deathAnimTimer += deltaTime;
UpdateDying(deltaTime);
}
return;
}
//re-enable collider
if (!Collider.Enabled)
{
var lowestLimb = FindLowestLimb();
Collider.SetTransform(new Vector2(
Collider.SimPosition.X,
Math.Max(lowestLimb.SimPosition.Y + (Collider.radius + Collider.height / 2), Collider.SimPosition.Y)),
0.0f);
Collider.Enabled = true;
}
ResetPullJoints();
if (strongestImpact > 0.0f)
{
character.Stun = MathHelper.Clamp(strongestImpact * 0.5f, character.Stun, 5.0f);
strongestImpact = 0.0f;
}
if (inWater)
{
Collider.FarseerBody.FixedRotation = false;
UpdateSineAnim(deltaTime);
}
else if (currentHull != null && CanEnterSubmarine)
{
if (Math.Abs(MathUtils.GetShortestAngle(Collider.Rotation, 0.0f)) > 0.001f)
{
//rotate collider back upright
Collider.AngularVelocity = MathUtils.GetShortestAngle(Collider.Rotation, 0.0f) * 60.0f;
Collider.FarseerBody.FixedRotation = false;
}
else
{
Collider.FarseerBody.FixedRotation = true;
}
UpdateWalkAnim(deltaTime);
}
if (!character.IsRemotePlayer)
{
if (mirror || !inWater)
{
if (targetMovement.X > 0.1f && targetMovement.X > Math.Abs(targetMovement.Y) * 0.5f)
{
TargetDir = Direction.Right;
}
else if (targetMovement.X < -0.1f && targetMovement.X < -Math.Abs(targetMovement.Y) * 0.5f)
{
TargetDir = Direction.Left;
}
}
else
{
Limb head = GetLimb(LimbType.Head);
if (head == null) head = GetLimb(LimbType.Torso);
float rotation = MathUtils.WrapAngleTwoPi(head.Rotation);
rotation = MathHelper.ToDegrees(rotation);
if (rotation < 0.0f) rotation += 360;
if (rotation > 20 && rotation < 160)
{
TargetDir = Direction.Left;
}
else if (rotation > 200 && rotation < 340)
{
TargetDir = Direction.Right;
}
}
}
if (character.SelectedCharacter != null) DragCharacter(character.SelectedCharacter);
if (!flip) return;
flipTimer += deltaTime;
if (TargetDir != Direction.None && TargetDir != dir)
{
if (flipTimer > 1.0f || character.IsRemotePlayer)
{
Flip();
if (mirror || !inWater) Mirror();
flipTimer = 0.0f;
}
}
}
private float eatTimer = 0.0f;
public override void DragCharacter(Character target)
{
if (target == null) return;
Limb mouthLimb = Array.Find(Limbs, l => l != null && l.MouthPos.HasValue);
if (mouthLimb == null) mouthLimb = GetLimb(LimbType.Head);
if (mouthLimb == null)
{
DebugConsole.ThrowError("Character \"" + character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)");
return;
}
Character targetCharacter = target;
float eatSpeed = character.Mass / targetCharacter.Mass * 0.1f;
eatTimer += (float)Timing.Step * eatSpeed;
Vector2 mouthPos = mouthLimb.SimPosition;
if (mouthLimb.MouthPos.HasValue)
{
float cos = (float)Math.Cos(mouthLimb.Rotation);
float sin = (float)Math.Sin(mouthLimb.Rotation);
mouthPos += new Vector2(
mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin,
mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos);
}
Vector2 attackSimPosition = character.Submarine == null ? ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;
Vector2 limbDiff = attackSimPosition - mouthPos;
float limbDist = limbDiff.Length();
if (limbDist < 1.0f)
{
//pull the target character to the position of the mouth
//(+ make the force fluctuate to waggle the character a bit)
targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation);
targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
//pull the character's mouth to the target character (again with a fluctuating force)
float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength);
if (eatTimer % 1.0f < 0.5f && (eatTimer - (float)Timing.Step * eatSpeed) % 1.0f > 0.5f)
{
//apply damage to the target character to get some blood particles flying
targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), 10.0f, false);
//keep severing joints until there is only one limb left
LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered && l.CanBeSevered);
if (nonSeveredJoints.Length == 0)
{
//only one limb left, the character is now full eaten
Entity.Spawner.AddToRemoveQueue(targetCharacter);
character.SelectedCharacter = null;
character.Health += 10.0f;
}
else //sever a random joint
{
targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]);
}
}
}
else
{
character.SelectedCharacter = null;
}
}
void UpdateSineAnim(float deltaTime)
{
movement = TargetMovement*swimSpeed;
MainLimb.pullJoint.Enabled = true;
MainLimb.pullJoint.WorldAnchorB = Collider.SimPosition;
if (movement.LengthSquared() < 0.00001f) return;
float movementAngle = MathUtils.VectorToAngle(movement) - MathHelper.PiOver2;
if (rotateTowardsMovement)
{
Collider.SmoothRotate(movementAngle, 25.0f);
MainLimb.body.SmoothRotate(movementAngle, steerTorque);
}
else
{
Collider.SmoothRotate(HeadAngle * Dir, 25.0f);
MainLimb.body.SmoothRotate(HeadAngle * Dir, steerTorque);
}
Limb tail = GetLimb(LimbType.Tail);
if (tail != null && waveAmplitude > 0.0f)
{
walkPos -= movement.Length();
float waveRotation = (float)Math.Sin(walkPos / waveLength);
tail.body.ApplyTorque(waveRotation * tail.Mass * 100.0f * waveAmplitude);
}
for (int i = 0; i < Limbs.Length; i++)
{
if (Limbs[i].SteerForce <= 0.0f) continue;
Vector2 pullPos = Limbs[i].pullJoint == null ? Limbs[i].SimPosition : Limbs[i].pullJoint.WorldAnchorA;
Limbs[i].body.ApplyForce(movement * Limbs[i].SteerForce * Limbs[i].Mass, pullPos);
}
Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement, 0.5f);
floorY = Limbs[0].SimPosition.Y;
}
void UpdateWalkAnim(float deltaTime)
{
movement = MathUtils.SmoothStep(movement, TargetMovement * walkSpeed, 0.2f);
float mainLimbHeight, mainLimbAngle;
if (MainLimb.type == LimbType.Torso)
{
mainLimbHeight = TorsoPosition;
mainLimbAngle = torsoAngle;
}
else
{
mainLimbHeight = HeadPosition;
mainLimbAngle = headAngle;
}
MainLimb.body.SmoothRotate(mainLimbAngle * Dir, 50.0f);
Collider.LinearVelocity = new Vector2(
movement.X,
Collider.LinearVelocity.Y > 0.0f ? Collider.LinearVelocity.Y * 0.5f : Collider.LinearVelocity.Y);
MainLimb.MoveToPos(GetColliderBottom() + Vector2.UnitY * mainLimbHeight, 10.0f);
MainLimb.pullJoint.Enabled = true;
MainLimb.pullJoint.WorldAnchorB = GetColliderBottom() + Vector2.UnitY * mainLimbHeight;
walkPos -= MainLimb.LinearVelocity.X * 0.05f;
Vector2 transformedStepSize = new Vector2(
(float)Math.Cos(walkPos) * stepSize.X * 3.0f,
(float)Math.Sin(walkPos) * stepSize.Y * 2.0f);
foreach (Limb limb in Limbs)
{
switch (limb.type)
{
case LimbType.LeftFoot:
case LimbType.RightFoot:
Vector2 footPos = new Vector2(limb.SimPosition.X, MainLimb.SimPosition.Y - mainLimbHeight);
if (limb.RefJointIndex>-1)
{
RevoluteJoint refJoint = LimbJoints[limb.RefJointIndex];
footPos.X = refJoint.WorldAnchorA.X;
}
footPos.X += limb.StepOffset.X * Dir;
footPos.Y += limb.StepOffset.Y;
if (limb.type == LimbType.LeftFoot)
{
limb.MoveToPos(footPos +new Vector2(
transformedStepSize.X + movement.X * 0.1f,
(transformedStepSize.Y > 0.0f) ? transformedStepSize.Y : 0.0f),
8.0f);
}
else if (limb.type == LimbType.RightFoot)
{
limb.MoveToPos(footPos + new Vector2(
-transformedStepSize.X + movement.X * 0.1f,
(-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f),
8.0f);
}
if (footRotation != null) limb.body.SmoothRotate((float)footRotation * Dir, 50.0f);
break;
case LimbType.LeftLeg:
case LimbType.RightLeg:
if (legTorque != 0.0f) limb.body.ApplyTorque(limb.Mass * legTorque * Dir);
break;
}
}
}
void UpdateDying(float deltaTime)
{
Limb head = GetLimb(LimbType.Head);
Limb tail = GetLimb(LimbType.Tail);
if (head != null && !head.IsSevered) head.body.ApplyTorque((float)(Math.Sqrt(head.Mass) * Dir * Math.Sin(walkPos)) * 10.0f);
if (tail != null && !tail.IsSevered) tail.body.ApplyTorque((float)(Math.Sqrt(tail.Mass) * -Dir * (float)Math.Sin(walkPos)) * 10.0f);
walkPos += deltaTime * 5.0f;
Vector2 centerOfMass = GetCenterOfMass();
foreach (Limb limb in Limbs)
{
if (limb.type == LimbType.Head || limb.type == LimbType.Tail || limb.IsSevered) continue;
limb.body.ApplyForce((centerOfMass - limb.SimPosition) * (float)(Math.Sin(walkPos) * Math.Sqrt(limb.Mass)) * 10.0f);
}
}
public override void Flip()
{
base.Flip();
foreach (Limb l in Limbs)
{
if (!l.DoesFlip) continue;
l.body.SetTransform(l.SimPosition,
-l.body.Rotation);
}
}
private void Mirror()
{
Vector2 centerOfMass = GetCenterOfMass();
foreach (Limb l in Limbs)
{
TrySetLimbPosition(l,
centerOfMass,
new Vector2(centerOfMass.X - (l.SimPosition.X - centerOfMass.X), l.SimPosition.Y),
true);
}
}
}
}