Files
Cielonos/Assets/Scripts/MainGame/Interactions/RewardTable.cs
SoulliesOfficial 9a9e48f8a5
2026-06-27 12:52:03 -04:00

341 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using Cielonos.Core.Interaction;
using Cielonos.MainGame.Inventory;
using Cielonos.MainGame.Inventory.Collections;
using Cielonos.MainGame.UI;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Cielonos.MainGame.Interactions
{
public enum RewardTier
{
Minor,
Major
}
/// <summary>
/// 奖励台(奖励战斗后出现)。
/// 生成时隐藏,当 CombatRoomSm 通知清空时出现。
/// 交互后打开奖励选择面板,在 3 个候选奖励中选择 1 个。
/// </summary>
public class RewardTable : InteractableObjectBase
{
private const int OFFER_COUNT = 3;
[TitleGroup("RewardTier & Prefabs")]
[SerializeField]
[Tooltip("不同稀有度对应的 table 模型。")]
private Dictionary<RewardTier, GameObject> _tablePrefabs = new Dictionary<RewardTier, GameObject>();
[SerializeField]
[Tooltip("被使用后或不可用时显示的模型。")]
private GameObject _notAvailableTablePrefab;
private GameObject _currentTable;
[TitleGroup("Reward Config")]
public RewardTier tier;
[TitleGroup("Runtime State"), ReadOnly]
private List<int> _selectedTypes = new List<int>();
[TitleGroup("Runtime State"), ReadOnly]
private List<ItemBase> _currentOffers = new List<ItemBase>();
[ShowInInspector, ReadOnly]
private bool _isAvailable;
private void Start()
{
// 初始状态:隐藏模型
if (_currentTable != null)
{
_currentTable.SetActive(false);
}
// 监听战斗房间清空事件以显示模型
CombatManager.CombatRoomSm.OnRoomCleared += OnRoomCleared;
// 如果房间已提前清空(例如非战斗阶段),直接显示模型
if (RunManager.Instance != null && RunManager.Instance.currentPhase == RunPhase.MapSelection)
{
OnRoomCleared();
}
}
private void OnDestroy()
{
CombatManager.CombatRoomSm.OnRoomCleared -= OnRoomCleared;
// 清理内存中未被选中的剩余候选物品
ClearCurrentOffers();
}
private void OnRoomCleared()
{
// 激活并显示台子模型
Setup();
if (_currentTable != null)
{
_currentTable.SetActive(true);
}
Debug.Log($"[RewardTable] 战斗房间已清空,奖励台模型已激活 ({tier})。");
}
protected override void InitializeChoices()
{
choices.Add(new InteractionChoice("查看", OpenTable));
}
private void Setup()
{
if (_currentTable != null) Destroy(_currentTable);
if (_tablePrefabs.TryGetValue(tier, out GameObject tablePrefab) && tablePrefab != null)
{
_currentTable = Instantiate(tablePrefab, transform);
}
DetermineRewardTypes();
_isAvailable = true;
}
private void DetermineRewardTypes()
{
_selectedTypes.Clear();
// 根据游戏运行种子初始化随机数生成器
System.Random rng = RunManager.Instance?.currentRun?.randomizer?.Next() ?? new System.Random();
// 检查玩家背包中当前是否有任何可升级的装备
var backpack = MainGameManager.Player.inventorySc.backpackSm;
bool hasUpgradeable = backpack.mainWeapons.Exists(i => i.IsUpgradable) ||
backpack.supportEquipments.Exists(i => i.IsUpgradable) ||
backpack.passiveEquipments.Exists(i => i.IsUpgradable);
// 可用的奖励类型池
// 0: 增加最大生命值, 1: 增加最大能量值, 2: 升级装备值, 3: 稀有材料
List<int> pool = new List<int> { 0, 1, 3 };
if (hasUpgradeable)
{
pool.Add(2);
}
// 恰好挑选 3 个不重复的奖励选项
while (_selectedTypes.Count < OFFER_COUNT && pool.Count > 0)
{
int index = rng.Next(pool.Count);
_selectedTypes.Add(pool[index]);
pool.RemoveAt(index);
}
}
private void OpenTable()
{
if (!_isAvailable)
{
Debug.Log("[RewardTable] 奖励台已被使用。");
return;
}
// 若尚未生成候选奖励,则进行生成
if (_currentOffers.Count == 0)
{
GenerateOffers();
}
if (_currentOffers == null || _currentOffers.Count == 0)
{
Debug.LogWarning("[RewardTable] 生成候选奖励失败。");
return;
}
// 打开奖励选择 UI 页面
var uiPage = PlayerCanvas.MainGamePages.rewardChoicePage;
if (uiPage != null)
{
uiPage.SetOffers(_currentOffers, HandleRewardSelected);
uiPage.Open();
}
else
{
Debug.LogError("[RewardTable] PlayerCanvas.MainGamePages.rewardChoicePage 未注册!");
}
}
private void GenerateOffers()
{
ClearCurrentOffers();
// 仅在首次打开奖励台的瞬间,动态拉取玩家背包中当前拥有的所有可升级装备
var backpack = MainGameManager.Player.inventorySc.backpackSm;
List<ItemBase> upgradeableItems = new List<ItemBase>();
foreach (var item in backpack.mainWeapons)
{
if (item.IsUpgradable) upgradeableItems.Add(item);
}
foreach (var item in backpack.supportEquipments)
{
if (item.IsUpgradable) upgradeableItems.Add(item);
}
foreach (var item in backpack.passiveEquipments)
{
if (item.IsUpgradable) upgradeableItems.Add(item);
}
// 根据之前随机选定的类型,实例化奖励选项的实例物体
foreach (int typeIndex in _selectedTypes)
{
ItemBase itemInstance = null;
if (typeIndex == 0) // 增加最大生命值
{
itemInstance = CreateItemInstance<PermanentCoating>("PermanentCoating");
if (itemInstance is PermanentCoating coating)
{
coating.stackAmount = (tier == RewardTier.Major) ? 2 : 1;
}
}
else if (typeIndex == 1) // 增加最大能量值
{
itemInstance = CreateItemInstance<EnergyReservoir>("EnergyReservoir");
if (itemInstance is EnergyReservoir reservoir)
{
reservoir.stackAmount = (tier == RewardTier.Major) ? 2 : 1;
}
}
else if (typeIndex == 2) // 装备升级组件
{
itemInstance = CreateItemInstance<PrefabricatedComponent>("PrefabricatedComponent");
if (itemInstance is PrefabricatedComponent prefabricatedComponent)
{
// 预制组件的数量(升级的次数)也与奖励档次挂钩:普通奖为 1 次Major 奖为 2 次
prefabricatedComponent.stackAmount = (tier == RewardTier.Major) ? 2 : 1;
if (upgradeableItems.Count > 0)
{
// 此处使用 Unity 的 Random 随机抽取目标装备,避免在 UI 交互时推进游戏主运行种子的状态
int randIdx = UnityEngine.Random.Range(0, upgradeableItems.Count);
prefabricatedComponent.targetEquipment = upgradeableItems[randIdx];
}
}
}
else if (typeIndex == 3) // 稀有材料
{
itemInstance = CreateItemInstance<RareMaterial>("RareMaterial");
if (itemInstance is RareMaterial rareMaterial)
{
// 此处同样使用 Unity 的 Random避免推进主运行种子并根据奖励档次生成对应区间的数量
int amount = (tier == RewardTier.Major) ? UnityEngine.Random.Range(120, 181) : UnityEngine.Random.Range(40, 61);
rareMaterial.stackAmount = amount;
}
}
if (itemInstance != null)
{
_currentOffers.Add(itemInstance);
}
}
}
private ItemBase CreateItemInstance<T>(string prefabName) where T : ItemBase
{
string resourcePath = $"Items/Consumable/{prefabName}";
GameObject prefab = Resources.Load<GameObject>(resourcePath);
if (prefab == null)
{
Debug.LogError($"[RewardTable] Failed to load prefab from Resources/{resourcePath}");
return null;
}
GameObject inst = Instantiate(prefab, transform);
inst.name = prefabName;
T component = inst.GetComponent<T>();
if (component == null)
{
Debug.LogError($"[RewardTable] Prefab {prefabName} is missing script {typeof(T).Name}");
Destroy(inst);
return null;
}
return component;
}
private void HandleRewardSelected(int index)
{
if (index < 0 || index >= _currentOffers.Count) return;
ItemBase selectedItem = _currentOffers[index];
var backpack = MainGameManager.Player.inventorySc.backpackSm;
// 判断该物品是否为可堆叠的消耗品
ConsumableBase selectedConsumable = selectedItem as ConsumableBase;
bool wasStackedIntoExisting = false;
if (selectedConsumable != null)
{
// 如果背包中已经存在该消耗品,那么在获取时它会合并堆叠,
// 此时当前的实例将不需要以单独元素形式加入背包列表。
ConsumableBase existingItem = backpack.consumables.Find(c => c.GetType() == selectedConsumable.GetType());
if (existingItem != null)
{
wasStackedIntoExisting = true;
}
}
// 将选中的物品加入到玩家背包
backpack.ObtainItem(selectedItem);
// 处理临时生成的物品实例的生命周期清理(如果它没有在自身的 OnObtained 逻辑中自毁)
if (selectedItem != null)
{
if (wasStackedIntoExisting && selectedConsumable != null)
{
// 如果已堆叠合并,销毁临时游戏物体
Destroy(selectedConsumable.gameObject);
}
else if (selectedConsumable != null && backpack.consumables.Contains(selectedConsumable))
{
// 如果是作为新的一项添加到背包,将其移动到背包容器节点下并归零坐标
selectedConsumable.transform.SetParent(MainGameManager.Player.inventorySc.consumableContainer);
selectedConsumable.transform.localPosition = Vector3.zero;
}
}
// 销毁其余未被选中的候选物品实例
for (int i = 0; i < _currentOffers.Count; i++)
{
if (i != index && _currentOffers[i] != null)
{
Destroy(_currentOffers[i].gameObject);
}
}
_currentOffers.Clear();
// 将奖励台标记为已使用,并替换模型为不可用模型
_isAvailable = false;
if (_currentTable != null) Destroy(_currentTable);
if (_notAvailableTablePrefab != null)
{
_currentTable = Instantiate(_notAvailableTablePrefab, transform);
}
SetExhausted();
Debug.Log($"[RewardTable] 已选择索引为 {index} 的奖励,奖励台交互关闭并进入耗尽状态。");
}
private void ClearCurrentOffers()
{
foreach (var item in _currentOffers)
{
if (item != null)
{
Destroy(item.gameObject);
}
}
_currentOffers.Clear();
}
}
}