补全部分 EventComponent 设计

This commit is contained in:
2026-03-16 11:53:24 +08:00
parent c76fd85a2c
commit dc2aa59d58
26 changed files with 2028 additions and 59 deletions
@@ -4,5 +4,7 @@ namespace GeometryTD.UI
{
public int OptionIndex;
public string OptionText;
public bool IsSelectable;
public string BlockedReason;
}
}
@@ -4,6 +4,7 @@ namespace GeometryTD.UI
{
public class RepoFormContext : UIContext
{
public string GoldText;
public CombineAreaContext CombineAreaContext;
public CompAreaContext CompAreaContext;
public ParticipantAreaContext ParticipantAreaContext;
@@ -140,6 +140,7 @@ namespace GeometryTD.UI
return new RepoFormContext
{
GoldText = $"金币: {rawData.Inventory.Gold}",
CombineAreaContext = new CombineAreaContext(),
CompAreaContext = new CompAreaContext
{
@@ -322,6 +323,7 @@ namespace GeometryTD.UI
return;
}
Form.RefreshGoldText(latestContext.GoldText);
Form.RefreshParticipantArea(latestContext.ParticipantAreaContext);
ApplyParticipantSelection();
}
@@ -73,7 +73,7 @@ namespace GeometryTD.UI
private static EventFormContext BuildContext(EventFormRawData rawData)
{
if (rawData?.EventItem == null)
if (rawData == null)
{
return null;
}
@@ -82,24 +82,30 @@ namespace GeometryTD.UI
for (int i = 0; i < options.Length; i++)
{
string optionText = string.Empty;
if (rawData.EventItem.Options != null && i < rawData.EventItem.Options.Length &&
rawData.EventItem.Options[i] != null)
bool isSelectable = false;
string blockedReason = string.Empty;
if (rawData.OptionItems != null && i < rawData.OptionItems.Length &&
rawData.OptionItems[i] != null)
{
optionText = rawData.EventItem.Options[i].OptionText;
optionText = rawData.OptionItems[i].OptionText;
isSelectable = rawData.OptionItems[i].IsSelectable;
blockedReason = rawData.OptionItems[i].BlockedReason;
}
options[i] = new EventOptionItemContext
{
OptionIndex = i,
OptionText = optionText
OptionText = optionText,
IsSelectable = isSelectable,
BlockedReason = blockedReason
};
}
return new EventFormContext
{
EventId = rawData.EventItem.Id,
Title = rawData.EventItem.Title,
Description = rawData.EventItem.Description,
EventId = rawData.EventId,
Title = rawData.Title,
Description = rawData.Description,
OptionItems = options
};
}
@@ -117,7 +123,7 @@ namespace GeometryTD.UI
return;
}
m_UseCase.SelectOption(args.SelectedItemId);
m_UseCase.TrySelectOption(args.SelectedItemId);
}
}
}
}
@@ -245,8 +245,8 @@ namespace GeometryTD.UI
RepoFormContext latestContext = BuildContext(latestRawData);
if (latestContext != null)
{
Form.RefreshUI(latestContext);
ApplyParticipantSelection();
SetContext(latestContext);
RefreshCurrentUI();
}
}
@@ -1,9 +1,18 @@
using GeometryTD.Definition;
namespace GeometryTD.UI
{
public class EventFormRawData
{
public EventItem EventItem;
public int EventId;
public string Title;
public string Description;
public EventOptionRawData[] OptionItems;
}
public sealed class EventOptionRawData
{
public int OptionIndex;
public string OptionText;
public bool IsSelectable;
public string BlockedReason;
}
}
@@ -1,6 +1,5 @@
using GeometryTD.CustomComponent;
using GeometryTD.CustomEvent;
using GeometryTD.Definition;
using GeometryTD.Procedure;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
@@ -8,10 +7,21 @@ namespace GeometryTD.UI
public class EventFormUseCase : IUIUseCase
{
private EventItem _currentEvent;
private RunNodeExecutionContext _currentContext;
private readonly EventOptionExecutor _executor = new EventOptionExecutor();
public void SetCurrentEvent(EventItem eventItem)
public void BindEvent(
EventItem eventItem,
RunNodeExecutionContext context)
{
_currentEvent = eventItem;
_currentContext = context != null ? context.Clone() : null;
}
public void Clear()
{
_currentEvent = null;
_currentContext = null;
}
public EventFormRawData CreateInitialModel()
@@ -23,26 +33,74 @@ namespace GeometryTD.UI
return new EventFormRawData
{
EventItem = _currentEvent
EventId = _currentEvent.Id,
Title = _currentEvent.Title,
Description = _currentEvent.Description,
OptionItems = BuildOptionItems()
};
}
public EventOption SelectOption(int optionIndex)
public bool TrySelectOption(int optionIndex)
{
if (_currentEvent == null || _currentEvent.Options == null)
{
return null;
return false;
}
if (optionIndex < 0 || optionIndex >= _currentEvent.Options.Length)
{
Log.Warning("EventFormUseCase.SelectOption() option index is invalid: {0}", optionIndex);
return null;
Log.Warning("EventFormUseCase.TrySelectOption() option index is invalid: {0}", optionIndex);
return false;
}
BackpackInventoryData workingInventory = GameEntry.PlayerInventory != null
? GameEntry.PlayerInventory.GetInventorySnapshot()
: new BackpackInventoryData();
EventOptionExecutionResult executionResult = _executor.Execute(
_currentEvent,
optionIndex,
_currentContext,
workingInventory);
if (!executionResult.IsAccepted)
{
Log.Warning(
"EventFormUseCase.TrySelectOption() rejected. EventId={0}, OptionIndex={1}, Reason={2}",
_currentEvent.Id,
optionIndex,
executionResult.FailureReason);
return false;
}
if (GameEntry.PlayerInventory != null)
{
GameEntry.PlayerInventory.ReplaceInventorySnapshot(workingInventory);
}
// TODO: 执行 Requirements 校验、CostEffects/RewardEffects 结算与后续节点推进。
GameEntry.EventNode.EndEvent();
return _currentEvent.Options[optionIndex];
return true;
}
private EventOptionRawData[] BuildOptionItems()
{
EventOption[] options = _currentEvent.Options ?? System.Array.Empty<EventOption>();
EventOptionRawData[] result = new EventOptionRawData[options.Length];
BackpackInventoryData inventory = GameEntry.PlayerInventory != null
? GameEntry.PlayerInventory.GetInventorySnapshot()
: new BackpackInventoryData();
for (int i = 0; i < options.Length; i++)
{
EventOption option = options[i];
EventOptionAvailability availability = _executor.EvaluateOption(option, inventory);
result[i] = new EventOptionRawData
{
OptionIndex = i,
OptionText = option?.OptionText ?? string.Empty,
IsSelectable = availability.IsSelectable,
BlockedReason = availability.BlockedReason
};
}
return result;
}
}
}
}
@@ -7,8 +7,18 @@ namespace GeometryTD.UI
public class EventOptionItem : MonoBehaviour
{
[SerializeField] private TMP_Text _optionText;
private static readonly Color SelectableTextColor = Color.white;
private static readonly Color BlockedTextColor = new Color(0.75f, 0.75f, 0.75f, 1f);
private int _optionIndex;
private bool _isSelectable;
private CommonButton _commonButton;
private void Awake()
{
_commonButton = GetComponent<CommonButton>();
}
public void OnInit(EventOptionItemContext context)
{
@@ -20,9 +30,16 @@ namespace GeometryTD.UI
}
_optionIndex = context.OptionIndex;
_isSelectable = context.IsSelectable;
if (_optionText != null)
{
_optionText.text = context.OptionText ?? string.Empty;
_optionText.text = BuildDisplayText(context);
_optionText.color = _isSelectable ? SelectableTextColor : BlockedTextColor;
}
if (_commonButton != null)
{
_commonButton.Interactive = _isSelectable;
}
gameObject.SetActive(!string.IsNullOrEmpty(context.OptionText));
@@ -31,20 +48,38 @@ namespace GeometryTD.UI
public void OnReset()
{
_optionIndex = -1;
_isSelectable = false;
if (_optionText != null)
{
_optionText.text = string.Empty;
_optionText.color = SelectableTextColor;
}
if (_commonButton != null)
{
_commonButton.Interactive = true;
}
}
public void OnClick()
{
if (_optionIndex < 0)
if (_optionIndex < 0 || !_isSelectable)
{
return;
}
GameEntry.Event.Fire(this, EventOptionItemSelectedEventArgs.Create(_optionIndex));
}
private static string BuildDisplayText(EventOptionItemContext context)
{
string optionText = context.OptionText ?? string.Empty;
if (context.IsSelectable || string.IsNullOrEmpty(context.BlockedReason))
{
return optionText;
}
return $"{optionText}\n<size=75%>{context.BlockedReason}</size>";
}
}
}
@@ -1,4 +1,5 @@
using GeometryTD.CustomEvent;
using TMPro;
using UnityEngine;
using UnityGameFramework.Runtime;
@@ -12,6 +13,8 @@ namespace GeometryTD.UI
[SerializeField] private ParticipantArea _participantArea;
[SerializeField] private TMP_Text _goldText;
public void RefreshUI(RepoFormContext context)
{
if (context == null)
@@ -19,11 +22,21 @@ namespace GeometryTD.UI
return;
}
RefreshGoldText(context.GoldText);
_combineArea?.OnInit(context.CombineAreaContext);
_compArea?.OnInit(context.CompAreaContext);
_participantArea?.OnInit(context.ParticipantAreaContext);
}
public void RefreshGoldText(string goldText)
{
if (_goldText != null)
{
_goldText.text = goldText ?? string.Empty;
}
}
public bool TryAssignItemToCombineArea(RepoItemContext itemContext)
{
if (_combineArea == null)
@@ -69,6 +82,11 @@ namespace GeometryTD.UI
protected override void OnClose(bool isShutdown, object userData)
{
if (_goldText != null)
{
_goldText.text = string.Empty;
}
_combineArea?.OnReset();
_compArea?.OnReset();
_participantArea?.OnReset();