using System; using System.Collections.Generic; using Cielonos.Core.Interaction; using Cielonos.MainGame.Inventory; using Cielonos.MainGame.Inventory.Collections; using Cielonos.MainGame.Rewards; using Cielonos.MainGame.UI; using Sirenix.OdinInspector; using UnityEngine; namespace Cielonos.MainGame.Interactions { /// /// 战斗完成奖励用的一次性补给包。 /// 候选生成、领取和放弃补偿均由 RewardProfile 驱动。 /// public class SupplyPack : InteractableObjectBase, ICombatRewardInteractable { [TitleGroup("Presentation")] [SerializeField] private GameObject _availableViewPrefab; [SerializeField] private GameObject _notAvailableTablePrefab; [TitleGroup("Reward Profile")] [SerializeField] private RewardProfile _profile; [TitleGroup("Runtime State"), ReadOnly] private List _currentOffers = new(); [ShowInInspector, ReadOnly] private bool _isAvailable; private GameObject _currentView; private Transform _offerStagingRoot; private void Start() { SetAvailable(_isAvailable); } private void OnDestroy() { ClearCurrentOffers(); } protected override void InitializeChoices() { choices.Add(new InteractionChoice("打开补给包", OpenSupplyPack)); } public void SetupCombatReward(CombatRewardInstruction instruction, System.Random rng) { Setup(); } private void Setup() { if (_currentView != null) { Destroy(_currentView); } if (_availableViewPrefab != null) { _currentView = Instantiate(_availableViewPrefab, transform); } ClearCurrentOffers(); _isAvailable = GetProfile() != null; if (!_isAvailable) { Debug.LogError("[SupplyPack] RewardProfile is not assigned."); } RefreshChoiceStates(); SetAvailable(_isAvailable); } private void OpenSupplyPack() { if (!_isAvailable) { Setup(); if (!_isAvailable) { return; } } if (_currentOffers.Count == 0) { GenerateOffers(); } RewardProfile profile = GetProfile(); if (_currentOffers.Count == 0) { Debug.LogWarning($"[SupplyPack] Profile '{profile?.name ?? "None"}' generated no valid offers."); return; } var page = PlayerCanvas.MainGamePages.rewardChoicePage; if (page == null) { Debug.LogError("[SupplyPack] RewardChoiceUIPage is not registered."); return; } page.SetOffers(_currentOffers, HandleOfferSelected, HandleOfferSkipped); page.Open(); } private void GenerateOffers() { ClearCurrentOffers(); RewardProfile profile = GetProfile(); if (profile == null) { return; } System.Random rng = RunManager.Instance?.currentRun?.randomizer?.Next() ?? new System.Random(); _currentOffers = RewardGenerator.GenerateRuntimeOffers(profile, rng, GetOfferStagingRoot(), RewardGenerationContext.FromPlayer()); } private void HandleOfferSelected(int index) { if (index < 0 || index >= _currentOffers.Count) { return; } ItemBase selected = _currentOffers[index]; _currentOffers.RemoveAt(index); int amount = selected is ConsumableBase consumable ? consumable.stackAmount : 1; NotificationData notification = NotificationUIArea.CreateItemObtainedData(selected, amount); MainGameManager.Player.inventorySc.backpackSm.ObtainRewardOffer(selected); PlayerCanvas.NotificationUIArea?.Push(notification); ClearCurrentOffers(); CompleteSupplyPack(); } private void HandleOfferSkipped() { RewardProfile profile = GetProfile(); System.Random rng = RunManager.Instance?.currentRun?.randomizer?.Next() ?? new System.Random(); int compensation = profile?.RollSkipCompensation(rng) ?? 0; if (compensation > 0) { MainGameManager.Player.inventorySc.backpackSm.ObtainItem(compensation); } PlayerCanvas.NotificationUIArea?.PushRareMaterial(compensation, "放弃补给"); ClearCurrentOffers(); CompleteSupplyPack(); } private void CompleteSupplyPack() { _isAvailable = false; RefreshChoiceStates(); if (_currentView != null) { Destroy(_currentView); } if (_notAvailableTablePrefab != null) { _currentView = Instantiate(_notAvailableTablePrefab, transform); } SetExhausted(); if (RunManager.Instance == null || !RunManager.Instance.TryCompletePendingCombatReward()) { RunManager.Instance?.CompleteCurrentNode(); } } private RewardProfile GetProfile() { return _profile; } private void RefreshChoiceStates() { if (choices == null || choices.Count == 0) { return; } choices[0].isInteractable = _isAvailable; } private void SetAvailable(bool available) { _isAvailable = available; RefreshChoiceStates(); SetInteractable(available); if (_currentView != null) { _currentView.SetActive(available); } } private Transform GetOfferStagingRoot() { if (_offerStagingRoot != null) { return _offerStagingRoot; } GameObject root = new("GeneratedOffers"); root.transform.SetParent(transform, false); root.SetActive(false); _offerStagingRoot = root.transform; return _offerStagingRoot; } private void ClearCurrentOffers() { foreach (ItemBase item in _currentOffers) { if (item != null) { Destroy(item.gameObject); } } _currentOffers.Clear(); } } }