using System;
using System.Collections.Generic;
using Cielonos.MainGame.Inventory;
using UnityEngine;
namespace Cielonos.MainGame.Items
{
///
/// Resources/Items 下的物品 prefab 目录。它只负责发现、筛选和抽取 ItemBase prefab,
/// 不负责奖励的实例化、归属或 UI 行为。
///
public static class ItemPool
{
private const string ItemsRootPath = "Items";
private static readonly Dictionary PrefabCache = new();
public static List Roll(int count, System.Random rng, ItemFilter filter = null,
params string[] resourceFolders)
{
return Roll(count, rng, filter, null, resourceFolders);
}
///
/// 按权重从候选池中无重复抽取。excluded 用于同一次生成内避免重复物品。
///
public static List Roll(int count, System.Random rng, ItemFilter filter,
ISet excluded, params string[] resourceFolders)
{
if (count <= 0 || rng == null)
{
return new List();
}
List candidates = BuildCandidates(filter, excluded, resourceFolders);
List results = new(Mathf.Min(count, candidates.Count));
while (results.Count < count && candidates.Count > 0)
{
float totalWeight = 0f;
foreach (CandidateEntry candidate in candidates)
{
totalWeight += candidate.weight;
}
if (totalWeight <= 0f)
{
break;
}
float roll = (float)(rng.NextDouble() * totalWeight);
float cumulative = 0f;
int selectedIndex = candidates.Count - 1;
for (int index = 0; index < candidates.Count; index++)
{
cumulative += candidates[index].weight;
if (roll <= cumulative)
{
selectedIndex = index;
break;
}
}
CandidateEntry selected = candidates[selectedIndex];
results.Add(selected.itemPrefab);
candidates.RemoveAt(selectedIndex);
}
return results;
}
public static bool HasCandidate(ItemFilter filter, ISet excluded = null,
params string[] resourceFolders)
{
return BuildCandidates(filter, excluded, resourceFolders).Count > 0;
}
public static void ClearCache()
{
PrefabCache.Clear();
}
private static List BuildCandidates(ItemFilter filter, ISet excluded,
string[] resourceFolders)
{
List candidates = new();
string[] paths = resourceFolders != null && resourceFolders.Length > 0
? resourceFolders
: new[] { string.Empty };
foreach (string resourceFolder in paths)
{
string resourcePath = string.IsNullOrEmpty(resourceFolder)
? ItemsRootPath
: $"{ItemsRootPath}/{resourceFolder}";
foreach (ItemBase itemPrefab in LoadPrefabs(resourcePath))
{
if (itemPrefab == null || itemPrefab.contentData == null)
{
continue;
}
if (excluded != null && excluded.Contains(itemPrefab))
{
continue;
}
ContentData data = itemPrefab.contentData;
if (data.dropWeight <= 0f || (filter != null && !filter.Match(itemPrefab)))
{
continue;
}
candidates.Add(new CandidateEntry(itemPrefab, data.dropWeight));
}
}
return candidates;
}
private static ItemBase[] LoadPrefabs(string resourcePath)
{
if (PrefabCache.TryGetValue(resourcePath, out ItemBase[] cached))
{
return cached;
}
GameObject[] prefabs = Resources.LoadAll(resourcePath);
List items = new(prefabs.Length);
foreach (GameObject prefab in prefabs)
{
ItemBase item = prefab.GetComponent();
if (item != null)
{
items.Add(item);
}
}
cached = items.ToArray();
PrefabCache[resourcePath] = cached;
return cached;
}
private readonly struct CandidateEntry
{
public readonly ItemBase itemPrefab;
public readonly float weight;
public CandidateEntry(ItemBase itemPrefab, float weight)
{
this.itemPrefab = itemPrefab;
this.weight = weight;
}
}
}
}