Aetheria/Assets/Scripts/Gameplay/AetheriaUnityActionBarPresentation.cs
2026-06-29 06:30:24 +01:00

273 lines
9.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using GameCult.Aetheria.State.Verse;
using UnityEngine;
public sealed class AetheriaUnityActionBarPresentation
{
private IReadOnlyList<ActionBarSlot> _slots = Array.Empty<ActionBarSlot>();
private AetheriaRuntimeCatalogSnapshot _catalog;
private GameSettings _settings;
private Func<Entity> _resolveEntity = () => null;
private Func<AetheriaControl> _resolveControl = () => null;
private IReadOnlyList<AetheriaUnityActionBarBinding> _localBindings =
Array.Empty<AetheriaUnityActionBarBinding>();
public void Bind(
IReadOnlyList<ActionBarSlot> slots,
AetheriaRuntimeCatalogSnapshot catalog,
GameSettings settings,
Func<Entity> resolveEntity,
Func<AetheriaControl> resolveControl)
{
_slots = slots ?? Array.Empty<ActionBarSlot>();
_catalog = catalog;
_settings = settings;
_resolveEntity = resolveEntity ?? (() => null);
_resolveControl = resolveControl ?? (() => null);
}
public int SlotCount => _slots?.Count ?? 0;
public string SlotLabel(int slotIndex)
{
var slot = ResolveSlot(slotIndex);
if (slot == null)
return "";
var controlPath = slot.ControlPath ?? "";
if (string.IsNullOrWhiteSpace(controlPath))
return "Action Bar";
var slashIndex = controlPath.LastIndexOf('/');
return slashIndex >= 0 && slashIndex < controlPath.Length - 1
? controlPath.Substring(slashIndex + 1)
: controlPath;
}
public string BindingLabel(int slotIndex)
{
var slot = ResolveSlot(slotIndex);
return slot?.Binding switch
{
ActionBarWeaponGroupBinding weaponGroup => $"G{weaponGroup.Group + 1}",
ActionBarConsumableBinding consumable => consumable.Target?.Name ?? "Consumable",
ActionBarGearBinding gear => _catalog?.FindItem(gear.Item?.EquippableItem, x => x.ItemKey)?.Name ?? "Gear",
_ => "Empty"
};
}
public bool TryResolveControlPath(int slotIndex, out string controlPath)
{
controlPath = ResolveSlot(slotIndex)?.ControlPath ?? "";
return !string.IsNullOrWhiteSpace(controlPath);
}
public void RequestBinding(ActionBarSlot slot, DragObject dragAction)
{
var entity = _resolveEntity();
if (slot == null || dragAction == null || entity == null)
return;
var binding = CreateBindingCommit(slot, entity, dragAction);
if (binding == null ||
string.IsNullOrWhiteSpace(binding.ControlPath) ||
string.IsNullOrWhiteSpace(binding.Kind))
{
return;
}
SetLocalBinding(binding);
ApplyLocalBindings();
}
public bool RequestWeaponGroupBinding(int slotIndex, int groupIndex)
{
if (groupIndex < 0 || !TryResolveControlPath(slotIndex, out var controlPath))
return false;
SetLocalBinding(new AetheriaUnityActionBarBinding
{
ControlPath = controlPath,
Kind = "weapon_group",
ItemKey = "",
EquipmentIndex = -1,
BehaviorIndex = -1,
WeaponGroup = groupIndex
});
ApplyLocalBindings();
return true;
}
public bool ClearBinding(int slotIndex)
{
if (!TryResolveControlPath(slotIndex, out var controlPath))
return false;
_localBindings = (_localBindings ?? Array.Empty<AetheriaUnityActionBarBinding>())
.Where(binding => !string.Equals(binding?.ControlPath ?? "", controlPath, StringComparison.Ordinal))
.ToArray();
ApplyLocalBindings();
return true;
}
public void ApplyLocalBindings()
{
ApplyBindings(_resolveEntity(), _localBindings);
}
public void ApplyBindings(
Entity entity,
IReadOnlyList<AetheriaUnityActionBarBinding> bindings)
{
if (_slots == null || _slots.Count == 0)
return;
foreach (var slot in _slots)
slot.Binding = null;
if (entity == null || bindings == null || bindings.Count == 0)
return;
var slotsByControlPath = _slots
.Where(slot => !string.IsNullOrWhiteSpace(slot?.ControlPath))
.GroupBy(slot => slot.ControlPath, StringComparer.Ordinal)
.ToDictionary(group => group.Key, group => group.First(), StringComparer.Ordinal);
foreach (var binding in bindings)
{
if (binding == null ||
string.IsNullOrWhiteSpace(binding.ControlPath) ||
!slotsByControlPath.TryGetValue(binding.ControlPath, out var slot))
{
continue;
}
var slotBinding = CreateBinding(slot, entity, binding);
if (slotBinding != null)
slot.Binding = slotBinding;
}
}
public ActionBarBinding CreateBinding(ActionBarSlot slot, Entity entity, DragObject dragAction)
{
var binding = CreateBindingCommit(slot, entity, dragAction);
return binding == null ? null : CreateBinding(slot, entity, binding);
}
private AetheriaUnityActionBarBinding CreateBindingCommit(
ActionBarSlot slot,
Entity entity,
DragObject dragAction)
{
if (slot == null || entity == null || dragAction == null)
return null;
switch (dragAction)
{
case EquippedItemDragObject equippedItemDragAction:
var equippedItem = equippedItemDragAction.EquippedItem;
var trigger = equippedItem?.GetBehavior<IActivatedBehavior>();
var equipmentIndex = equippedItem == null ? -1 : entity.Equipment.IndexOf(equippedItem);
var behaviorIndex = equippedItem?.Behaviors == null ? -1 : Array.IndexOf(equippedItem.Behaviors, trigger);
return trigger == null || equipmentIndex < 0 || behaviorIndex < 0
? null
: new AetheriaUnityActionBarBinding
{
ControlPath = slot.ControlPath ?? "",
Kind = "gear",
ItemKey = equippedItem.EquippableItem?.ItemKey ?? "",
EquipmentIndex = equipmentIndex,
BehaviorIndex = behaviorIndex
};
case ItemInstanceDragObject itemInstanceDragAction:
var consumable = FindConsumable(itemInstanceDragAction.Item);
return consumable == null
? null
: new AetheriaUnityActionBarBinding
{
ControlPath = slot.ControlPath ?? "",
Kind = "consumable",
ItemKey = consumable.ItemKey ?? ""
};
default:
return null;
}
}
private void SetLocalBinding(AetheriaUnityActionBarBinding binding)
{
if (binding == null || string.IsNullOrWhiteSpace(binding.ControlPath))
return;
_localBindings = (_localBindings ?? Array.Empty<AetheriaUnityActionBarBinding>())
.Where(existing => !string.Equals(existing?.ControlPath ?? "", binding.ControlPath, StringComparison.Ordinal))
.Concat(new[] { binding })
.ToArray();
}
private ActionBarBinding CreateBinding(
ActionBarSlot slot,
Entity entity,
AetheriaUnityActionBarBinding binding)
{
if (slot == null || entity == null || binding == null)
return null;
switch (binding.Kind)
{
case "consumable":
var consumable = _catalog?.FindItem(binding.ItemKey ?? "");
return consumable != null &&
string.Equals(consumable.Category, AetheriaRuntimeItemCategories.Consumable, StringComparison.Ordinal)
? new ActionBarConsumableBinding(entity, slot, _resolveControl, _settings, consumable)
: null;
case "gear":
if (binding.EquipmentIndex < 0 || binding.EquipmentIndex >= entity.Equipment.Count)
return null;
var equippedItem = entity.Equipment[binding.EquipmentIndex];
var behaviors = equippedItem?.Behaviors;
if (equippedItem == null ||
!string.Equals(equippedItem.EquippableItem?.ItemKey ?? "", binding.ItemKey ?? "", StringComparison.Ordinal) ||
behaviors == null ||
binding.BehaviorIndex < 0 ||
binding.BehaviorIndex >= behaviors.Length)
{
return null;
}
if (!(behaviors[binding.BehaviorIndex] is IActivatedBehavior activatedBehavior))
return null;
return new ActionBarGearBinding(entity, slot, _resolveControl, _settings, equippedItem, activatedBehavior);
case "weapon_group":
return entity.WeaponGroups != null &&
binding.WeaponGroup >= 0 &&
binding.WeaponGroup < entity.WeaponGroups.Length
? new ActionBarWeaponGroupBinding(entity, slot, _resolveControl, _settings, binding.WeaponGroup)
: null;
default:
return null;
}
}
private AetheriaRuntimeCatalogItem FindConsumable(ItemInstance item)
{
var typedItem = _catalog?.FindItem(item, x => x.ItemKey);
return typedItem != null &&
string.Equals(typedItem.Category, AetheriaRuntimeItemCategories.Consumable, StringComparison.Ordinal)
? typedItem
: null;
}
private ActionBarSlot ResolveSlot(int slotIndex)
{
return _slots != null &&
slotIndex >= 0 &&
slotIndex < _slots.Count
? _slots[slotIndex]
: null;
}
}