Files
Net.Like.Xue.Tokyo/Packages-Local/Com.Project.B.Unity/UX/UXInventory.cs

359 lines
12 KiB
C#
Raw Normal View History

2025-06-24 23:49:13 +08:00
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using BITKit;
using BITKit.Entities;
using BITKit.Mod;
using BITKit.UX;
using Cysharp.Threading.Tasks;
using Net.BITKit.Localization;
using Net.Project.B.Inventory;
using Net.Project.B.Item;
using Net.Project.B.UX;
using Project.B.Entities;
using Project.B.Item;
using Project.B.Player;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Composites;
using UnityEngine.UIElements;
namespace Project.B.UX
{
public static class UXInventoryUtils
{
[RuntimeInitializeOnLoadMethod]
private static void Reload()
{
_scriptableItems.Clear();
}
private static readonly Dictionary<int, ScriptableItem> _scriptableItems = new();
public static async UniTask<ScriptableItem> GetScriptableItem(int scriptableId)
{
if (_scriptableItems.Count is 0)
{
BITApp.ServiceProvider.QueryComponents(out IEntitiesService entitiesService);
foreach (var scriptableItem in entitiesService.QueryComponents<ScriptableItem>().ToArray())
{
_scriptableItems[scriptableItem.Id] = scriptableItem;
}
}
if (_scriptableItems.TryGetValue(scriptableId, out var item))
{
return item;
}
return
await ModService.LoadAsset<ScriptableItem>(
DictionaryReferenceScriptableObject.Dictionary[scriptableId]
);
}
public static async UniTask<ScriptableItem> Setup(VisualElement visualElement, IRuntimeItem item)
{
if(visualElement is null)return null;
var scriptableItem = item is null ? null : await GetScriptableItem(item.ScriptableId);
if (visualElement.Get<Button>() is { } button)
{
button.tooltip = scriptableItem?.Description;
}
foreach (var itemQuality in Enum.GetValues(typeof(ItemQuality)).OfType<ItemQuality>())
{
visualElement.RemoveFromClassList($"v{(int)itemQuality}");
}
if (scriptableItem)
{
visualElement.AddToClassList($"v{(int)scriptableItem.Quality}");
}
{
if (visualElement.Get<Label>() is { } label)
{
var name = scriptableItem?.Name;
BITApp.ServiceProvider.QueryComponents(out ILocalizationService localizationService);
label.text = localizationService.GetLocalizedString(name);
}
}
{
if (visualElement.Get<Label>(1) is { } label)
{
label.text = item?.Amount > 0 ? item.Amount.ToString() : string.Empty;
}
}
if (visualElement.Get<VisualElement>() is { } image)
{
image.style.backgroundImage =scriptableItem ? new StyleBackground(scriptableItem.Icon):null;
}
if (scriptableItem)
{
if (visualElement.layout.width > visualElement.layout.height)
{
if (scriptableItem?.RectangleIcon)
{
visualElement.Get<VisualElement>().style.backgroundImage =
new StyleBackground(scriptableItem.RectangleIcon);
}
}
}
return scriptableItem;
}
}
public class UXInventory<TSource> : UIToolKitPanel,IUXInventory where TSource:IUXPanel
{
protected override string DocumentPath => "ux_inventory";
public override bool AllowCursor => true;
public override bool AllowInput => true;
private readonly IUXKeyMap<InputAction> _keyMap;
private bool _isInitialized;
private readonly IManagedItemService _itemService;
private readonly IPlayerFactory _playerFactory;
private readonly IUXItemInspector _inspector;
private readonly ConcurrentDictionary<int, VisualElement> _itemContainers = new();
private readonly HashSet<int> _inspected = new();
public UXInventory(IUXService uxService, IUXKeyMap<InputAction> keyMap, IPlayerFactory playerFactory, IManagedItemService itemService, IUXItemInspector inspector) : base(uxService)
{
_keyMap = keyMap;
_playerFactory = playerFactory;
_itemService = itemService;
_inspector = inspector;
_playerFactory.OnEntityCreated+=OnEntityCreated;
OnInitiatedAsync += InitiatedAsync;
}
private async UniTask InitiatedAsync()
{
_inventoryContainer.Clear();
_itemTemplate = await ModService.LoadAsset<VisualTreeAsset>("ux_item_template");
for (var i = 0; i < 32; i++)
{
var visualElement = RootVisualElement.Q("PlayerSlot-"+i);
if(visualElement is null)continue;
UXInventoryUtils.Setup(visualElement,null).Forget();
visualElement.Q<Button>().clicked += () =>
{
if (visualElement.userData is not IRuntimeItem item) return;
_weaponInventory.Draw(item.Id);
};
visualElement.RegisterCallback<MouseDownEvent>(evt =>
{
if (visualElement.userData is not IRuntimeItem item)return;
switch (Keyboard.current.leftCtrlKey.isPressed, evt.button)
{
case (false, not 1): return;
case (true, 1):
{
_weaponInventory.Drop(item.Id);
}
break;
default:
{
ContextMenuBuilder
.Create()
.BuildText("操作")
.BuildAction("使用", () => _weaponInventory.Draw(item.Id))
.BuildAction("检视", () => _inspector.Inspect(item))
.BuildAction("丢下", () => _weaponInventory.Drop(item.Id))
.Build();
}
break;
}
});
}
if (_returnButton is not null)
{
_returnButton.clicked += OnReturn;
}
}
[UXBindPath("inventory-container")]
private VisualElement _inventoryContainer;
[UXBindPath("cell-container",true)]
private VisualElement _cellContainer;
[UXBindPath("return-button", true)]
private Button _returnButton;
private VisualTreeAsset _itemTemplate;
[Inject]
private IPlayerInventory _playerInventory;
[Inject]
private IPlayerWeaponInventory _weaponInventory;
[Inject] private IPlayerEquipmentInventory _equipmentInventory;
protected override void OnReturn() => UXService.Entry<IUXHud>();
private UniTask OnEntityCreated(string arg1, IEntity arg2)
{
UXUtils.Inject(this);
arg2.Inject(this);
_playerInventory.Container.OnAdd+=OnAdd;
_playerInventory.Container.OnRemove += OnRemove;
_playerInventory.OnConsumedItem += OnRemove;
_playerInventory.Container.OnSet += OnSet;
_playerInventory.Container.OnDrop += OnRemove;
_equipmentInventory.OnItemAdded += OnEquipmentAdd;
_equipmentInventory.OnItemUpdated += OnEquipmentAdd;
_equipmentInventory.OnItemRemoved += OnEquipmentRemove;
_equipmentInventory.OnItemConsumed += OnEquipmentRemove;
return UniTask.CompletedTask;
}
private void OnEquipmentRemove(int arg1, IRuntimeItem arg2)
{
var visualElement = RootVisualElement.Q("PlayerSlot-"+arg1);
if(visualElement is null)return;
visualElement.userData = null;
UXInventoryUtils.Setup(visualElement, null).Forget();
visualElement.Q<Button>().clickable = null;
}
private void OnEquipmentAdd(int arg1, IRuntimeItem arg2)
{
var visualElement = RootVisualElement.Q("PlayerSlot-"+arg1);
if (visualElement is null) return;
UXInventoryUtils. Setup(visualElement, arg2).Forget();
visualElement.userData = arg2;
}
private void OnSet(IRuntimeItem obj)
{
if (_itemContainers.TryGetValue(obj.Id, out var container))
{
container.Get<Label>(1).text = obj.Amount.ToString();
}
}
private void OnRemove(IRuntimeItem obj)
{
//Debug.Log($"丢下物品{obj.Id},玩家还有:{_playerInventory.Container.GetItems().Length}个物品");
if (_itemContainers.TryRemove(obj.Id, out var container))
{
container.RemoveFromHierarchy();
}
else
{
//Debug.LogWarning($"未找到:{obj.Id},当前所有的ID为:{string.Join("\n",_itemContainers.Keys)}");
}
}
private async void OnAdd(IRuntimeItem obj)
{
var container = _inventoryContainer.Create(_itemTemplate);
container.Q<Button>().viewDataKey = obj.Id.ToString();
container.Q<Button>().tooltip = obj.Id.ToString();
_itemContainers.TryAdd(obj.Id, container);
var scriptableItem =await UXInventoryUtils.GetScriptableItem(obj.ScriptableId);
UXInventoryUtils.Setup(container, obj).Forget();
if (scriptableItem.ControllerClass is not null)
{
container.Q<Button>().clicked += () =>
{
_playerInventory.UseItem(obj.Id);
UXService.Entry<TSource>();
};
}
container.Q<Button>().RegisterCallback<MouseDownEvent>(x =>
{
switch (Keyboard.current.leftCtrlKey.isPressed,x.button)
{
case (false,not 1): return;
case (true, 1):
{
_playerInventory.Container.Drop(obj.Id);
}
break;
case (false, 1):
{
ContextMenuBuilder
.Create()
.BuildText("操作")
.BuildAction("检视", () =>_inspector.Inspect(obj))
.BuildAction("丢下", () =>_playerInventory.Container.Drop(obj.Id))
.Build();
}
break;
}
});
if (scriptableItem.Quality is not ItemQuality.Common && scriptableItem.Quality == AutoInspect && _inspected.Add(obj.ScriptableId))
{
_inspector.Inspect(obj);
}
}
protected override void OnPanelEntry()
{
base.OnPanelEntry();
InputActionGroup.RegisterCallback(_keyMap.InventoryKey, OnReturn);
InputActionGroup.RegisterCallback(_keyMap.CancelKey, OnReturn);
if (_cellContainer is not null)
{
// _cellContainer.style.width = _playerInventory.Size * 100;
}
}
private void OnReturn(InputAction.CallbackContext obj)
{
if(obj.performed is false)return;
UXService.Entry<TSource>();
}
protected override void OnPanelExit()
{
base.OnPanelExit();
InputActionGroup.UnRegisterCallback(_keyMap.InventoryKey, OnReturn);
InputActionGroup.UnRegisterCallback(_keyMap.CancelKey, OnReturn);
}
public ItemQuality AutoInspect { get; set; }
}
}