using System.Collections.Generic; using GeometryTD.CustomEvent; using GeometryTD.CustomUtility; using GeometryTD.Definition; using GameFramework.Event; using GeometryTD.CustomComponent; using UnityEngine; using UnityGameFramework.Runtime; namespace GeometryTD.UI { public partial class RepoFormController : UIFormControllerCommonBase { private const int MaxParticipantCount = 4; private RepoFormUseCase _useCase; private readonly Dictionary _itemClickActionMap = new Dictionary(); private readonly Dictionary _itemDescSeedMap = new Dictionary(); private readonly HashSet _participantTowerIds = new HashSet(); private readonly HashSet _compAreaTowerIds = new HashSet(); private sealed class ItemDescSeed { public string Title; public string TypeText; public string Description; public TagType[] Tags; public TagRuntimeData[] TagRuntimes; } protected override UIFormType UIFormTypeId => UIFormType.RepoForm; protected override void RefreshUI(RepoForm form, RepoFormContext context) { form.RefreshUI(context); ApplyParticipantSelection(); } protected override void SubscribeCustomEvents() { GameEntry.Event.Subscribe(RepoItemClickedEventArgs.EventId, OnRepoItemClicked); GameEntry.Event.Subscribe(RepoItemDragEndedEventArgs.EventId, OnRepoItemDragEnded); GameEntry.Event.Subscribe(CombineSlotClickedEventArgs.EventId, OnCombineSlotClicked); GameEntry.Event.Subscribe(RepoCombineRequestedEventArgs.EventId, OnRepoCombineRequested); GameEntry.Event.Subscribe(RepoParticipantAssignRequestedEventArgs.EventId, OnRepoParticipantAssignRequested); GameEntry.Event.Subscribe(RepoFormReturnEventArgs.EventId, OnRepoFormReturn); GameEntry.Event.Subscribe(RepoSellModeToggleRequestedEventArgs.EventId, OnRepoSellModeToggleRequested); GameEntry.Event.Subscribe(RepoSellCancelRequestedEventArgs.EventId, OnRepoSellCancelRequested); GameEntry.Event.Subscribe(RepoSellConfirmRequestedEventArgs.EventId, OnRepoSellConfirmRequested); } protected override void UnsubscribeCustomEvents() { GameEntry.Event.Unsubscribe(RepoItemClickedEventArgs.EventId, OnRepoItemClicked); GameEntry.Event.Unsubscribe(RepoItemDragEndedEventArgs.EventId, OnRepoItemDragEnded); GameEntry.Event.Unsubscribe(CombineSlotClickedEventArgs.EventId, OnCombineSlotClicked); GameEntry.Event.Unsubscribe(RepoCombineRequestedEventArgs.EventId, OnRepoCombineRequested); GameEntry.Event.Unsubscribe(RepoParticipantAssignRequestedEventArgs.EventId, OnRepoParticipantAssignRequested); GameEntry.Event.Unsubscribe(RepoFormReturnEventArgs.EventId, OnRepoFormReturn); GameEntry.Event.Unsubscribe(RepoSellModeToggleRequestedEventArgs.EventId, OnRepoSellModeToggleRequested); GameEntry.Event.Unsubscribe(RepoSellCancelRequestedEventArgs.EventId, OnRepoSellCancelRequested); GameEntry.Event.Unsubscribe(RepoSellConfirmRequestedEventArgs.EventId, OnRepoSellConfirmRequested); } public override int? OpenUI(object userData = null) { if (userData is RepoFormContext repoFormContext) { return OpenUIInternal(repoFormContext); } if (userData is RepoFormRawData rawDataFromUserData) { return OpenUI(rawDataFromUserData); } if (userData != null) { Log.Warning("RepoFormController.OpenUI() userData type is invalid."); return null; } if (_useCase == null) { Log.Error("RepoFormController.OpenUI() useCase is null."); return null; } RepoFormRawData rawData = _useCase.CreateInitialModel(); return OpenUI(rawData); } public int? OpenUI(RepoFormRawData rawData) { RepoFormContext context = BuildContext(rawData); return OpenUIInternal(context); } public override void BindUseCase(IUIUseCase useCase) { if (!(useCase is RepoFormUseCase repoFormUseCase)) { Log.Error("RepoFormController.BindUseCase() useCase is invalid."); return; } _useCase = repoFormUseCase; } #region Event Handlers private void OnRepoItemClicked(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender)) { return; } if (!(e is RepoItemClickedEventArgs args)) { return; } RepoItemClickActionType clickActionType = RepoItemClickActionType.OpenDetail; if (_itemClickActionMap.TryGetValue(args.ItemId, out RepoItemClickActionType mappedAction)) { clickActionType = mappedAction; } if (args.ItemId <= 0) { return; } if (_useCase != null && _useCase.State == RepoFormState.Sell) { if (_useCase.TryToggleSellSelection( args.ItemId, out RepoFormRawData sellModeRawData, out PlayerInventorySaleFailureReason failureReason)) { SetContext(BuildContext(sellModeRawData)); RefreshCurrentUI(); } else if (failureReason == PlayerInventorySaleFailureReason.ParticipantTower) { OpenSellBlockedDialog("参战防御塔不能直接出售,请先移出参战区。"); } return; } if (clickActionType == RepoItemClickActionType.RemoveParticipant) { if (_useCase == null || Form == null) { return; } if (!_useCase.TryRemoveParticipantTower(args.ItemId)) { return; } RefreshParticipantAreaOnly(); return; } if (!_itemDescSeedMap.TryGetValue(args.ItemId, out ItemDescSeed seed)) { return; } GameEntry.UIRouter.OpenUI(UIFormType.ItemDescForm, new ItemDescFormRawData { Title = seed.Title, TypeText = seed.TypeText, Description = seed.Description ?? string.Empty, Price = 0, ScreenPosition = args.ScreenPosition, Tags = seed.Tags, TagRuntimes = seed.TagRuntimes }); } private void OnRepoItemDragEnded(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender)) { return; } if (Context != null && Context.State == RepoFormState.Sell) { return; } if (!(e is RepoItemDragEndedEventArgs args)) { return; } if (Form == null || !_itemClickActionMap.ContainsKey(args.ItemId)) { return; } bool isSelected = _compAreaTowerIds.Contains(args.ItemId) ? _participantTowerIds.Contains(args.ItemId) : args.Assigned; Form.SetRepoItemSelected(args.ItemId, isSelected); } private void OnCombineSlotClicked(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender)) { return; } if (Context != null && Context.State == RepoFormState.Sell) { return; } if (!(e is CombineSlotClickedEventArgs args)) { return; } if (Form == null) { return; } if (Form.TryClearCombineSlot(args.SlotIndex, out long removedItemId) && removedItemId > 0) { Form.SetRepoItemSelected(removedItemId, false); } } private void OnRepoFormReturn(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender) || !(e is RepoFormReturnEventArgs)) { return; } if (Form == null) { return; } CloseUI(); } private void OnRepoCombineRequested(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender)) { return; } if (!(e is RepoCombineRequestedEventArgs args)) { return; } if (_useCase == null || Form == null) { return; } if (!_useCase.TryAssembleTower(args.MuzzleItemId, args.BearingItemId, args.BaseItemId)) { return; } RepoFormRawData latestRawData = _useCase.CreateInitialModel(); RepoFormContext latestContext = BuildContext(latestRawData); if (latestContext != null) { SetContext(latestContext); RefreshCurrentUI(); } } private void OnRepoParticipantAssignRequested(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender)) { return; } if (Context != null && Context.State == RepoFormState.Sell) { return; } if (!(e is RepoParticipantAssignRequestedEventArgs args)) { return; } if (_useCase == null || Form == null || args.TowerItemId <= 0) { return; } ParticipantTowerAssignResult result = _useCase.TryAddParticipantTower(args.TowerItemId); if (result == null || !result.IsSuccess) { if (result != null) { Log.Warning( "RepoFormController denied participant assignment. TowerId={0}, FailureReason={1}, ValidationFailureReason={2}.", result.TowerInstanceId, result.FailureReason, result.ValidationFailureReason); GameEntry.UIRouter.OpenUI( UIFormType.DialogForm, RepoParticipantAssignDialogUtility.BuildDialog(result, MaxParticipantCount)); } return; } RefreshParticipantAreaOnly(); } private void OnRepoSellModeToggleRequested(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender) || !(e is RepoSellModeToggleRequestedEventArgs) || _useCase == null) { return; } if (_useCase.State != RepoFormState.Assemble) { return; } RepoFormRawData rawData = _useCase.EnterSellState(); SetContext(BuildContext(rawData)); RefreshCurrentUI(); } private void OnRepoSellCancelRequested(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender) || !(e is RepoSellCancelRequestedEventArgs) || _useCase == null) { return; } if (_useCase.State != RepoFormState.Sell) { return; } SetContext(BuildContext(_useCase.ExitSellState())); RefreshCurrentUI(); } private void OnRepoSellConfirmRequested(object sender, GameEventArgs e) { if (!IsEventFromCurrentForm(sender) || !(e is RepoSellConfirmRequestedEventArgs) || _useCase == null) { return; } if (!_useCase.TryConfirmSellSelection(out RepoFormRawData rawData, out PlayerInventorySaleResult result)) { if (result != null && result.FailureReason == PlayerInventorySaleFailureReason.ParticipantTower) { OpenSellBlockedDialog("参战防御塔不能直接出售,请先移出参战区。"); } return; } SetContext(BuildContext(rawData)); RefreshCurrentUI(); if (GameEntry.UI.GetUIForm(UIFormType.ShopForm) != null) { GameEntry.UIRouter.OpenUI(UIFormType.ShopForm); } } private bool IsEventFromCurrentForm(object sender) { if (Form == null) { return false; } if (ReferenceEquals(sender, Form)) { return true; } if (sender is Component component) { RepoForm ownerForm = component.GetComponentInParent(); return ownerForm == Form; } return false; } private static void OpenSellBlockedDialog(string message) { GameEntry.UIRouter.OpenUI(UIFormType.DialogForm, new DialogFormRawData { Mode = 1, Title = "无法出售", Message = message, PauseGame = false, ConfirmText = "知道了" }); } #endregion } }