226 lines
7.6 KiB
C#
226 lines
7.6 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Frozen;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using BITKit;
|
|
using BITKit.Entities;
|
|
using Cysharp.Threading.Tasks;
|
|
using DrawXXL;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Net.BITKit.Quadtree;
|
|
using Net.Project.B.Item;
|
|
using Net.Project.B.WorldNode;
|
|
using Project.B.Item;
|
|
using Project.B.Map;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEngine.Pool;
|
|
using Object = UnityEngine.Object;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace Net.Project.B.Loot
|
|
{
|
|
public class UnityLootService : ILootService, IDisposable
|
|
{
|
|
private readonly ILogger<UnityLootService> _logger;
|
|
|
|
private readonly IManagedItemService _managedItemService;
|
|
|
|
private readonly IEntitiesService _entitiesService;
|
|
|
|
private readonly IAsyncTicker _asyncTicker;
|
|
|
|
private readonly IReadOnlyDictionary<string, IScriptableLoot> _scriptableLoots;
|
|
|
|
private readonly IReadOnlyDictionary<int, ScriptableItem> _scriptableItems;
|
|
|
|
private readonly IGameMapService _gameMapService;
|
|
|
|
private Quadtree _quadtree = new(default, new float2(2048, 2048));
|
|
|
|
private readonly ConcurrentDictionary<int, (Matrix4x4 matrix4X4, UnityLootNode lootNode)> _lootNodes = new();
|
|
|
|
private readonly ConcurrentQueue<int> _removeQueue = new();
|
|
|
|
private Quadtree _spawnTree = new Quadtree(default, new float2(2048, 2048));
|
|
|
|
public UnityLootService(IEntitiesService entitiesService, IManagedItemService managedItemService,
|
|
IAsyncTicker asyncTicker, IGameMapService gameMapService, ILogger<UnityLootService> logger)
|
|
{
|
|
_entitiesService = entitiesService;
|
|
_managedItemService = managedItemService;
|
|
_asyncTicker = asyncTicker;
|
|
_gameMapService = gameMapService;
|
|
_logger = logger;
|
|
|
|
_entitiesService.OnAdd += OnAdd;
|
|
|
|
_scriptableLoots = _entitiesService.QueryComponents<IScriptableLoot>()
|
|
.ToArray().ToFrozenDictionary(x => x.Name, x => x);
|
|
_scriptableItems = _entitiesService.QueryComponents<ScriptableItem>()
|
|
.ToArray().ToFrozenDictionary(x => x.Id, x => x);
|
|
|
|
_asyncTicker.OnTickAsync += OnTickAsync;
|
|
|
|
_gameMapService.OnMapChange += OnMapChange;
|
|
}
|
|
|
|
private UniTask OnMapChange(string arg)
|
|
{
|
|
_quadtree = new Quadtree(default, new float2(2048, 2048));
|
|
|
|
_spawnTree = new Quadtree(default, new float2(2048, 2048));
|
|
|
|
return UniTask.CompletedTask;
|
|
}
|
|
|
|
private UniTask OnTickAsync(float arg)
|
|
{
|
|
var cameraPosition = Camera.main.transform.position;
|
|
|
|
var query =_quadtree.Query(new float2(cameraPosition.x, cameraPosition.z), 16);
|
|
|
|
foreach (var id in query)
|
|
{
|
|
Spawn(id);
|
|
_removeQueue.Enqueue(id);
|
|
}
|
|
while (_removeQueue.TryDequeue(out var id))
|
|
{
|
|
_quadtree.Remove(id);
|
|
}
|
|
return UniTask.CompletedTask;
|
|
}
|
|
|
|
private void OnAdd(IEntity obj)
|
|
{
|
|
if (obj.ServiceProvider.GetService<UnityLootNode>() is not { } lootNode) return;
|
|
|
|
var transform = obj.ServiceProvider.GetRequiredService<Transform>();
|
|
|
|
var pos = transform.position;
|
|
|
|
var matrix = Matrix4x4.TRS(transform.position, transform.rotation, transform.localScale);
|
|
|
|
_lootNodes.TryAdd(obj.Id, new(matrix, lootNode));
|
|
|
|
_quadtree.Insert(obj.Id, new float2(pos.x, pos.z));
|
|
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_gameMapService.OnMapChanging -= OnMapChange;
|
|
_entitiesService.OnAdd -= OnAdd;
|
|
_asyncTicker.OnTickAsync -= OnTickAsync;
|
|
}
|
|
|
|
public UniTask<IReadOnlyDictionary<int, int>> GetLoot(IWorldLootType type)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private void Spawn(int lootId)
|
|
{
|
|
var lootList = ListPool<IRuntimeItem>.Get();
|
|
|
|
try
|
|
{
|
|
if (_lootNodes.TryRemove(lootId, out var v) is false) return;
|
|
|
|
var matrix = v.matrix4X4;
|
|
var lootNode = v.lootNode;
|
|
|
|
float3 position = matrix.GetPosition();
|
|
|
|
if (_scriptableLoots.TryGetValue(lootNode.LootName, out var scriptableLoot) is false) return;
|
|
|
|
var sumWeight = scriptableLoot.ItemWeights.Values.Sum();
|
|
sumWeight = math.clamp(sumWeight, 100, sumWeight);
|
|
|
|
var spawned = _spawnTree.Query(position.xz, 8);
|
|
|
|
var spawnedThisFrame = 0;
|
|
|
|
var max = 3;
|
|
|
|
if (spawned.Length > max)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var count = Random.Range(0, max);
|
|
|
|
|
|
for (var i = 0; i < count && spawnedThisFrame++ < max; i++)
|
|
{
|
|
var id = scriptableLoot.ItemWeights.ElementAt(Random.Range(0, scriptableLoot.ItemWeights.Count))
|
|
.Key;
|
|
var weight = scriptableLoot.ItemWeights[id];
|
|
|
|
if (Random.Range(0, sumWeight) <= weight) continue;
|
|
var runtimeItem = _scriptableItems[id].CreateRuntimeItem();
|
|
_managedItemService.AddOrUpdateItem(runtimeItem);
|
|
|
|
lootList.Add(runtimeItem);
|
|
}
|
|
|
|
if (_entitiesService.Entities.TryGetValue(lootId, out var lootEntity) is false) return;
|
|
|
|
lootEntity.ServiceProvider
|
|
.QueryComponents(out IRuntimeItemContainer container);
|
|
lootEntity.ServiceProvider
|
|
.QueryComponents(out Transform transform);
|
|
|
|
if (lootList.Count > 0)
|
|
{
|
|
if (container is not null)
|
|
{
|
|
foreach (var runtimeItem in lootList)
|
|
{
|
|
container.Add(runtimeItem);
|
|
_spawnTree.Insert(runtimeItem.Id,position.xz);
|
|
}
|
|
}
|
|
else if (transform && transform.TryGetComponent<Collider>(out var collider))
|
|
{
|
|
foreach (var runtimeItem in lootList)
|
|
{
|
|
var random = new Unity.Mathematics.Random();
|
|
random.InitState((uint)Random.Range(1, uint.MaxValue));
|
|
var randomPoint = random.NextFloat3(collider.bounds.min, collider.bounds.max);
|
|
var randomRotation = random.NextQuaternionRotation();
|
|
|
|
|
|
_managedItemService.InstanceItem(randomPoint, randomRotation, runtimeItem);
|
|
|
|
_spawnTree.Insert(runtimeItem.Id,randomPoint.xz);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var runtimeItem in lootList)
|
|
{
|
|
_managedItemService.InstanceItem(matrix.GetPosition(), matrix.rotation, runtimeItem);
|
|
|
|
_spawnTree.Insert(runtimeItem.Id,position.xz);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
finally
|
|
{
|
|
ListPool<IRuntimeItem>.Release(lootList);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
}
|