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;
165 UpdateDying(deltaTime);
170 UpdateDying(deltaTime);
215 UpdateSineAnim(deltaTime);
223 if (Math.Abs(MathUtils.GetShortestAngle(
Collider.
Rotation, standAngle)) > 0.001f)
233 UpdateWalkAnim(deltaTime);
268 rotation = MathHelper.ToDegrees(rotation);
273 if (rotation > 20 && rotation < 160)
277 else if (rotation > 200 && rotation < 340)
286 flipCooldown -= deltaTime;
289 flipTimer += deltaTime;
332 private float eatTimer = 0.0f;
336 if (target ==
null) {
return; }
338 if (mouthLimb ==
null) {
return; }
362 float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.
Mass, 1)) * 10);
363 eatTimer += deltaTime * eatSpeed;
368 Vector2 limbDiff = attackSimPosition - mouthPos;
370 bool tooFar =
character.
InWater ? limbDiff.LengthSquared() > extent * extent : limbDiff.X > extent;
379 float dragForce = MathHelper.Clamp(eatSpeed * 10, 0, 40);
380 if (dragForce > 0.1f)
382 Vector2 targetPos = mouthPos;
402 float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
407 float force = (float)Math.Sin(eatTimer * 100) * mouthLimb.
Mass;
408 mouthLimb.
body.
ApplyLinearImpulse(Vector2.UnitY * force * 2, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
417 jaw.body.ApplyTorque(-(
float)Math.Sin(eatTimer * 150) * jaw.Mass * 25);
422 float particleFrequency = MathHelper.Clamp(eatSpeed / 2, 0.02f, 0.5f);
423 if (Rand.Value() < particleFrequency / 6)
427 if (Rand.Value() < particleFrequency)
431 if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
436 if (nonSeveredJoints.None())
449 enemyAi.PetBehavior?.OnEat(target);
465 void UpdateSineAnim(
float deltaTime)
469 bool isMoving =
movement.LengthSquared() > 0.00001f;
474 Vector2 forward = VectorExtensions.Forward(
Collider.
Rotation + MathHelper.PiOver2);
475 float dot = Vector2.Dot(forward, Vector2.Normalize(
movement));
479 t = MathHelper.Clamp((1 + dot) / 10, 0.01f, 0.1f);
488 mainLimb.PullJointEnabled =
true;
492 WalkPos = MathHelper.SmoothStep(
WalkPos, MathHelper.PiOver2, deltaTime * 5);
499 float newRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver2 *
Dir);
514 float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
515 float mainLimbAngle = 0;
524 mainLimbAngle *=
Dir;
525 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
527 movementAngle += MathHelper.TwoPi;
529 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
531 movementAngle -= MathHelper.TwoPi;
554 bool isAngleApplied =
false;
555 foreach (var limb
in Limbs)
557 if (limb.IsSevered) {
continue; }
558 if (limb.type !=
LimbType.Tail) {
continue; }
559 if (!limb.Params.ApplyTailAngle) {
continue; }
561 isAngleApplied =
true;
568 void RotateTail(
Limb tail)
570 if (tail ==
null) {
return; }
571 float? mainLimbTargetAngle =
null;
572 if (mainLimb.type ==
LimbType.Torso)
576 else if (mainLimb.type ==
LimbType.Head)
582 if (mainLimbTargetAngle.HasValue && maxMultiplier > 1)
584 float diff = Math.Abs(mainLimb.Rotation - tail.
Rotation);
585 float offset = Math.Abs(mainLimbTargetAngle.Value -
TailAngle.Value);
586 torque *= MathHelper.Lerp(1, maxMultiplier, MathUtils.InverseLerp(0, MathHelper.PiOver2, diff - offset));
588 SmoothRotateWithoutWrapping(tail, movementAngle +
TailAngle.Value *
Dir, mainLimb, torque);
594 movementAngle =
Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
597 movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
619 bool isAngleApplied =
false;
620 foreach (var limb
in Limbs)
622 if (limb.IsSevered) {
continue; }
623 if (limb.type !=
LimbType.Tail) {
continue; }
624 if (!limb.Params.ApplyTailAngle) {
continue; }
626 isAngleApplied =
true;
633 void RotateTail(
Limb tail)
645 if (waveLength > 0 && waveAmplitude > 0)
647 WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
651 foreach (var limb
in Limbs)
653 if (limb.IsSevered) {
continue; }
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);
673 for (
int i = 0; i <
Limbs.Length; i++)
676 if (limb.IsSevered) {
continue; }
677 if (limb.SteerForce <= 0.0f) {
continue; }
683 Vector2 mainLimbDiff = mainLimb.PullJointWorldAnchorB - mainLimb.SimPosition;
686 mainLimb.PullJointWorldAnchorB = Vector2.SmoothStep(
687 mainLimb.PullJointWorldAnchorB,
689 mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : (float)Math.Abs(Math.Sin(
WalkPos)));
694 mainLimb.PullJointWorldAnchorB = Vector2.Lerp(
695 mainLimb.PullJointWorldAnchorB,
697 mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : 0.5f);
701 foreach (var limb
in Limbs)
703 if (limb.IsSevered) {
continue; }
704 if (Math.Abs(limb.Params.ConstantTorque) > 0)
707 limb.body.SmoothRotate(
MainLimb.
Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) *
Dir, limb.Mass * limb.Params.ConstantTorque * movementFactor, wrapAngle:
true);
709 if (limb.Params.BlinkFrequency > 0)
718 void UpdateWalkAnim(
float deltaTime)
734 float movementAngle = 0.0f;
737 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
739 movementAngle += MathHelper.TwoPi;
741 while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
743 movementAngle -= MathHelper.TwoPi;
757 float walkPosX = (float)Math.Cos(
WalkPos);
759 limpAmount = Math.Max(Math.Abs(walkPosX) * limpAmount, 0.0f) * Math.Min(Math.Abs(
TargetMovement.X), 0.3f) *
Dir;
771 Vector2 pos = colliderBottom +
new Vector2(limpAmount,
TorsoPosition.Value + stepLift);
773 if (torso != mainLimb)
775 pos.X = torso.SimPosition.X;
779 torso.PullJointEnabled =
true;
780 torso.PullJointWorldAnchorB = pos;
787 bool headFacingBackwards =
false;
788 if (
HeadAngle.HasValue && head != mainLimb)
791 if (Math.Sign(head.SimPosition.X - mainLimb.SimPosition.X) != Math.Sign(
Dir))
793 headFacingBackwards =
true;
800 if (head != mainLimb)
802 pos.X = head.SimPosition.X;
806 head.PullJointEnabled =
true;
807 head.PullJointWorldAnchorB = pos;
813 bool isAngleApplied =
false;
814 foreach (var limb
in Limbs)
816 if (limb.IsSevered) {
continue; }
817 if (limb.type !=
LimbType.Tail) {
continue; }
818 if (!limb.Params.ApplyTailAngle) {
continue; }
820 isAngleApplied =
true;
827 void RotateTail(Limb tail)
839 Vector2 transformedStepSize = Vector2.Zero;
842 transformedStepSize =
new Vector2(
847 foreach (Limb limb
in Limbs)
849 if (limb.IsSevered) {
continue; }
850 if (Math.Abs(limb.Params.ConstantTorque) > 0)
852 limb.body.SmoothRotate(
MainLimb.
Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) *
Dir, limb.Mass * limb.Params.ConstantTorque, wrapAngle:
true);
854 if (limb.Params.BlinkFrequency > 0 && !limb.Params.OnlyBlinkInWater)
862 Vector2 footPos =
new Vector2(limb.SimPosition.X, colliderBottom.Y);
864 if (limb.RefJointIndex > -1)
868 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.");
875 footPos.X += limb.StepOffset.X *
Dir;
876 footPos.Y += limb.StepOffset.Y;
878 bool playFootstepSound =
false;
881 if (Math.Sign(Math.Sin(prevWalkPos)) > 0 && Math.Sign(transformedStepSize.Y) < 0)
883 playFootstepSound =
true;
886 limb.DebugRefPos = footPos + Vector2.UnitX *
movement.X * 0.1f;
887 limb.DebugTargetPos = footPos +
new Vector2(
888 transformedStepSize.X +
movement.X * 0.1f,
889 (transformedStepSize.Y > 0.0f) ? transformedStepSize.Y : 0.0f);
892 else if (limb.type ==
LimbType.RightFoot)
894 if (Math.Sign(Math.Sin(prevWalkPos)) < 0 && Math.Sign(transformedStepSize.Y) > 0)
896 playFootstepSound =
true;
899 limb.DebugRefPos = footPos + Vector2.UnitX *
movement.X * 0.1f;
900 limb.DebugTargetPos = footPos +
new Vector2(
901 -transformedStepSize.X +
movement.X * 0.1f,
902 (-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f);
906 if (playFootstepSound)
915 SmoothRotateWithoutWrapping(limb,
931 void UpdateDying(
float deltaTime)
933 if (deathAnimDuration <= 0.0f) {
return; }
935 float noise = (PerlinNoise.GetPerlin(
WalkPos * 0.002f,
WalkPos * 0.003f) - 0.5f) * 5.0f;
944 if (connectedToHeadCount == 1) { baseLimb =
null; }
950 if (connectedToTorsoCount > connectedToHeadCount)
956 else if (baseLimb ==
null)
959 if (baseLimb ==
null) {
return; }
965 if (baseLimb !=
null) { baseLimb.body.ApplyTorque((
float)(Math.Sqrt(baseLimb.Mass) *
Dir * (Math.Sin(
WalkPos) + noise)) * 30.0f * animStrength); }
966 if (tail !=
null && connectedToBaseLimb.Contains(tail)) { tail.body.ApplyTorque((
float)(Math.Sqrt(tail.Mass) * -
Dir * (Math.Sin(
WalkPos) + noise)) * 30.0f * animStrength); }
968 WalkPos += deltaTime * 10.0f * animStrength;
972 foreach (Limb limb
in Limbs)
974 if (!connectedToBaseLimb.Contains(limb)) {
continue; }
976 if (limb.LightSource !=
null)
978 limb.LightSource.Color = Color.Lerp(limb.InitialLightSourceColor, Color.TransparentBlack,
deathAnimTimer / deathAnimDuration);
979 if (limb.InitialLightSpriteAlpha.HasValue)
981 limb.LightSource.OverrideLightSpriteAlpha = MathHelper.Lerp(limb.InitialLightSpriteAlpha.Value, 0.0f,
deathAnimTimer / deathAnimDuration);
985 if (limb.type ==
LimbType.Head || limb.type ==
LimbType.Tail || limb.IsSevered || !limb.body.Enabled)
continue;
986 if (limb.Mass <= 0.0f)
988 string errorMsg =
"Creature death animation error: invalid limb mass on character \"" +
character.
SpeciesName +
"\" (type: " + limb.type +
", mass: " + limb.Mass +
")";
989 DebugConsole.ThrowError(errorMsg);
990 GameAnalyticsManager.AddErrorEventOnce(
"FishAnimController.UpdateDying:InvalidMass" +
character.
ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
995 Vector2 diff = (centerOfMass - limb.SimPosition);
996 if (!MathUtils.IsValid(diff))
998 string errorMsg =
"Creature death animation error: invalid diff (center of mass: " + centerOfMass +
", limb position: " + limb.SimPosition +
")";
999 DebugConsole.ThrowError(errorMsg);
1000 GameAnalyticsManager.AddErrorEventOnce(
"FishAnimController.UpdateDying:InvalidDiff" +
character.
ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1005 limb.body.ApplyForce(diff * (
float)(Math.Sin(
WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength, maxVelocity: 10.0f);
1009 private void SmoothRotateWithoutWrapping(Limb limb,
float angle, Limb referenceLimb,
float torque)
1013 angle = referenceLimb.body.WrapAngleToSameNumberOfRevolutions(angle);
1014 limb?.body.SmoothRotate(angle, torque, wrapAngle:
false);
override? float HeadPosition
float? GetValidOrNull(AnimationParams p, float? v)
virtual ? Vector2 StepSize
AnimationParams? CurrentAnimationParams
override? float TorsoAngle
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)
void MoveToPos(Vector2 pos, float force, bool pullFromCenter=false)
Vector2 PullJointWorldAnchorA
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.
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()
Limb GetLimb(LimbType limbType, bool excludeSevered=true)
Note that if there are multiple limbs of the same type, only the first (valid) limb is returned.
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.