using System; using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.Serialization; using Random = UnityEngine.Random; namespace BlueWater.Items { public class ItemManager : Singleton { [SerializeField, Required] private Item _defaultItemPrefab; [SerializeField, Required] private ItemDataSo _itemDataSo; private Dictionary _itemDatas; [SerializeField, Required] private ItemDropTableSo _itemDropTableSo; private Dictionary _itemDropTables; [SerializeField, Required] private FoodDataSo _foodDataSo; private Dictionary _foodDatas; [SerializeField, Required] private CocktailDataSo _cocktailDataSo; private Dictionary _cocktailDatas; [FormerlySerializedAs("_drinkDataSo")] [SerializeField, Required] private LiquidDataSo liquidDataSo; private Dictionary _drinkDatas; [field: SerializeField, Required] public ItemSlotDataSo ItemSlotDataSo { get; private set; } [Title("드롭 아이템 설정")] [SerializeField] private float _randomDropRadius = 3f; [SerializeField] private float _minSeparationDistance = 1.5f; private const int MaxAttempts = 1000; protected override void OnAwake() { base.OnAwake(); Initialize(); } private void Initialize() { _itemDatas = InitializeDictionary(_itemDataSo.Datas, data => data.Idx); _itemDropTables = InitializeDictionary(_itemDropTableSo.ItemDropTables, table => table.CharacterData.CharacterIdx); _foodDatas = InitializeDictionary(_foodDataSo.Datas, data => data.Idx); _cocktailDatas = InitializeDictionary(_cocktailDataSo.Datas, data => data.Idx); _drinkDatas = InitializeDictionary(liquidDataSo.Datas, data => data.Idx); } private Dictionary InitializeDictionary(List dataList, Func keySelector) { return dataList.ToDictionary(keySelector); } public void ItemDropRandomPosition(string idx, Vector3 dropPosition, float randomDropRadius = float.PositiveInfinity) { var itemDropTable = GetItemDropTableByIdx(idx); if (itemDropTable == null) return; var droppedItemList = itemDropTable.GetDroppedItemList(); var droppedPositions = new List(); foreach (var element in droppedItemList) { Vector3 newDropPosition; var attempts = 0; do { var radius = float.IsPositiveInfinity(randomDropRadius) ? _randomDropRadius : randomDropRadius; attempts++; var newDropPositionX = Random.Range(dropPosition.x - radius, dropPosition.x + radius); var newDropPositionZ = Random.Range(dropPosition.z - radius, dropPosition.z + radius); newDropPosition = new Vector3(newDropPositionX, dropPosition.y, newDropPositionZ); } while (!IsDropPositionAvailable(newDropPosition, droppedPositions) && attempts < MaxAttempts); droppedPositions.Add(newDropPosition); var itemPrefab = _itemDatas[element.Idx].ItemPrefab; if (!itemPrefab) { itemPrefab = _defaultItemPrefab; } var instantiateItem = Instantiate(itemPrefab, dropPosition, itemPrefab.transform.rotation); instantiateItem.Initialize(element); var pushPower = Vector3.up * 30f + (newDropPosition - dropPosition) * 10f; instantiateItem.AddForce(pushPower, ForceMode.Impulse); } } private bool IsDropPositionAvailable(Vector3 position, List positions) { return positions.Any(pos => Vector3.Distance(position, pos) > _minSeparationDistance); } public T GetDataByIdx(Dictionary dataDictionary, string idx) where T : class { if (dataDictionary.TryGetValue(idx, out var data)) return data; Debug.LogError($"{idx}와 일치하는 아이템이 없습니다."); return null; } public ItemData GetItemDataByIdx(string idx) => GetDataByIdx(_itemDatas, idx); public ItemDropTable GetItemDropTableByIdx(string idx) { if (string.IsNullOrEmpty(idx)) { Debug.Log("ItemDropTable이 비어있습니다."); return null; } if (_itemDropTables.TryGetValue(idx, out var itemDropTable)) return itemDropTable; Debug.LogError($"{idx}와 일치하는 아이템이 없습니다."); return null; } public FoodData GetFoodDataByIdx(string idx) => GetDataByIdx(_foodDatas, idx); public CocktailData GetCocktailDataByIdx(string idx) => GetDataByIdx(_cocktailDatas, idx); public LiquidData GetDrinkDataByIdx(string idx) => GetDataByIdx(_drinkDatas, idx); } }