359 lines
12 KiB
C#
359 lines
12 KiB
C#
![]() |
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; }
|
||
|
}
|
||
|
|
||
|
}
|