using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Obvious.Soap; using ShiningGames.UFS2; using UFS2.ScriptableObjects; using UnityEngine; namespace UFS2.Gameplay { public class FishEntityManager : Singleton { public class Fish { private FishData fishData; private Transform lastTarget; private Vector3 position; private Vector3 eulerRotation; private float weight; private bool isCulled; public static event Action OnCull; public static event Action OnDraw; public float DistanceTo(Vector3 position) { return Vector3.Distance(position, this.position); } public void Cull() { if (!isCulled) { isCulled = true; Fish.OnCull?.Invoke(this); } } public void Show() { if (isCulled) { isCulled = false; Fish.OnDraw?.Invoke(this); } } } [Tooltip("How many times check raycast on fish, use this for optimiziation fish scanners")] [SerializeField] private int _RayPerFrame = 5; [SerializeField] private List _FisheshList; [SerializeField] private int _IterationPos; [SerializeField] private LevelFishData levelFishData; [SerializeField] private FloatVariable fishGrowInterval; [SerializeField] private Vector2IntVariable FishGrowRate; private bool isUpdating = true; private bool isInitializationLoading = true; private List cullTransforms = new List(); private List cullTransformsPending = new List(); private static List transformsCulledBeforeInitialize; public List AllFishes => _FisheshList; public List CullTransforms => cullTransforms; private float DespawnCullDistance { get { if ((float)cullTransformsPending.Count != 0f) { return levelFishData.DrawDistance + 20f; } return levelFishData.CullDistance; } } public static event Action OnGrowFish; public static event Action OnFishReachMaxSize; private void Start() { _FisheshList = UnityEngine.Object.FindObjectsOfType().ToList(); if (transformsCulledBeforeInitialize == null) { return; } foreach (Transform item in transformsCulledBeforeInitialize) { AddTransformCull(item); } transformsCulledBeforeInitialize = null; } private void OnEnable() { FishSpawner.OnSpawnFish += AddFish; FishSpawner.OnDespawnFish += RemoveFish; FishSpawner.OnStartFishesSpawned += FishSpawner_OnStartFishesSpawned; StartCoroutine(CullTransformsPendingCoroutine()); StartCoroutine(FarthestFishDespawnCoroutine()); StartCoroutine(FishAIRaycastingCoroutine()); StartCoroutine(FishGrowCoroutine()); } private void OnDisable() { FishSpawner.OnSpawnFish -= AddFish; FishSpawner.OnDespawnFish -= RemoveFish; FishSpawner.OnStartFishesSpawned -= FishSpawner_OnStartFishesSpawned; StopAllCoroutines(); } private void FishSpawner_OnStartFishesSpawned() { isInitializationLoading = false; } private IEnumerator SpawnFishInPointCoroutine(FishEntity entity) { int num = 0; int abortCounter = 0; int perFrameMax = 15; int abortMax = 100; float distanceToDraw = (isInitializationLoading ? 0f : levelFishData.DrawDistance); Transform transform = GetCullTargetToCheck(preferPending: true); isUpdating = false; do { FishTarget fishTarget = FishTargetBaker.GetRandomTarget(entity.Data.BehaviourType, transform); if (!FishTargetBaker.Instance.CheckIfFishCanBeSpawnedInArea(entity, fishTarget.Area)) { fishTarget = null; } if (fishTarget != null) { int num2 = ((!(transform == null)) ? 1 : 0); if (num2 > 0) { float num3 = Vector3.Distance(transform.position, fishTarget.Transform.position); if (num3 > distanceToDraw && num3 < DespawnCullDistance) { num2--; } } if (num2 <= 0) { entity.Draw(fishTarget); if (isInitializationLoading) { yield return null; } else { yield return new WaitForSeconds(0.1f); } break; } } num++; abortCounter++; if (num > perFrameMax) { yield return null; transform = GetCullTargetToCheck(preferPending: false); num = 0; } } while (abortCounter <= abortMax); isUpdating = true; Transform GetCullTargetToCheck(bool preferPending) { Transform result = null; if (preferPending && cullTransformsPending.Count > 0) { result = cullTransformsPending[UnityEngine.Random.Range(0, cullTransformsPending.Count)]; } else if (cullTransforms.Count > 0) { result = cullTransforms[UnityEngine.Random.Range(0, cullTransforms.Count)]; } return result; } } private IEnumerator CullTransformsPendingCoroutine() { int nearFishCountMin = 5; float NearFishDistance = levelFishData.DrawDistance + 10f; int targetIndex = 0; while (true) { ValidateTransformsList(); if (cullTransforms != null && cullTransforms.Count > 0) { if (targetIndex >= cullTransforms.Count) { targetIndex = 0; } Transform transform = cullTransforms[targetIndex]; targetIndex++; int num = 0; if (_FisheshList != null && _FisheshList.Count > 0) { foreach (FishEntity fishesh in _FisheshList) { if (Vector3.Distance(transform.position, fishesh.transform.position) < NearFishDistance) { num++; if (num >= nearFishCountMin) { break; } } } } if (num >= nearFishCountMin) { if (cullTransformsPending.Contains(transform)) { cullTransformsPending.Remove(transform); } } else if (!cullTransformsPending.Contains(transform)) { cullTransformsPending.Add(transform); } } yield return new WaitForSeconds(3f); } } private IEnumerator FarthestFishDespawnCoroutine() { while (true) { if (!MultiplayerFishSpawner.IsSlave && cullTransformsPending.Count > 0) { FishEntity fishEntity = null; float num = 0f; foreach (FishEntity fishesh in _FisheshList) { if (!(fishesh != null) || !(FishEntity.CurrentFishInFight != fishesh) || fishesh.CurrentFishState == FishState.Atack || MultiplayerFishSpawner.CheckFishInteractionWithPlayer(fishesh) || fishesh.IsCulled) { continue; } float num2 = float.MaxValue; foreach (Transform cullTransform in cullTransforms) { float num3 = Vector3.Distance(fishesh.transform.position, cullTransform.position); if (num3 < num2) { num2 = num3; } } if (num2 > num) { num = num2; fishEntity = fishesh; } } if (fishEntity != null) { fishEntity.Cull(); } } yield return new WaitForSeconds(6f); } } private IEnumerator FishAIRaycastingCoroutine() { int currentIndex = 0; while (true) { int num = 0; while (num < _RayPerFrame && _FisheshList.Count > 0) { if (currentIndex >= _FisheshList.Count) { currentIndex = 0; } FishEntity fishEntity = _FisheshList[currentIndex]; currentIndex++; num++; if (fishEntity != null && FishEntity.CurrentFishInFight != fishEntity && !fishEntity.IsCulled) { fishEntity.EnableAIRaycasting(); } } yield return null; } } private IEnumerator FishGrowCoroutine() { int currentIndex = 0; while (true) { if (!MultiplayerFishSpawner.IsSlave && fishGrowInterval != null && FishGrowRate != null && _FisheshList != null && _FisheshList.Count > 0) { if (currentIndex >= _FisheshList.Count) { currentIndex = 0; } FishEntity fish = _FisheshList[currentIndex]; currentIndex++; if (fish != null && FishEntity.CurrentFishInFight != fish && fish.CurrentFishState != FishState.Atack && !MultiplayerFishSpawner.CheckFishInteractionWithPlayer(fish) && !fish.IsCulled && !FishIsNerbyTarget(fish)) { float y = levelFishData.FishesSetting.First((LevelFishData.FishDataHelper element) => element.FishData == fish.Data).Weight.y; float num = y * ((float)UnityEngine.Random.Range(FishGrowRate.Value.x, FishGrowRate.Value.y) * 0.01f); float num2 = fish.Weight + num; if (num2 > y) { FishEntityManager.OnFishReachMaxSize?.Invoke(fish); } else { FishEntityManager.OnGrowFish?.Invoke(fish, num2); } } yield return new WaitForSeconds(fishGrowInterval.Value / (float)_FisheshList.Count); } else { yield return new WaitForSeconds(1f); } } bool FishIsNerbyTarget(FishEntity fishEntity) { bool result = false; foreach (Transform cullTransform in cullTransforms) { if (Vector3.Distance(fishEntity.transform.position, cullTransform.position) <= 10f) { result = true; break; } } return result; } } private void CullUpdater() { if (MultiplayerFishSpawner.IsSlave) { return; } for (int i = 0; i < _RayPerFrame; i++) { if (_FisheshList == null) { break; } if (_FisheshList.Count <= 0) { break; } if (_IterationPos >= _FisheshList.Count) { _IterationPos = 0; } FishEntity fishEntity = _FisheshList[_IterationPos]; _IterationPos++; if (!(fishEntity != null) || !(FishEntity.CurrentFishInFight != fishEntity) || fishEntity.CurrentFishState == FishState.Atack || MultiplayerFishSpawner.CheckFishInteractionWithPlayer(fishEntity)) { continue; } int num = cullTransforms.Count; foreach (Transform cullTransform in cullTransforms) { bool flag = Vector3.Distance(cullTransform.position, fishEntity.transform.position) > DespawnCullDistance; if (!fishEntity.IsCulled && flag) { num--; } } if (fishEntity.IsCulled) { StartCoroutine(SpawnFishInPointCoroutine(fishEntity)); break; } if (num <= 0) { fishEntity.Cull(); StartCoroutine(SpawnFishInPointCoroutine(fishEntity)); break; } } } private void RemoveCull() { if (!MultiplayerFishSpawner.IsSlave || _FisheshList == null || _FisheshList.Count <= 0) { return; } foreach (FishEntity fishesh in _FisheshList) { if (fishesh != null && fishesh.IsCulled) { fishesh.Draw(); } } } private void Update() { ValidateTransformsList(); if (!MultiplayerFishSpawner.IsSlave) { if (isUpdating) { CullUpdater(); } } else { RemoveCull(); } } public static void AddTransformCull(Transform t) { if (Singleton.Instance == null) { if (transformsCulledBeforeInitialize == null) { transformsCulledBeforeInitialize = new List(); } transformsCulledBeforeInitialize.Add(t); } else { Singleton.Instance.cullTransforms.Add(t); Singleton.Instance.cullTransformsPending.Add(t); } } public static void RemoveTransformCull(Transform t) { if ((bool)Singleton.Instance) { Singleton.Instance.cullTransforms.Remove(t); Singleton.Instance.cullTransformsPending.Remove(t); } } private void ValidateTransformsList() { cullTransforms.RemoveAll((Transform element) => element == null); cullTransformsPending.RemoveAll((Transform element) => element == null); } private void AddFish(FishEntity fish, int levelFishDataIndex) { _FisheshList.Add(fish); } private void RemoveFish(FishEntity fish) { _FisheshList.Remove(fish); } } }