using System; using System.Collections.Generic; using UFS2.Gameplay; using UFS2.ScriptableObjects; using UnityEngine; public class FishFightState : IState { private static float takeTimeAmount = 5f; private static float spitOutBaitTimeThresholdWhenNoTension = 15f; private FishEntity entity; private FHook bait; private FLure lure; private FRod rod; private float takeTime; private float baitTakeAttempts; private bool isStrikeEnter; private bool isSpitoutBite; private float lastTimeWhenTensionApplied; private float wigglingTimer; private float wigglingAmount; private bool wigglingActive; private bool wigglingCooldown; private Vector3 moveDirection; private float changeTime; private LayerMask obstacleMask = LayerMask.GetMask("Default", "Terrain", "UnderwaterObject", "Bridges"); private float yankTimer; private int yankCount; private bool yankCooldown; private Rigidbody defaultBaitBody; private Rigidbody defaultLurebody; private bool isTimerCanChange => changeTime < Time.time; private bool isInWater { get { if (GameWaterSystem.IsPositionUnderWater(entity.transform.position + Vector3.up * 0.25f)) { return GameWaterSystem.IsPositionUnderWater(entity.JointAnchor.position); } return false; } } private float velocityLimit => entity.MoveSpeed / entity.Physics.Rbody.drag; public static event Action OnEnter; public static event Action OnExit; public static event Action OnFishCanTake; public static event Action OnFishStrikeEnter; public static event Action OnFishYank; public FishFightState(FishEntity entity) { this.entity = entity; } private void ConnectFishToLine(Rigidbody rb) { entity.Physics.Rbody.mass = 15f; entity.SetCurrentFishInFight(rod); Rigidbody endLineRigidbody = rod.fishingLine.currentLineHandler.EndLineRigidbody; entity.Physics.AttachFishToBody(endLineRigidbody, entity.GetJointAnchorPositionScaled); } public void Enter() { FishFightState.OnEnter?.Invoke(); bait = null; lure = null; rod = null; FHook component2; if (entity.LureTarget.TryGetComponent(out var component)) { lure = component; rod = component.currentRod; ConnectFishToLine(component.rigidbody); SettingLure(component, isJoinedToFish: true); } else if (entity.LureTarget.TryGetComponent(out component2)) { bait = component2; rod = component2.currentRod; ConnectFishToLine(component2.rigidbody); SettingBait(component2, isJoinedToFish: true); } takeTime = Time.time + takeTimeAmount; if ((bool)bait) { takeTime += 3.5f; } baitTakeAttempts = 3f; isStrikeEnter = false; isSpitoutBite = false; lastTimeWhenTensionApplied = Time.time; moveDirection = entity.transform.forward; changeTime = Time.time; entity.MoveSpeed = entity.Data.MoveMaxSpeed; entity.Target = null; SetCollidersTrigger(value: false); entity.SetAIEnabled(value: false); entity.ResetRotate(); entity.Physics.ResetTail(); entity.WeightTugging = FScriptsHandler.Instance.fishFightData.GetTuggingForceActiveFight(entity); entity.FightTime = 0f; entity.Stamina01 = 1f; yankTimer = Time.time; yankCount = 0; yankCooldown = false; if (Singleton.Instance.SettingsData.IsControllerVibrationEnabled) { GameManager.Instance.ControllerVibrate(0.5f, 1); } } public void Execute() { if (entity.IsCatched) { return; } SpitOutHandler(); if (isSpitoutBite) { return; } VelocityLimitHandler(); EnterStrike(); if ((bool)lure) { lure.transform.position = entity.JointAnchor.position; } if ((bool)bait) { bait.transform.position = entity.JointAnchor.position; } if ((bool)lure) { FightBehaviourHandler(); } if (!bait) { return; } if (!entity.IsHooked) { if (entity.Data.BehaviourType == FishBehaviourType.ActivePredator || entity.Data.BehaviourType == FishBehaviourType.PassivePredator) { FightBehaviourHandler(); } else { BaitBehaviourHandler(); } } else { FightBehaviourHandler(); } } public void Exit() { if ((bool)lure) { SettingLure(lure, isJoinedToFish: false); } if ((bool)bait) { SettingBait(bait, isJoinedToFish: false); } FishFightState.OnExit?.Invoke(); Rigidbody rbody = entity.Physics.Rbody; rbody.mass = 1f; rbody.useGravity = true; rbody.angularDrag = 1f; rbody.drag = 1f; entity.Physics.ResetTail(); entity.Physics.VelocityLimit = -1f; entity.SetAnimatorFloat("AngularVelocityY", 0f); entity.SetAnimatorFloat("speed", 0f); entity.SetAnimatorBool("jump", b: false); entity.SetIKAndAnimatorLayers(0f); entity.IsHooked = false; entity.LastCatchTime = Time.time; if ((bool)rod && (bool)rod.fishingLine) { rod.fishingLine.CurrentLineTension = 0f; } } private void SpitOutHandler() { if (isSpitoutBite) { return; } if (!entity.IsCanFight) { SpitOutBait(); } else if (entity.IsHooked && lastTimeWhenTensionApplied + spitOutBaitTimeThresholdWhenNoTension < Time.time) { SpitOutBait(); } else { if (entity.IsHooked || !(takeTime < Time.time)) { return; } if ((bool)bait && baitTakeAttempts > 0f) { FishFightState.OnFishCanTake?.Invoke(); baitTakeAttempts -= 1f; takeTime += 2f; Vector3[] array = new Vector3[3] { -entity.LureTarget.up, entity.LureTarget.right, -entity.LureTarget.right }; Vector3 direction = array[UnityEngine.Random.Range(0, array.Length)]; entity.Physics.MoveInDirection(direction, 10f, 15f, Time.deltaTime, ForceMode.Impulse); if (baitTakeAttempts <= 0f) { SpitOutBait(); } } if ((bool)lure) { SpitOutBait(); } } } private void VelocityLimitHandler() { entity.Physics.VelocityLimit = velocityLimit; if (!(entity.Data.Weight > 100f)) { return; } if (yankCooldown) { if (Time.time > yankTimer) { yankCooldown = false; } } else if (entity.Physics.Rbody.velocity.magnitude > velocityLimit * 2.5f) { yankCount++; yankTimer = Time.time + 2f; yankCooldown = true; if (yankCount >= 3) { rod.CutLine(destroyBaits: true); FishFightState.OnFishYank?.Invoke(); } } } private void SpitOutBait() { entity.SpitOutBait(); SetCollidersTrigger(value: true); if (!entity.IsCatched && Singleton.Instance.SettingsData.IsControllerVibrationEnabled) { GameManager.Instance.ControllerVibrate(0.5f, 1); } isSpitoutBite = true; } private void EnterStrike() { if (entity.IsHooked && !isStrikeEnter) { isStrikeEnter = true; FishFightState.OnFishStrikeEnter?.Invoke(); } } private void StaminaHandler() { entity.Stamina01 = 1f - Mathf.Clamp01(entity.FightTime / FScriptsHandler.Instance.fishFightData.GetFightTime(entity)); float tuggingForceActiveFight = FScriptsHandler.Instance.fishFightData.GetTuggingForceActiveFight(entity); float tuggingForceWeakFight = FScriptsHandler.Instance.fishFightData.GetTuggingForceWeakFight(entity); float tuggingForceExhausted = FScriptsHandler.Instance.fishFightData.GetTuggingForceExhausted(entity); entity.WeightTugging = Mathf.Lerp(tuggingForceWeakFight, tuggingForceActiveFight, entity.Stamina01); if (entity.Stamina01 <= 0f) { entity.WeightTugging = tuggingForceExhausted; entity.WeightTugging = Mathf.Min(entity.WeightTugging, 90f); } } private void DirectionChangeHandler() { bool directionChangeNeeded; bool escapeFound; Vector3 alleyEscapeDirection = GetAlleyEscapeDirection(out directionChangeNeeded, out escapeFound); if (!isInWater || directionChangeNeeded) { changeTime = Mathf.Min(changeTime, Time.time + 0.2f); } if (!isTimerCanChange) { return; } if (!isInWater && !directionChangeNeeded) { ChangeTime(0.5f, 1f); moveDirection = Vector3.down; return; } if (directionChangeNeeded) { ChangeTime(); moveDirection = alleyEscapeDirection; return; } ChangeTime(); Transform transform = FScriptsHandler.Instance.m_PlayerMain.transform; Vector3 forward = transform.forward; Vector3 right = transform.right; Vector3 up = transform.up; float num = Vector3.Distance(transform.position, entity.transform.position); List list = new List { right + -forward, -right + -forward, forward, right + forward, -right + forward, right, -right }; if (num > 6f) { list.Add(up + forward); list.Add(-up + forward); } int index = UnityEngine.Random.Range(0, list.Count); moveDirection = list[index]; } private void HandleLine() { _ = FScriptsHandler.Instance.m_PlayerMain; FFishingLine fishingLine = rod.fishingLine; float reelingDrag = rod.currentReel.reelingDrag; float linePullingForce = rod.currentReel.LinePullingForce; entity.Physics.SetJointDistance(fishingLine.currentLineHandler.ObiLineOut); fishingLine.CurrentLineTension = fishingLine.GetTension(entity.WeightTugging) * 2f; if (fishingLine.CurrentLineTension > 0f) { lastTimeWhenTensionApplied = Time.time; if (reelingDrag < 1f && fishingLine.CurrentLineTension > linePullingForce) { float num = Mathf.Clamp01((fishingLine.CurrentLineTension - linePullingForce) / 10f); float amount = entity.MoveSpeed * Time.deltaTime * num; fishingLine.currentLineHandler.SetRopeLenght(amount, rod); } } } private void HandleFishAnimator(bool jaw = false) { if (wigglingCooldown) { if (Time.time > wigglingTimer) { wigglingActive = false; wigglingCooldown = false; } } else if (wigglingActive) { if (Time.time > wigglingTimer) { wigglingActive = false; wigglingCooldown = true; wigglingTimer = Time.time + 3f; } } else if (!isInWater || entity.Physics.IsJointTension) { wigglingActive = true; wigglingCooldown = false; wigglingTimer = Time.time + 1f; } wigglingAmount = Mathf.MoveTowards(wigglingAmount, wigglingActive ? (entity.GetMoveSpeedAnimation() * 1.7f) : entity.GetMoveSpeedAnimation(), Time.deltaTime * 4f); entity.SetAnimatorFloat("AngularVelocityY", 0f); entity.SetAnimatorFloat("speed", wigglingAmount); entity.SetAnimatorBool("jump", !isInWater); entity.SetAnimatorBool("jaw", jaw); } private void BaitBehaviourHandler() { StaminaHandler(); moveDirection = entity.LureTarget.forward; float moveSpeed = Mathf.MoveTowards(entity.MoveSpeed, entity.MoveSpeed * 0.4f, Time.deltaTime); float rotationSpeed = 3f; entity.MoveSpeed = moveSpeed; entity.Physics.MoveInDirection(moveDirection, entity.MoveSpeed, rotationSpeed, Time.deltaTime); HandleLine(); HandleFishAnimator(jaw: true); } private void FightBehaviourHandler() { entity.FightTime += Time.deltaTime; StaminaHandler(); DirectionChangeHandler(); float moveSpeed = Mathf.Lerp(entity.Data.MoveSpeed * 0.7f, entity.Data.MoveMaxSpeed, entity.Stamina01); if (entity.Stamina01 <= 0f) { moveSpeed = entity.Data.MoveSpeed * 0.3f; } float rotationSpeed = 3f; entity.MoveSpeed = moveSpeed; entity.Physics.MoveInDirection(moveDirection, entity.MoveSpeed, rotationSpeed, Time.deltaTime); HandleLine(); HandleFishAnimator(); if (rod.fishingLine.linelenghtDiferent > 0.8f && Singleton.Instance.SettingsData.IsControllerVibrationEnabled) { GameManager.Instance.ControllerVibrate(0.5f, 1); } } private Vector3 GetAlleyEscapeDirection(out bool directionChangeNeeded, out bool escapeFound) { Vector3 result = Vector3.zero; directionChangeNeeded = true; escapeFound = false; Vector3 right = entity.transform.right; Vector3 up = entity.transform.up; Vector3 forward = entity.transform.forward; List dirList = new List { forward, (up + forward).normalized, (-up + forward).normalized, (right + forward).normalized, (-right + forward).normalized }; bool flag = false; Vector3 hitNormal = Vector3.zero; Vector3 hitPoint = Vector3.zero; foreach (Vector3 item in dirList) { if (RaycastForObstacles(item, out hitNormal, out hitPoint)) { flag = true; break; } } Vector3 directionOppositeObstacle; Vector3 perpendicular; if (flag) { directionChangeNeeded = true; directionOppositeObstacle = (entity.transform.position - hitPoint).normalized; Vector3 lhs = ((Mathf.Abs(Vector3.Dot(directionOppositeObstacle, Vector3.up)) < 0.99f) ? Vector3.up : Vector3.right); Vector3 hitNormal2 = Vector3.Cross(lhs, directionOppositeObstacle); perpendicular = hitNormal2.normalized; dirList.Clear(); dirList.Add(directionOppositeObstacle); AddDirectionsAround(30f); AddDirectionsAround(70f); dirList.Sort(delegate(Vector3 a, Vector3 b) { float value = Vector3.Dot(forward, a); return Vector3.Dot(forward, b).CompareTo(value); }); foreach (Vector3 item2 in dirList) { if (!RaycastForObstacles(item2, out hitNormal2, out var _)) { result = item2; directionChangeNeeded = true; escapeFound = true; break; } } } else { result = forward; directionChangeNeeded = false; escapeFound = true; } if (!escapeFound) { Debug.Log("Fish fight rotation return zero"); } return result; void AddDirectionsAround(float angle) { for (int i = 0; i < 4; i++) { Vector3 vector = directionOppositeObstacle; vector = Quaternion.AngleAxis(angle, perpendicular) * vector; vector = Quaternion.AngleAxis(90f * (float)i, directionOppositeObstacle) * vector; vector.Normalize(); dirList.Add(vector); } } } private void SetCollidersTrigger(bool value) { Collider[] componentsInChildren = entity.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].isTrigger = value; } } private void ChangeTime(float min = 3f, float max = 7f) { changeTime = Time.time + UnityEngine.Random.Range(min, max); } private bool RaycastForObstacles(Vector3 direction, out Vector3 hitNormal, out Vector3 hitPoint, float originBackwardOffset = 0.2f) { Vector3 origin = -direction * originBackwardOffset + entity.transform.position; float maxDistance = Vector3.Distance(entity.JointAnchor.position, entity.transform.position) + originBackwardOffset + 0.5f; RaycastHit hitInfo; bool num = Physics.Raycast(origin, direction, out hitInfo, maxDistance, obstacleMask); if (num) { hitNormal = hitInfo.normal; hitPoint = hitInfo.point; return num; } hitNormal = Vector3.zero; hitPoint = Vector3.zero; return num; } private void SettingBait(FHook bait, bool isJoinedToFish) { Rigidbody rigidbody = bait?.currentRod?.fishingLine?.currentLineHandler?.EndLineRigidbody; if (isJoinedToFish) { defaultBaitBody = bait.CfgJoint.connectedBody; bait.HideRenderer(); bait.rigidbody.rotation = entity.JointAnchor.rotation; bait.CfgJoint.connectedBody.velocity = entity.Physics.Rbody.velocity; bait.CfgJoint.connectedBody = null; bait.GetComponent().enabled = false; bait.Rigidbody.isKinematic = true; bait.CfgJoint.enableCollision = false; bait.CfgJoint.autoConfigureConnectedAnchor = false; bait.CfgJoint.connectedAnchor = entity.GetJointAnchorPositionScaled; if ((bool)rigidbody) { rigidbody.useGravity = false; } } else { bait.ShowRenderer(); bait.CfgJoint.connectedBody = defaultBaitBody; bait.GetComponent().enabled = true; bait.Rigidbody.isKinematic = false; bait.CfgJoint.enableCollision = true; bait.CfgJoint.autoConfigureConnectedAnchor = false; bait.CfgJoint.connectedAnchor = Vector3.zero; if ((bool)defaultBaitBody) { bait.transform.position = defaultBaitBody.position; } if ((bool)rigidbody) { rigidbody.useGravity = true; } } } private void SettingLure(FLure lure, bool isJoinedToFish) { Rigidbody endLineRigidbody = lure.currentRod.fishingLine.currentLineHandler.EndLineRigidbody; if (isJoinedToFish) { defaultLurebody = lure.CfgJoint.connectedBody; lure.HideRenderer(); lure.CfgJoint.connectedBody.velocity = entity.Physics.Rbody.velocity; lure.rigidbody.rotation = entity.JointAnchor.rotation; lure.CfgJoint.connectedBody = null; lure.GetComponent().enabled = false; lure.rigidbody.isKinematic = true; lure.CfgJoint.enableCollision = false; lure.CfgJoint.autoConfigureConnectedAnchor = false; lure.CfgJoint.connectedAnchor = entity.GetJointAnchorPositionScaled; endLineRigidbody.useGravity = false; } else { lure.ShowRenderer(); lure.CfgJoint.connectedBody = defaultLurebody; lure.GetComponent().enabled = true; lure.rigidbody.isKinematic = false; lure.CfgJoint.enableCollision = true; lure.CfgJoint.autoConfigureConnectedAnchor = false; lure.CfgJoint.connectedAnchor = Vector3.zero; lure.transform.position = defaultLurebody.position; endLineRigidbody.useGravity = true; } } }