using System; using Obvious.Soap; using UnityEngine; public class FishAI : MonoBehaviour { public Transform anchor; public Action OnLureTargetReached; [SerializeField] private LayerMask lureCheckLayerMask; [SerializeField] private LayerMask obstacleLayerMask; public ScriptableEventNoParam onLureIsTooLargeForFish; [SerializeField] private FishZoneObject fishZone; public float WaitTime = 1f; public FishData fishData; public float length; private bool isLure; private Rigidbody _rbody; private WaterBody _waterBody; private int _terrainLayerMaskId; private int _fishLayerMaskId; private int _lureLayerMaskId; private LayerMask _layerCollisionMask; private Vector3 _moveDirection; private Vector3 _moveTargetPosition; private Vector3 _velocity; public Lure lure; private float distanceToTarget; private RaycastHit[] rayHits = new RaycastHit[1]; private float _CheckLureCooldown = 2.5f; private float _CheckLureTime; public FishZoneObject FishZone { set { fishZone = value; } } public bool isTargetReached { get; private set; } private float _getAreaDistance => UnityEngine.Random.Range(fishData.areaDistance * 0.2f, fishData.areaDistance); public event Action OnDestinationReached = delegate { }; private void Awake() { _rbody = GetComponent(); _waterBody = GetComponent(); _terrainLayerMaskId = LayerMask.NameToLayer("Terrain"); _fishLayerMaskId = LayerMask.NameToLayer("Fish"); _lureLayerMaskId = LayerMask.NameToLayer("Lure"); _layerCollisionMask = LayerMask.GetMask("Terrain", "Fish", "Lure"); } public void SetMovePosition(Vector3 position) { _moveTargetPosition = position; } public void SetMoveToTargetPosition(Vector3 position) { isTargetReached = false; _moveTargetPosition = position; } public void SetLure(Lure lure) { this.lure = lure; } public void SetRandomizedMoveToTargetPosition(Vector3 originPosition) { isTargetReached = false; _moveTargetPosition = originPosition + UnityEngine.Random.onUnitSphere * _getAreaDistance; float value = Mathf.Clamp(_moveTargetPosition.y, base.transform.position.y - 2f, base.transform.position.y + 2f); _moveTargetPosition.y = Mathf.Clamp(value, float.NegativeInfinity, -0.5f); } public void SeekUpdateHandler(float multiply = 1f) { _rbody.AddForce(_velocity, ForceMode.Acceleration); TargetReachUpdate(lure ? 0.15f : 0.1f); CalculateFishMovement(multiply); CheckRaycastForAction(); LureDetectHandler(); } public void LureEatUpdateHandler(float multiply = 1f) { _rbody.AddForce(_velocity, ForceMode.Acceleration); TargetReachUpdate(); CalculateFishMovement(multiply); CheckRaycastForAction(); } public void FightUpdateHandler(float multiply = 1f) { _rbody.AddForce(_velocity, ForceMode.Acceleration); TargetReachUpdate(); CalculateFishMovement(multiply); } private void TargetReachUpdate(float treshold = 0.1f) { distanceToTarget = Vector3.Distance(anchor.position, _moveTargetPosition); isTargetReached = distanceToTarget < treshold; if (isTargetReached) { _velocity = Vector3.zero; } } private void CalculateFishMovement(float speedMultiplier = 1f) { _moveDirection = _moveTargetPosition - anchor.position; if (_rbody.linearVelocity != Vector3.zero) { Quaternion b = Quaternion.LookRotation(_rbody.linearVelocity, Vector3.up); base.transform.rotation = Quaternion.Slerp(base.transform.rotation, b, Time.deltaTime * Mathf.Clamp(5f * speedMultiplier, 5f, 10f)); } Vector3 vector = _moveDirection.normalized * Mathf.Clamp(fishData.Speed, 0f, float.PositiveInfinity); vector *= speedMultiplier; _velocity = ((_velocity.sqrMagnitude > vector.sqrMagnitude) ? Vector3.Slerp(_velocity, vector, Time.deltaTime * 1f) : vector); } private void CheckRaycastForAction() { if (Physics.Raycast(anchor.position, _moveDirection, out var hitInfo, fishData.Speed, _layerCollisionMask)) { int layer = hitInfo.collider.gameObject.layer; if (layer == _terrainLayerMaskId) { _velocity = Vector3.zero; isTargetReached = true; lure = null; SetCheckLureCooldown(); } else if (layer == _fishLayerMaskId && hitInfo.transform.localScale.x > base.transform.localScale.x) { _velocity = _moveDirection.normalized * (fishData.Speed * 10f); _moveTargetPosition = hitInfo.point + hitInfo.normal * _getAreaDistance; _moveTargetPosition.y = base.transform.position.y; lure = null; SetCheckLureCooldown(); } } } public void ResetBehaviour() { lure = null; isTargetReached = false; } private void OnCollisionStay(Collision collision) { if (collision.gameObject.layer == _terrainLayerMaskId) { _moveTargetPosition += collision.contacts[0].normal.normalized; } if (!_waterBody.InWater && collision.gameObject.layer == _terrainLayerMaskId) { _rbody.AddForce(Vector3.up * 1f, ForceMode.Impulse); } } private void LureDetectHandler() { if (!(Time.time < _CheckLureTime) && !lure && Physics.RaycastNonAlloc(base.transform.position, base.transform.forward, rayHits, fishData.EyeDetectionDistance, lureCheckLayerMask) > 0 && rayHits[0].transform.TryGetComponent(out var component) && !component.IsFishConnected) { bool flag = false; float num = length * 0.4f; float num2 = length * 0.01f; if (component.IsLure) { flag = fishData.CheckLure(component.LureData.ID); } if (component.IsBait) { flag = fishData.CheckBait(component.BaitData.ID); } if (component.lureSize < num && component.lureSize > num2 && flag) { lure = component; isTargetReached = false; _moveTargetPosition = component.transform.position; Debug.Log($"Lure {component.name} size: {component.lureSize} detected by fish and accepted {base.gameObject.name} l: {length}"); } else { onLureIsTooLargeForFish.Raise(); string text = ((component.lureSize >= num) ? "large" : "small"); Debug.Log($"Lure {component.name} is too {text} size: {component.lureSize} for fish: {base.gameObject.name} l: {length}"); } } } private void SetCheckLureCooldown() { _CheckLureTime = Time.time + _CheckLureCooldown; } }