using Microsoft.Xna.Framework; using System.Collections.Generic; namespace Barotrauma { class FishWalkParams : FishGroundedParams { public static FishWalkParams GetDefaultAnimParams(Character character) { return Check(character) ? GetDefaultAnimParams(character, AnimationType.Walk) : Empty; } public static FishWalkParams GetAnimParams(Character character, Either file, bool throwErrors = true) { return Check(character) ? GetAnimParams(character, AnimationType.Walk, file, throwErrors) : null; } protected static readonly FishWalkParams Empty = new FishWalkParams(); public override void StoreSnapshot() => StoreSnapshot(); } class FishRunParams : FishGroundedParams { public static FishRunParams GetDefaultAnimParams(Character character) { return Check(character) ? GetDefaultAnimParams(character, AnimationType.Run) : Empty; } public static FishRunParams GetAnimParams(Character character, Either file, bool throwErrors = true) { return Check(character) ? GetAnimParams(character, AnimationType.Run, file, throwErrors) : null; } protected static readonly FishRunParams Empty = new FishRunParams(); public override void StoreSnapshot() => StoreSnapshot(); } class FishSwimFastParams : FishSwimParams { public static FishSwimFastParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams(character, AnimationType.SwimFast); public static FishSwimFastParams GetAnimParams(Character character, Either file, bool throwErrors = true) { return GetAnimParams(character, AnimationType.SwimFast, file, throwErrors); } public override void StoreSnapshot() => StoreSnapshot(); } class FishSwimSlowParams : FishSwimParams { public static FishSwimSlowParams GetDefaultAnimParams(Character character) => GetDefaultAnimParams(character, AnimationType.SwimSlow); public static FishSwimSlowParams GetAnimParams(Character character, Either file, bool throwErrors = true) { return GetAnimParams(character, AnimationType.SwimSlow, file, throwErrors); } public override void StoreSnapshot() => StoreSnapshot(); } abstract class FishGroundedParams : GroundedMovementParams, IFishAnimation { protected static bool Check(Character character) { if (!character.AnimController.CanWalk) { DebugConsole.ThrowError($"{character.SpeciesName} cannot use run animations!", contentPackage: character.Prefab.ContentPackage); return false; } return true; } [Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the character be flipped depending on which direction it faces. Should usually be enabled on all characters that have distinctive upper and lower sides.")] public bool Flip { get; set; } [Serialize(1f, IsPropertySaveable.Yes, description: "Reduces continuous flipping when the character abruptly changes direction."), Editable] public float FlipCooldown { get; set; } [Serialize(0.5f, IsPropertySaveable.Yes, description: "How much it takes before the character flips. The timer starts when the character starts to move in the different direction."), Editable] public float FlipDelay { get; set; } [Serialize(10.0f, IsPropertySaveable.Yes, description: "How much force is used to move the head to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)] public float HeadMoveForce { get; set; } [Serialize(10.0f, IsPropertySaveable.Yes, description: "How much force is used to move the torso to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)] public float TorsoMoveForce { get; set; } [Serialize(8.0f, IsPropertySaveable.Yes, description: "How much force is used to move the feet to the correct position."), Editable(MinValueFloat = 0, MaxValueFloat = 100)] public float FootMoveForce { get; set; } [Serialize(50.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 1000, ValueStep = 1)] public float TailTorque { get; set; } [Serialize(0.0f, IsPropertySaveable.Yes, description: "Optional torque that's constantly applied to legs."), Editable(MinValueFloat = 0, MaxValueFloat = 1000)] public float LegTorque { get; set; } /// /// The angle of the collider when standing (i.e. out of water). /// In degrees. /// [Serialize(0f, IsPropertySaveable.Yes, description: "The angle of the character's collider when standing."), Editable(MinValueFloat = -360, MaxValueFloat = 360)] public float ColliderStandAngle { get => MathHelper.ToDegrees(ColliderStandAngleInRadians); set => ColliderStandAngleInRadians = MathHelper.ToRadians(value); } public float ColliderStandAngleInRadians { get; private set; } [Serialize(null, IsPropertySaveable.Yes), Editable] public string FootAngles { get => ParseFootAngles(FootAnglesInRadians); set => SetFootAngles(FootAnglesInRadians, value); } /// /// Key = limb id, value = angle in radians /// public Dictionary FootAnglesInRadians { get; set; } = new Dictionary(); /// /// In degrees. /// [Serialize(float.NaN, IsPropertySaveable.Yes), Editable(-360f, 360f)] public float TailAngle { get => float.IsNaN(TailAngleInRadians) ? float.NaN : MathHelper.ToDegrees(TailAngleInRadians); set { if (!float.IsNaN(value)) { TailAngleInRadians = MathHelper.ToRadians(value); } } } public float TailAngleInRadians { get; private set; } = float.NaN; } abstract class FishSwimParams : SwimParams, IFishAnimation { [Serialize(false, IsPropertySaveable.Yes, description: "Instead of linear movement (default), use a wave-like movement. Note: WaveAmplitude and WaveLength don't have any effect on this. It's synced with the movement speed."), Editable] public bool UseSineMovement { get; set; } [Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the character be flipped depending on which direction it faces. Should usually be enabled on all characters that have distinctive upper and lower sides.")] public bool Flip { get; set; } [Serialize(1f, IsPropertySaveable.Yes, description: "Reduces continuous flipping when the character abruptly changes direction."), Editable] public float FlipCooldown { get; set; } [Serialize(0.5f, IsPropertySaveable.Yes, description: "How much it takes before the character flips. The timer starts when the character starts to move in the different direction."), Editable] public float FlipDelay { get; set; } [Editable, Serialize(true, IsPropertySaveable.Yes, description: "If enabled, the character will simply be mirrored horizontally when it wants to turn around. If disabled, it will rotate itself to face the other direction.")] public bool Mirror { get; set; } [Editable, Serialize(true, IsPropertySaveable.Yes, description: "Disabling this will make mirroring instantaneous.")] public bool MirrorLerp { get; set; } [Serialize(5f, IsPropertySaveable.Yes), Editable] public float WaveAmplitude { get; set; } [Serialize(10.0f, IsPropertySaveable.Yes), Editable] public float WaveLength { get; set; } [Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should the character face towards the direction it's heading.")] public bool RotateTowardsMovement { get; set; } [Serialize(50.0f, IsPropertySaveable.Yes, description: "How much torque is used to rotate the tail to the correct orientation."), Editable(MinValueFloat = 0, MaxValueFloat = 2000, ValueStep = 1)] public float TailTorque { get; set; } [Serialize(1f, IsPropertySaveable.Yes, description: "Multiplier applied based on the angle difference between the tail and the main limb. Increasing the value prevents snake-like characters from getting tangled on themselves. Default = 1 (no boost)"), Editable(MinValueFloat = 1, MaxValueFloat = 100)] public float TailTorqueMultiplier { get; set; } [Serialize(null, IsPropertySaveable.Yes), Editable] public string FootAngles { get => ParseFootAngles(FootAnglesInRadians); set => SetFootAngles(FootAnglesInRadians, value); } [Serialize(false, IsPropertySaveable.Yes, description: "Should the animation be updated even if the character is not moving?"), Editable] public bool UpdateAnimationWhenNotMoving { get; set; } /// /// Key = limb id, value = angle in radians /// public Dictionary FootAnglesInRadians { get; set; } = new Dictionary(); /// /// In degrees. /// [Serialize(float.NaN, IsPropertySaveable.Yes), Editable(-360f, 360f)] public float TailAngle { get => float.IsNaN(TailAngleInRadians) ? float.NaN : MathHelper.ToDegrees(TailAngleInRadians); set { if (!float.IsNaN(value)) { TailAngleInRadians = MathHelper.ToRadians(value); } } } public float TailAngleInRadians { get; private set; } = float.NaN; } interface IFishAnimation { string FootAngles { get; set; } Dictionary FootAnglesInRadians { get; set; } float TailAngle { get; set; } float TailAngleInRadians { get; } float TailTorque { get; set; } bool Flip { get; set; } float FlipCooldown { get; set; } float FlipDelay { get; set; } } }