Files
2026-03-04 10:03:45 +08:00

491 lines
12 KiB
C#

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<FishEntityManager>
{
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<Fish> OnCull;
public static event Action<Fish> 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<FishEntity> _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<Transform> cullTransforms = new List<Transform>();
private List<Transform> cullTransformsPending = new List<Transform>();
private static List<Transform> transformsCulledBeforeInitialize;
public List<FishEntity> AllFishes => _FisheshList;
public List<Transform> CullTransforms => cullTransforms;
private float DespawnCullDistance
{
get
{
if ((float)cullTransformsPending.Count != 0f)
{
return levelFishData.DrawDistance + 20f;
}
return levelFishData.CullDistance;
}
}
public static event Action<FishEntity, float> OnGrowFish;
public static event Action<FishEntity> OnFishReachMaxSize;
private void Start()
{
_FisheshList = UnityEngine.Object.FindObjectsOfType<FishEntity>().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<FishEntityManager>.Instance == null)
{
if (transformsCulledBeforeInitialize == null)
{
transformsCulledBeforeInitialize = new List<Transform>();
}
transformsCulledBeforeInitialize.Add(t);
}
else
{
Singleton<FishEntityManager>.Instance.cullTransforms.Add(t);
Singleton<FishEntityManager>.Instance.cullTransformsPending.Add(t);
}
}
public static void RemoveTransformCull(Transform t)
{
if ((bool)Singleton<FishEntityManager>.Instance)
{
Singleton<FishEntityManager>.Instance.cullTransforms.Remove(t);
Singleton<FishEntityManager>.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);
}
}
}