2026-05-10 11:47:55 -04:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using Cielonos.Core.Interaction;
|
|
|
|
|
|
using Cielonos.MainGame.Characters;
|
2026-05-23 08:27:50 -04:00
|
|
|
|
using Cielonos.MainGame.Inventory;
|
|
|
|
|
|
using Cielonos.MainGame.Inventory.Collections;
|
2026-05-10 11:47:55 -04:00
|
|
|
|
using Cielonos.MainGame.Items;
|
2026-07-18 03:16:20 -04:00
|
|
|
|
using Cielonos.MainGame.Rewards;
|
2026-05-10 11:47:55 -04:00
|
|
|
|
using Cielonos.MainGame.UI;
|
2026-07-18 03:16:20 -04:00
|
|
|
|
using Sirenix.OdinInspector;
|
2026-05-10 11:47:55 -04:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Cielonos.MainGame.Interactions
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 物流中心(商店)场景交互对象。
|
|
|
|
|
|
/// 玩家靠近按交互键后呼出 LogisticCenterUIPage,显示可购买的随机装备。
|
|
|
|
|
|
/// 商店可反复打开(不像机械台单次使用),商品在首次进入时生成,买完即消失。
|
|
|
|
|
|
/// 货币通过消耗背包中的 RareMaterial 消耗品来支付。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class LogisticsCenter : InteractableObjectBase
|
|
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
|
[TitleGroup("Reward Profile")]
|
|
|
|
|
|
[SerializeField, Required]
|
|
|
|
|
|
private RewardProfile _profile;
|
2026-05-10 11:47:55 -04:00
|
|
|
|
|
|
|
|
|
|
private List<ShopOffer> currentOffers;
|
|
|
|
|
|
private bool isInitialized;
|
|
|
|
|
|
|
|
|
|
|
|
protected override void InitializeChoices()
|
|
|
|
|
|
{
|
|
|
|
|
|
choices.Add(new InteractionChoice("Open Logistic Center", OpenShop));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 由 RunManager 或场景加载逻辑调用,预先生成本次商店的商品列表和价格。
|
|
|
|
|
|
/// 若未调用此方法,交互时会自动从 Resources/Items 中生成。
|
|
|
|
|
|
/// </summary>
|
2026-07-18 03:16:20 -04:00
|
|
|
|
/// <param name="rng">本局 Run 的随机数生成器。</param>
|
2026-05-10 11:47:55 -04:00
|
|
|
|
public void Setup(System.Random rng)
|
|
|
|
|
|
{
|
|
|
|
|
|
currentOffers = RollShopOffers(rng);
|
|
|
|
|
|
isInitialized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OpenShop()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isInitialized)
|
|
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
|
System.Random rng = RunManager.Instance?.currentRun?.randomizer?.Next() ?? new System.Random();
|
2026-05-10 11:47:55 -04:00
|
|
|
|
currentOffers = RollShopOffers(rng);
|
|
|
|
|
|
isInitialized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (currentOffers == null || currentOffers.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[LogisticCenter] 候选池为空,没有可售卖的装备。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var uiPage = PlayerCanvas.MainGamePages.logisticsCenterPage;
|
|
|
|
|
|
uiPage.SetOffers(currentOffers, HandlePurchase);
|
|
|
|
|
|
uiPage.Open();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void HandlePurchase(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (index < 0 || index >= currentOffers.Count) return;
|
|
|
|
|
|
|
|
|
|
|
|
ShopOffer offer = currentOffers[index];
|
|
|
|
|
|
|
|
|
|
|
|
// 获取玩家背包中的 RareMaterial
|
|
|
|
|
|
PlayerInventorySubcontroller inventorySc = MainGameManager.Player.inventorySc;
|
|
|
|
|
|
RareMaterial rareMaterial = MainGameManager.Player.inventorySc.GetRareMaterial();
|
|
|
|
|
|
|
|
|
|
|
|
if (rareMaterial == null || rareMaterial.stackAmount < offer.price)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log("[LogisticCenter] RareMaterial 不足,无法购买。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 扣除 RareMaterial
|
|
|
|
|
|
rareMaterial.Use(offer.price);
|
|
|
|
|
|
int remaining = rareMaterial != null ? rareMaterial.stackAmount : 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 将物品加入背包
|
2026-07-18 03:16:20 -04:00
|
|
|
|
inventorySc.backpackSm.ObtainItemPrefab(offer.item);
|
2026-05-10 11:47:55 -04:00
|
|
|
|
|
|
|
|
|
|
// 从商品列表中移除已购买的商品
|
|
|
|
|
|
currentOffers.RemoveAt(index);
|
|
|
|
|
|
|
2026-07-18 03:16:20 -04:00
|
|
|
|
Debug.Log($"[LogisticCenter] 玩家购买了 '{offer.item.name}',花费 {offer.price} RareMaterial,剩余 {remaining}。");
|
2026-05-10 11:47:55 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-07-18 03:16:20 -04:00
|
|
|
|
/// 从 RewardProfile 生成商店候选并生成包含价格的 ShopOffer。
|
|
|
|
|
|
/// Profile 可明确限制可用主武器,避免废弃武器自动进入商店池。
|
2026-05-10 11:47:55 -04:00
|
|
|
|
/// </summary>
|
2026-07-18 03:16:20 -04:00
|
|
|
|
private List<ShopOffer> RollShopOffers(System.Random rng)
|
2026-05-10 11:47:55 -04:00
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
|
if (_profile == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError("[LogisticsCenter] RewardProfile is not assigned.");
|
|
|
|
|
|
return new List<ShopOffer>();
|
|
|
|
|
|
}
|
2026-05-10 11:47:55 -04:00
|
|
|
|
|
2026-07-18 03:16:20 -04:00
|
|
|
|
List<ItemBase> rolledItems = RewardGenerator.GeneratePrefabOffers(_profile, rng,
|
|
|
|
|
|
RewardGenerationContext.FromPlayer().ExcludeOwnedNonConsumables());
|
2026-05-10 11:47:55 -04:00
|
|
|
|
|
|
|
|
|
|
List<ShopOffer> offers = new List<ShopOffer>(rolledItems.Count);
|
2026-07-18 03:16:20 -04:00
|
|
|
|
foreach (ItemBase item in rolledItems)
|
2026-05-10 11:47:55 -04:00
|
|
|
|
{
|
|
|
|
|
|
if (item == null || item.contentData == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
int price = MainGameManager.Config.RollPrice(item.contentData.itemRarity, rng);
|
2026-07-18 03:16:20 -04:00
|
|
|
|
offers.Add(new ShopOffer(item, price));
|
2026-05-10 11:47:55 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return offers;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 商店中的单个商品条目:包含物品预制体引用和售价。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
|
public class ShopOffer
|
|
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
|
public ItemBase item;
|
2026-05-10 11:47:55 -04:00
|
|
|
|
public int price;
|
|
|
|
|
|
|
2026-07-18 03:16:20 -04:00
|
|
|
|
public ShopOffer(ItemBase item, int price)
|
2026-05-10 11:47:55 -04:00
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
|
this.item = item;
|
2026-05-10 11:47:55 -04:00
|
|
|
|
this.price = price;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|