238 lines
6.8 KiB
C#
238 lines
6.8 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 战斗完成奖励用的一次性补给包。
|
|
/// 候选生成、领取和放弃补偿均由 RewardProfile 驱动。
|
|
/// </summary>
|
|
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<ItemBase> _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<RareMaterial>(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();
|
|
}
|
|
}
|
|
}
|