3 using Microsoft.Xna.Framework;
23 if (_ragdollParams ==
null)
27 return _ragdollParams;
31 _ragdollParams = value;
40 if (_fishWalkParams ==
null)
44 return _fishWalkParams;
46 set { _fishWalkParams = value; }
54 if (_fishRunParams ==
null)
58 return _fishRunParams;
60 set { _fishRunParams = value; }
68 if (_fishSwimSlowParams ==
null)
72 return _fishSwimSlowParams;
74 set { _fishSwimSlowParams = value; }
82 if (_fishSwimFastParams ==
null)
86 return _fishSwimFastParams;
88 set { _fishSwimFastParams = value; }
128 private float flipTimer, flipCooldown;
167 UpdateDying(deltaTime);
172 UpdateDying(deltaTime);
222 UpdateSineAnim(deltaTime);
230 if (Math.Abs(MathUtils.GetShortestAngle(
Collider.
Rotation, standAngle)) > 0.001f)
240 UpdateWalkAnim(deltaTime);
275 rotation = MathHelper.ToDegrees(rotation);
280 if (rotation > 20 && rotation < 160)
284 else if (rotation > 200 && rotation < 340)
293 flipCooldown -= deltaTime;
296 flipTimer += deltaTime;
339 private float eatTimer = 0.0f;
343 if (target ==
null) {
return; }
345 if (mouthLimb ==
null) {
return; }
370 Vector2 limbDiff = attackSimPosition - mouthPos;
372 bool tooFar =
character.
InWater ? limbDiff.LengthSquared() > extent * extent : limbDiff.X > extent;
380 float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.
Mass, 1)) * 10);
381 eatTimer += deltaTime * eatSpeed;
385 float dragForce = MathHelper.Clamp(eatSpeed * 10, 0, 40);
386 if (dragForce > 0.1f)
388 Vector2 targetPos = mouthPos;
408 float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
413 float force = (float)Math.Sin(eatTimer * 100) * mouthLimb.
Mass;
421 jaw.body.ApplyTorque(-(
float)Math.Sin(eatTimer * 150) * jaw.Mass * 25);
427 float particleFrequency = MathHelper.Clamp(eatSpeed / 2, 0.02f, 0.5f);
428 if (Rand.Value() < particleFrequency / 6)
432 if (Rand.Value() < particleFrequency)
436 if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
441 if (nonSeveredJoints.None())
452 enemyAi.PetBehavior?.OnEat(target);
467 void UpdateSineAnim(
float deltaTime)
471 bool isMoving =
movement.LengthSquared() > 0.00001f;
476 Vector2 forward = VectorExtensions.Forward(
Collider.
Rotation + MathHelper.PiOver2);
477 float dot = Vector2.Dot(forward, Vector2.Normalize(
movement));
481 t = MathHelper.Clamp((1 + dot) / 10, 0.01f, 0.1f);
490 mainLimb.PullJointEnabled =
true;
494 WalkPos = MathHelper.SmoothStep(
WalkPos, MathHelper.PiOver2, deltaTime * 5);
501 float newRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver2 *
Dir);
516 float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
517 float mainLimbAngle = 0;
526 mainLimbAngle *=
Dir;
527 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
529 movementAngle += MathHelper.TwoPi;
531 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
533 movementAngle -= MathHelper.TwoPi;
556 bool isAngleApplied =
false;
557 foreach (var limb
in Limbs)
559 if (limb.IsSevered) {
continue; }
560 if (!limb.Params.ApplyTailAngle) {
continue; }
562 isAngleApplied =
true;
569 void RotateTail(
Limb tail)
571 if (tail ==
null) {
return; }
572 float? mainLimbTargetAngle =
null;
573 if (mainLimb.type ==
LimbType.Torso)
577 else if (mainLimb.type ==
LimbType.Head)
583 if (mainLimbTargetAngle.HasValue && maxMultiplier > 1)
585 float diff = Math.Abs(mainLimb.Rotation - tail.
Rotation);
586 float offset = Math.Abs(mainLimbTargetAngle.Value -
TailAngle.Value);
587 torque *= MathHelper.Lerp(1, maxMultiplier, MathUtils.InverseLerp(0, MathHelper.PiOver2, diff - offset));
589 SmoothRotateWithoutWrapping(tail, movementAngle +
TailAngle.Value *
Dir, mainLimb, torque);
595 movementAngle =
Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
598 movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
620 bool isAngleApplied =
false;
621 foreach (var limb
in Limbs)
623 if (limb.IsSevered) {
continue; }
624 if (limb.type !=
LimbType.Tail) {
continue; }
625 if (!limb.Params.ApplyTailAngle) {
continue; }
627 isAngleApplied =
true;
634 void RotateTail(
Limb tail)
646 if (waveLength > 0 && waveAmplitude > 0)
648 WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
652 foreach (var limb
in Limbs)
654 if (limb.IsSevered) {
continue; }
662 if (limb.type ==
LimbType.Tail || limb.Params.ApplySineMovement)
664 if (waveLength > 0 && waveAmplitude > 0)
666 float waveRotation = (float)Math.Sin(
WalkPos * limb.Params.SineFrequencyMultiplier);
667 limb.body.ApplyTorque(waveRotation * limb.Mass * waveAmplitude * limb.Params.SineAmplitudeMultiplier);
670 if (limb.SteerForce <= 0.0f) {
continue; }
672 Vector2 pullPos = limb.PullJointWorldAnchorA;
676 Vector2 mainLimbDiff = mainLimb.PullJointWorldAnchorB - mainLimb.SimPosition;
679 mainLimb.PullJointWorldAnchorB = Vector2.SmoothStep(
680 mainLimb.PullJointWorldAnchorB,
682 mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : (float)Math.Abs(Math.Sin(
WalkPos)));
687 mainLimb.PullJointWorldAnchorB = Vector2.Lerp(
688 mainLimb.PullJointWorldAnchorB,
690 mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : 0.5f);
697 void UpdateWalkAnim(
float deltaTime)
713 float movementAngle = 0.0f;
716 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
718 movementAngle += MathHelper.TwoPi;
720 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
722 movementAngle -= MathHelper.TwoPi;
736 float walkPosX = (float)Math.Cos(
WalkPos);
738 limpAmount = Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(
TargetMovement.X), 0.3f) *
Dir;
750 Vector2 pos = colliderBottom +
new Vector2(limpAmount,
TorsoPosition.Value + stepLift);
752 if (torso != mainLimb)
754 pos.X = torso.SimPosition.X;
758 torso.PullJointEnabled =
true;
759 torso.PullJointWorldAnchorB = pos;
766 bool headFacingBackwards =
false;
767 if (
HeadAngle.HasValue && head != mainLimb)
770 if (Math.Sign(head.SimPosition.X - mainLimb.SimPosition.X) != Math.Sign(
Dir))
772 headFacingBackwards =
true;
779 if (head != mainLimb)
781 pos.X = head.SimPosition.X;
785 head.PullJointEnabled =
true;
786 head.PullJointWorldAnchorB = pos;
792 bool isAngleApplied =
false;
793 foreach (var limb
in Limbs)
795 if (limb.IsSevered) {
continue; }
796 if (limb.type !=
LimbType.Tail) {
continue; }
797 if (!limb.Params.ApplyTailAngle) {
continue; }
799 isAngleApplied =
true;
806 void RotateTail(Limb tail)
818 Vector2 transformedStepSize = Vector2.Zero;
821 transformedStepSize =
new Vector2(
826 foreach (Limb limb
in Limbs)
828 if (limb.IsSevered) {
continue; }
833 Vector2 footPos =
new Vector2(limb.SimPosition.X, colliderBottom.Y);
835 if (limb.RefJointIndex > -1)
839 DebugConsole.ThrowError($
"Reference joint index {limb.RefJointIndex} is out of array. This is probably due to a missing joint. If you just deleted a joint, don't do that without first removing the reference joint indices from the limbs. If this is not the case, please ensure that you have defined the index to the right joint.");
846 footPos.X += limb.StepOffset.X *
Dir;
847 footPos.Y += limb.StepOffset.Y;
849 bool playFootstepSound =
false;
852 if (Math.Sign(Math.Sin(prevWalkPos)) > 0 && Math.Sign(transformedStepSize.Y) < 0)
854 playFootstepSound =
true;
857 limb.DebugRefPos = footPos + Vector2.UnitX *
movement.X * 0.1f;
858 limb.DebugTargetPos = footPos +
new Vector2(
859 transformedStepSize.X +
movement.X * 0.1f,
860 (transformedStepSize.Y > 0.0f) ? transformedStepSize.Y : 0.0f);
863 else if (limb.type ==
LimbType.RightFoot)
865 if (Math.Sign(Math.Sin(prevWalkPos)) < 0 && Math.Sign(transformedStepSize.Y) > 0)
867 playFootstepSound =
true;
870 limb.DebugRefPos = footPos + Vector2.UnitX *
movement.X * 0.1f;
871 limb.DebugTargetPos = footPos +
new Vector2(
872 -transformedStepSize.X +
movement.X * 0.1f,
873 (-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f);
877 if (playFootstepSound)
886 SmoothRotateWithoutWrapping(limb,
902 void UpdateDying(
float deltaTime)
904 if (deathAnimDuration <= 0.0f) {
return; }
906 float noise = (PerlinNoise.GetPerlin(
WalkPos * 0.002f,
WalkPos * 0.003f) - 0.5f) * 5.0f;
915 if (connectedToHeadCount == 1) { baseLimb =
null; }
921 if (connectedToTorsoCount > connectedToHeadCount)
927 else if (baseLimb ==
null)
930 if (baseLimb ==
null) {
return; }
936 if (baseLimb !=
null) { baseLimb.body.ApplyTorque((
float)(Math.Sqrt(baseLimb.Mass) *
Dir * (Math.Sin(
WalkPos) + noise)) * 30.0f * animStrength); }
937 if (tail !=
null && connectedToBaseLimb.Contains(tail)) { tail.body.ApplyTorque((
float)(Math.Sqrt(tail.Mass) * -
Dir * (Math.Sin(
WalkPos) + noise)) * 30.0f * animStrength); }
939 WalkPos += deltaTime * 10.0f * animStrength;
943 foreach (Limb limb
in Limbs)
945 if (!connectedToBaseLimb.Contains(limb)) {
continue; }
947 if (limb.LightSource !=
null)
949 limb.LightSource.Color = Color.Lerp(limb.InitialLightSourceColor, Color.TransparentBlack,
deathAnimTimer / deathAnimDuration);
950 if (limb.InitialLightSpriteAlpha.HasValue)
952 limb.LightSource.OverrideLightSpriteAlpha = MathHelper.Lerp(limb.InitialLightSpriteAlpha.Value, 0.0f,
deathAnimTimer / deathAnimDuration);
956 if (limb.type ==
LimbType.Head || limb.type ==
LimbType.Tail || limb.IsSevered || !limb.body.Enabled)
continue;
957 if (limb.Mass <= 0.0f)
959 string errorMsg =
"Creature death animation error: invalid limb mass on character \"" +
character.
SpeciesName +
"\" (type: " + limb.type +
", mass: " + limb.Mass +
")";
960 DebugConsole.ThrowError(errorMsg);
961 GameAnalyticsManager.AddErrorEventOnce(
"FishAnimController.UpdateDying:InvalidMass" +
character.
ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
966 Vector2 diff = (centerOfMass - limb.SimPosition);
967 if (!MathUtils.IsValid(diff))
969 string errorMsg =
"Creature death animation error: invalid diff (center of mass: " + centerOfMass +
", limb position: " + limb.SimPosition +
")";
970 DebugConsole.ThrowError(errorMsg);
971 GameAnalyticsManager.AddErrorEventOnce(
"FishAnimController.UpdateDying:InvalidDiff" +
character.
ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
976 limb.body.ApplyForce(diff * (
float)(Math.Sin(
WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength, maxVelocity: 10.0f);
980 private void SmoothRotateWithoutWrapping(Limb limb,
float angle, Limb referenceLimb,
float torque)
984 angle = referenceLimb.body.WrapAngleToSameNumberOfRevolutions(angle);
985 limb?.body.SmoothRotate(angle, torque, wrapAngle:
false);
override? float HeadPosition
float? GetValidOrNull(AnimationParams p, float? v)
virtual ? Vector2 StepSize
AnimationParams? CurrentAnimationParams
override? float TorsoAngle
void UpdateBlink(float deltaTime)
void UpdateConstantTorque(float deltaTime)
override? float TorsoPosition
override? float HeadAngle
readonly CharacterParams Params
bool IsRemotelyControlled
Is the character controlled remotely (either by another player, or a server-side AIController)
virtual AIController AIController
float GetLegPenalty(float startSum=0)
override Vector2? SimPosition
void ApplyStatusEffects(ActionType actionType, float deltaTime)
float SpeedMultiplier
Can be used to modify the character's speed via StatusEffects
Character SelectedCharacter
CharacterInventory Inventory
readonly AnimController AnimController
bool IsRemotePlayer
Is the character controlled by another human player (should always be false in single player)
static EntitySpawner Spawner
virtual Vector2 WorldPosition
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
void AddEntityToRemoveQueue(Entity entity)
override void UpdateAnim(float deltaTime)
FishSwimFastParams FishSwimFastParams
FishRagdollParams FishRagdollParams
override SwimParams SwimFastParams
override GroundedMovementParams RunParams
new FishSwimParams CurrentSwimParams
FishSwimSlowParams FishSwimSlowParams
override SwimParams SwimSlowParams
override void DragCharacter(Character target, float deltaTime)
IFishAnimation CurrentFishAnimation
override GroundedMovementParams WalkParams
new FishGroundedParams CurrentGroundedParams
void Mirror(bool lerp=true)
override RagdollParams RagdollParams
FishRunParams FishRunParams
FishWalkParams FishWalkParams
FishAnimController(Character character, string seed, FishRagdollParams ragdollParams=null)
Dictionary< int, float > FootAnglesInRadians
Key = limb id, value = angle in radians
float ColliderStandAngleInRadians
static FishRagdollParams GetDefaultRagdollParams(Character character)
static FishRunParams GetDefaultAnimParams(Character character)
static FishSwimFastParams GetDefaultAnimParams(Character character)
float TailTorqueMultiplier
bool UpdateAnimationWhenNotMoving
bool RotateTowardsMovement
Dictionary< int, float > FootAnglesInRadians
Key = limb id, value = angle in radians
static FishSwimSlowParams GetDefaultAnimParams(Character character)
static FishWalkParams GetDefaultAnimParams(Character character)
static NetworkMember NetworkMember
float StepLiftHeadMultiplier
IEnumerable< Item > AllItemsMod
All items contained in the inventory. Allows modifying the contents of the inventory while being enum...
AttackResult AddDamage(Vector2 simPosition, float damage, float bleedingDamage, float burnDamage, bool playSound)
readonly LimbParams Params
void MoveToPos(Vector2 pos, float force, bool pullFromCenter=false)
void ApplyTorque(float torque)
void ApplyLinearImpulse(Vector2 impulse)
float? PositionSmoothingFactor
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...
void ResetPullJoints(Func< Limb, bool > condition=null)
List< Limb > GetConnectedLimbs(Limb limb)
Vector2 GetColliderBottom()
void PlayImpactSound(Limb limb)
bool SeverLimbJoint(LimbJoint limbJoint)
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.
void SetPosition(Vector2 simPosition, bool lerp=false, bool ignorePlatforms=true, bool forceMainLimbToCollider=false, bool moveLatchers=true)
bool? SimplePhysicsEnabled
void TrySetLimbPosition(Limb limb, Vector2 original, Vector2 simPosition, float rotation, bool lerp=false, bool ignorePlatforms=true)
Vector2? GetMouthPosition()
readonly Character character
Vector2 GetCenterOfMass()
bool IsSpritesheetOrientationHorizontal
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
ActionType
ActionTypes define when a StatusEffect is executed.