using Cysharp.Threading.Tasks; using SepCore.Event; using SepCore.Definition; using SepCore.Entity.Weapon; using GameFramework.Event; using UnityEngine; using UnityGameFramework.Runtime; namespace SepCore.UI { public partial class ShopController : UIControllerBase { private ShopUseCase _useCase; private ShopRawData _rawData; private bool _tooltipLocked = false; protected override UIFormType UIFormType => UIFormType.ShopForm; protected override void RefreshUI(ShopForm form, ShopContext context) { form.RefreshUI(context); } protected override void SubscribeCustomEvents() { GameEntry.Event.Subscribe(ShopRefreshEventArgs.EventId, ShopRefresh); GameEntry.Event.Subscribe(ShopPurchaseEventArgs.EventId, ShopPurchase); GameEntry.Event.Subscribe(ShopContinueEventArgs.EventId, ShopContinue); GameEntry.Event.Subscribe(DisplayItemHoverEventArgs.EventId, DisplayItemHover); GameEntry.Event.Subscribe(DisplayItemLockEventArgs.EventId, DisplayItemLock); GameEntry.Event.Subscribe(DisplayItemHideEventArgs.EventId, DisplayItemHide); GameEntry.Event.Subscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange); GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormComplete); } protected override void UnsubscribeCustomEvents() { GameEntry.Event.Unsubscribe(ShopRefreshEventArgs.EventId, ShopRefresh); GameEntry.Event.Unsubscribe(ShopPurchaseEventArgs.EventId, ShopPurchase); GameEntry.Event.Unsubscribe(ShopContinueEventArgs.EventId, ShopContinue); GameEntry.Event.Unsubscribe(DisplayItemHoverEventArgs.EventId, DisplayItemHover); GameEntry.Event.Unsubscribe(DisplayItemLockEventArgs.EventId, DisplayItemLock); GameEntry.Event.Unsubscribe(DisplayItemHideEventArgs.EventId, DisplayItemHide); GameEntry.Event.Unsubscribe(PlayerCoinChangeEventArgs.EventId, PlayerCoinChange); GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormComplete); } #region UI Methods public override async UniTask CloseUIAsync(object userData = null, float timeout = 30f) { GameEntry.UIRouter.CloseUIAsync(UIFormType.ItemTooltipForm).Forget(); _rawData = null; _tooltipLocked = false; await base.CloseUIAsync(userData, timeout); } public async UniTask OpenUIAsync(ShopRawData rawData, float timeout = 30f) { ShopContext context = await BuildContext(rawData); return await OpenFormAsync(context, timeout); } public override async UniTask OpenUIAsync(object userData = null, float timeout = 30f) { if (userData is ShopRawData rawData) { await OpenUIAsync(rawData, timeout); return; } if (userData != null) { Log.Warning("ShopController.OpenUIAsync() userData type is invalid."); return; } if (_useCase == null) { Log.Error("ShopController.OpenUIAsync() useCase is null."); return; } ShopRawData initialRawData = _useCase.CreateInitialModel(); await OpenUIAsync(initialRawData, timeout); } public override void BindUseCase(IUIUseCase useCase) { if (useCase is not ShopUseCase shopFormUseCase) { Log.Error("ShopForm.BindUseCase() useCase is invalid."); return; } _useCase = shopFormUseCase; } #endregion #region Service private async UniTask RefreshGoodsItems(ShopRefreshResult result) { for (int i = 0; i < result.GoodsItems.Count; i++) { if (i < Context.GoodsItems.Count) { Context.GoodsItems[i] = await CreateGoodsItemContextAsync(result.GoodsItems[i]); } else Context.GoodsItems.Add(await CreateGoodsItemContextAsync(result.GoodsItems[i])); } if (Context.GoodsItems.Count != result.GoodsItems.Count) { int count = Context.GoodsItems.Count - result.GoodsItems.Count; Context.GoodsItems.RemoveRange(result.GoodsItems.Count, count); } Context.RefreshPrice = result.RefreshPrice; Context.NeedRefreshGoodsItems = true; Context.NeedRefreshPrice = true; RefreshUI(Form, Context); } private void ApplyGoodsPurchased(ShopPurchaseResult result) { if (Context.GoodsItems != null && result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count) { Context.GoodsItems[result.GoodsIndex] = null; } DisplayItemContext context = new DisplayItemContext(result.DisplayItem); if (result.DisplayItem != null) { if (result.DisplayItem.IsWeapon) { AppendDisplayItemContext(Context.WeaponListContext, context); Context.NeedRefreshWeaponList = true; } else { AppendDisplayItemContext(Context.PropListContext, context); Context.NeedRefreshPropList = true; } } Context.NeedRefreshGoodsItems = true; RefreshUI(Form, Context); } private bool TryGetWeaponInfoRawData(int index, Vector3 targetPos, out ItemTooltipRawData rawData) { rawData = null; if (_rawData?.WeaponItems == null) { Log.Error("ShopFormController.TryGetWeaponInfoRawData() WeaponItems is null."); return false; } if (Context == null) { Log.Error("ShopFormController.TryGetWeaponInfoRawData() Context is null."); return false; } if (index < 0 || index >= _rawData.WeaponItems.Count) { Log.Error($"ShopFormController.TryGetWeaponInfoRawData() invalid weapon index: {index}."); return false; } WeaponBase weapon = _rawData.WeaponItems[index]; if (weapon?.WeaponData == null) { Log.Error($"ShopFormController.TryGetWeaponInfoRawData() weapon data is null at index {index}."); return false; } rawData = new ItemTooltipRawData { TargetPos = targetPos, Index = index, WeaponData = weapon.WeaponData, Rarity = weapon.WeaponData.Rarity, ItemType = ItemType.Weapon, Price = _useCase.CalculateRecyclePrice(weapon.WeaponData.Price), OnRecycle = RecycleWeapon, }; return true; } private bool TryGetPropInfoRawData(int index, Vector3 targetPos, out ItemTooltipRawData rawData) { rawData = null; if (_rawData?.PropItems == null) { Log.Error("ShopFormController.TryGetPropInfoRawData() PropItems is null."); return false; } if (index < 0 || index >= _rawData.PropItems.Count) { Log.Error($"ShopFormController.TryGetPropInfoRawData() invalid prop index: {index}."); return false; } PropItem propItem = _rawData.PropItems[index]; if (propItem == null) { Log.Error($"ShopFormController.TryGetPropInfoRawData() prop item is null at index {index}."); return false; } rawData = new ItemTooltipRawData { TargetPos = targetPos, Index = index, PropData = propItem, Rarity = propItem.Rarity, ItemType = ItemType.Prop, Price = 0 }; return true; } private void RecycleWeapon(int index, int price) { if (_useCase == null || Context == null) { Log.Error("ShopFormController.RecycleWeapon() controller state is invalid."); return; } bool success = _useCase.TryRecycleWeapon(index, price); if (!success) { return; } if (Context.WeaponListContext != null) { int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1); Context.WeaponListContext.CurrentCount = currentCount; } Context.NeedRefreshWeaponList = true; RefreshUI(Form, Context); GameEntry.UIRouter.CloseUIAsync(UIFormType.ItemTooltipForm).Forget(); } #endregion #region Event Handlers private async void ShopRefresh(object sender, GameEventArgs e) { if (e is not ShopRefreshEventArgs args) { return; } ShopRefreshResult result = _useCase.TryRefresh(args.Cost); if (result == null) { return; } await RefreshGoodsItems(result); } private void ShopPurchase(object sender, GameEventArgs e) { if (e is not ShopPurchaseEventArgs args) { return; } OnPurchaseRequestAsync(args).Forget(); } private async UniTaskVoid OnPurchaseRequestAsync(ShopPurchaseEventArgs args) { ShopPurchaseResult result = await _useCase.TryPurchaseAsync(args.GoodsIndex); if (result == null) { return; } ApplyGoodsPurchased(result); } private void ShopContinue(object sender, GameEventArgs e) { if (e is not ShopContinueEventArgs) { return; } _useCase?.Continue(); } private void DisplayItemHover(object sender, GameEventArgs e) { if (e is not DisplayItemHoverEventArgs args) { return; } if (_rawData == null) { Log.Error("ShopFormController.OnDisplayItemHover() _rawData is null."); return; } ItemTooltipRawData rawData; bool success = args.IsWeapon ? TryGetWeaponInfoRawData(args.Index, args.TargetPos, out rawData) : TryGetPropInfoRawData(args.Index, args.TargetPos, out rawData); if (!success) { return; } _tooltipLocked = false; GameEntry.UIRouter.OpenUIAsync(UIFormType.ItemTooltipForm, rawData).Forget(); } private void DisplayItemLock(object sender, GameEventArgs e) { if (e is not DisplayItemLockEventArgs) { return; } _tooltipLocked = true; } private void DisplayItemHide(object sender, GameEventArgs e) { if (e is not DisplayItemHideEventArgs args) { return; } if (_tooltipLocked) { return; } GameEntry.UIRouter.CloseUIAsync(UIFormType.ItemTooltipForm).Forget(); } private void PlayerCoinChange(object sender, GameEventArgs e) { if (e is not PlayerCoinChangeEventArgs args) return; if (Context == null) return; Context.PlayerCoin = args.CoinCount; Context.NeedRefreshPlayerCoin = true; RefreshUI(Form, Context); } private void OpenUIFormComplete(object sender, GameEventArgs e) { if (e is not OpenUIFormSuccessEventArgs ne) return; if (ne.UIForm.Logic is ItemTooltipForm) { _tooltipLocked = false; } } #endregion } }