迁移剩下的 UI 到 Serialization 方式
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SepCore.CustomUtility;
|
||||
using SepCore.Definition;
|
||||
using SepCore.Entity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class GoodsItemContext : UIContext
|
||||
{
|
||||
public string Name;
|
||||
public Rarity Rarity;
|
||||
public Sprite Icon;
|
||||
public ItemType ItemType;
|
||||
public string Description;
|
||||
public int Price;
|
||||
|
||||
public GoodsItemContext()
|
||||
{
|
||||
Name = string.Empty;
|
||||
Description = string.Empty;
|
||||
Rarity = Rarity.White;
|
||||
Price = 0;
|
||||
Icon = null;
|
||||
ItemType = ItemType.Weapon;
|
||||
}
|
||||
|
||||
public GoodsItemContext(GoodsItemRawData rawData) : this()
|
||||
{
|
||||
Price = rawData.Price;
|
||||
Rarity = rawData.Rarity;
|
||||
ItemType = rawData.ItemType;
|
||||
|
||||
if (ItemType == ItemType.Weapon)
|
||||
{
|
||||
if (rawData.DataRow is WeaponConfig weaponConfig)
|
||||
{
|
||||
Name = weaponConfig.WeaponType.ToString();
|
||||
Description = ItemDescUtility.CreateWeaponDescription(weaponConfig);
|
||||
}
|
||||
else if (rawData.DataRow is WeaponData weaponData)
|
||||
{
|
||||
Name = weaponData.Title;
|
||||
Description = ItemDescUtility.CreateWeaponDescription(weaponData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var prop = (PropConfig)rawData.DataRow;
|
||||
Name = prop.Name;
|
||||
Description = ItemDescUtility.CreatePropDescription(prop.Modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9d0b321b02be5f41a27cc0a8bf84e34
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class ShopContext : UIContext
|
||||
{
|
||||
public int CurrentLevel;
|
||||
public int RefreshPrice;
|
||||
public int PlayerCoin;
|
||||
public List<GoodsItemContext> GoodsItems;
|
||||
public DisplayListContext PropListContext;
|
||||
public DisplayListContext WeaponListContext;
|
||||
|
||||
// 控制字段
|
||||
public bool NeedRefreshGoodsItems;
|
||||
public bool NeedRefreshPlayerCoin;
|
||||
public bool NeedRefreshWeaponList;
|
||||
public bool NeedRefreshPropList;
|
||||
public bool NeedRefreshPrice;
|
||||
|
||||
public ShopContext()
|
||||
{
|
||||
CurrentLevel = 0;
|
||||
RefreshPrice = 0;
|
||||
PlayerCoin = 0;
|
||||
}
|
||||
|
||||
public ShopContext(ShopRawData rawData)
|
||||
{
|
||||
CurrentLevel = rawData.CurrentLevel;
|
||||
RefreshPrice = rawData.RefreshPrice;
|
||||
PlayerCoin = rawData.PlayerCoin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0ef5183d2f87a54793b86d848e6524a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SepCore.AsyncTask;
|
||||
using SepCore.Definition;
|
||||
using SepCore.Entity;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public enum DisplayListAreaType : byte
|
||||
{
|
||||
None = 0,
|
||||
Prop = 1,
|
||||
Weapon = 2
|
||||
}
|
||||
|
||||
public partial class ShopController
|
||||
{
|
||||
private async UniTask<ShopContext> BuildContext(ShopRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
Log.Error("ShopFormController.BuildContext() rawData is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
_rawData = rawData;
|
||||
|
||||
List<GoodsItemContext> goodsItems = new List<GoodsItemContext>();
|
||||
foreach (var item in rawData.GoodsItems)
|
||||
{
|
||||
var context = await CreateGoodsItemContextAsync(item);
|
||||
goodsItems.Add(context);
|
||||
}
|
||||
|
||||
return new ShopContext(rawData)
|
||||
{
|
||||
GoodsItems = goodsItems,
|
||||
PropListContext =
|
||||
BuildDisplayListContext(DisplayListAreaType.Prop, rawData.PropItems, rawData.PropMaxCount),
|
||||
WeaponListContext = BuildDisplayListContext(DisplayListAreaType.Weapon, rawData.WeaponItems,
|
||||
rawData.WeaponMaxCount),
|
||||
NeedRefreshGoodsItems = true,
|
||||
NeedRefreshPlayerCoin = true,
|
||||
NeedRefreshPropList = true,
|
||||
NeedRefreshWeaponList = true,
|
||||
NeedRefreshPrice = true
|
||||
};
|
||||
}
|
||||
|
||||
private static DisplayListContext BuildDisplayListContext(DisplayListAreaType listType,
|
||||
IReadOnlyList<object> items, int maxCount)
|
||||
{
|
||||
DisplayItemContext[] itemContexts = new DisplayItemContext[items.Count];
|
||||
switch (listType)
|
||||
{
|
||||
case DisplayListAreaType.Weapon:
|
||||
IReadOnlyList<WeaponBase> weapons = (IReadOnlyList<WeaponBase>)items;
|
||||
for (int i = 0; i < weapons.Count; i++)
|
||||
{
|
||||
itemContexts[i] = BuildWeaponItem(weapons[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DisplayListAreaType.Prop:
|
||||
IReadOnlyList<PropItem> propItems = (IReadOnlyList<PropItem>)items;
|
||||
for (int i = 0; i < propItems.Count; i++)
|
||||
{
|
||||
itemContexts[i] = BuildPropItem(propItems[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return new DisplayListContext
|
||||
{
|
||||
CurrentCount = itemContexts.Length,
|
||||
MaxCount = maxCount,
|
||||
ItemContexts = itemContexts
|
||||
};
|
||||
}
|
||||
|
||||
private static DisplayItemContext BuildPropItem(PropItem propItem)
|
||||
{
|
||||
return new DisplayItemContext
|
||||
{
|
||||
IconAssetName = propItem.IconAssetName,
|
||||
Rarity = propItem.Rarity,
|
||||
IsWeapon = false
|
||||
};
|
||||
}
|
||||
|
||||
private static DisplayItemContext BuildWeaponItem(WeaponBase weaponBase)
|
||||
{
|
||||
return new DisplayItemContext
|
||||
{
|
||||
IconAssetName = weaponBase.WeaponData.IconAssetName,
|
||||
Rarity = weaponBase.WeaponData.Rarity,
|
||||
IsWeapon = true
|
||||
};
|
||||
}
|
||||
|
||||
private static void AppendDisplayItemContext(DisplayListContext listContext, DisplayItemContext newItem)
|
||||
{
|
||||
int oldCount = listContext.ItemContexts.Length;
|
||||
DisplayItemContext[] newContexts = new DisplayItemContext[oldCount + 1];
|
||||
if (oldCount > 0)
|
||||
{
|
||||
System.Array.Copy(listContext.ItemContexts, newContexts, oldCount);
|
||||
}
|
||||
|
||||
newContexts[oldCount] = newItem;
|
||||
listContext.ItemContexts = newContexts;
|
||||
listContext.CurrentCount = oldCount + 1;
|
||||
}
|
||||
|
||||
private async UniTask<GoodsItemContext> CreateGoodsItemContextAsync(GoodsItemRawData rawData,
|
||||
float timeout = 30f)
|
||||
{
|
||||
var context = new GoodsItemContext(rawData);
|
||||
context.Icon = context.ItemType switch
|
||||
{
|
||||
ItemType.Weapon => await SpriteLoader.LoadSpriteByKeyAsync(rawData.DataRow switch
|
||||
{
|
||||
WeaponConfig weaponConfig => weaponConfig.Sprite,
|
||||
WeaponData weaponData => weaponData.IconAssetName,
|
||||
_ => string.Empty
|
||||
}),
|
||||
ItemType.Prop => await SpriteLoader.LoadSpriteByKeyAsync(((PropConfig)rawData.DataRow).Sprite),
|
||||
_ => null
|
||||
};
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d035632a30d29a4db6714cdbb9a391f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,374 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SepCore.Event;
|
||||
using SepCore.Definition;
|
||||
using SepCore.Entity;
|
||||
using GameFramework.Event;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public partial class ShopController : UIControllerBase<ShopContext, ShopForm>
|
||||
{
|
||||
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<int?> 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 (result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count)
|
||||
{
|
||||
Context.GoodsItems[result.GoodsIndex] = null;
|
||||
}
|
||||
|
||||
DisplayItemContext context = new DisplayItemContext(result.DisplayItem);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be57ace1e7b07be429632ea49bcab6c5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,134 @@
|
||||
using System.Collections.Generic;
|
||||
using SepCore.Event;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public partial class ShopForm : UGuiForm
|
||||
{
|
||||
private readonly List<ShopGoodsItem> _goodsItems = new List<ShopGoodsItem>();
|
||||
|
||||
private ShopContext _context;
|
||||
|
||||
private bool _listenersBound;
|
||||
|
||||
public void RefreshUI(ShopContext context)
|
||||
{
|
||||
EnsureButtonListeners();
|
||||
|
||||
_context = context;
|
||||
|
||||
if (context.NeedRefreshGoodsItems)
|
||||
{
|
||||
View.titleFormatText.Set(context.CurrentLevel);
|
||||
View.continueButtonFormatText.Set(context.CurrentLevel + 1);
|
||||
RefreshGoodsItems(context.GoodsItems);
|
||||
context.NeedRefreshGoodsItems = false;
|
||||
}
|
||||
|
||||
if (context.NeedRefreshPrice)
|
||||
{
|
||||
View.refreshButtonFormatText.Set(context.RefreshPrice);
|
||||
context.NeedRefreshPrice = false;
|
||||
}
|
||||
|
||||
if (context.NeedRefreshPlayerCoin)
|
||||
{
|
||||
View.playerCoinFormatText.Set(context.PlayerCoin);
|
||||
context.NeedRefreshPlayerCoin = false;
|
||||
}
|
||||
|
||||
if (context.NeedRefreshPropList)
|
||||
{
|
||||
View.propList.OnInit(context.PropListContext);
|
||||
context.NeedRefreshPropList = false;
|
||||
}
|
||||
|
||||
if (context.NeedRefreshWeaponList)
|
||||
{
|
||||
View.weaponList.OnInit(context.WeaponListContext);
|
||||
context.NeedRefreshWeaponList = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
|
||||
if (userData is not ShopContext context)
|
||||
{
|
||||
Log.Error("ShopForm requires ShopContext as userData.");
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
_context = null;
|
||||
|
||||
for (int i = 0; i < _goodsItems.Count; i++)
|
||||
{
|
||||
_goodsItems[i].OnReset();
|
||||
}
|
||||
|
||||
View.propList.OnReset();
|
||||
View.weaponList.OnReset();
|
||||
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void EnsureButtonListeners()
|
||||
{
|
||||
if (_listenersBound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_listenersBound = true;
|
||||
|
||||
View.continueButton.onClick.AddListener(OnContinueButtonClick);
|
||||
View.refreshButton.onClick.AddListener(OnRefreshButtonClick);
|
||||
}
|
||||
|
||||
private void RefreshGoodsItems(List<GoodsItemContext> goodsItems)
|
||||
{
|
||||
EnsureGoodsItemCount(goodsItems.Count);
|
||||
|
||||
for (int i = 0; i < _goodsItems.Count; i++)
|
||||
{
|
||||
ShopGoodsItem item = _goodsItems[i];
|
||||
if (i < goodsItems.Count && goodsItems[i] != null)
|
||||
{
|
||||
item.Init(goodsItems[i], i);
|
||||
item.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.OnReset();
|
||||
item.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureGoodsItemCount(int count)
|
||||
{
|
||||
while (_goodsItems.Count < count)
|
||||
{
|
||||
ShopGoodsItem item = Instantiate(View.goodsItemTemplate, View.GoodsItemRoot);
|
||||
_goodsItems.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnContinueButtonClick()
|
||||
{
|
||||
GameEntry.Event.Fire(this, ShopContinueEventArgs.Create());
|
||||
}
|
||||
|
||||
public void OnRefreshButtonClick()
|
||||
{
|
||||
GameEntry.Event.Fire(this, ShopRefreshEventArgs.Create(_context.RefreshPrice));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6f22432e5701804d9a78d0c07f4c334
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
// <auto-generated />
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public partial class ShopForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private ShopView _view = null;
|
||||
|
||||
public ShopView View => _view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b261ad08be35724a83c21227db57e6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using SepCore.Event;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
public class ShopGoodsItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
||||
[SerializeField] private TMP_Text _typeText;
|
||||
|
||||
[SerializeField] private TMP_Text _descriptionText;
|
||||
|
||||
[SerializeField] private TMP_Text _costText;
|
||||
|
||||
private int _index = -1;
|
||||
|
||||
private bool _listenersBound;
|
||||
|
||||
public void Init(GoodsItemContext data, int index)
|
||||
{
|
||||
EnsureListeners();
|
||||
|
||||
_index = index;
|
||||
|
||||
_iconArea.OnInit(data.Icon, data.Rarity);
|
||||
_titleText.text = data.Name;
|
||||
_typeText.text = data.ItemType switch
|
||||
{
|
||||
ItemType.None => "error",
|
||||
ItemType.Weapon => "武器",
|
||||
ItemType.Prop => "道具",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
_descriptionText.text = data.Description;
|
||||
_costText.text = $"{data.Price} <sprite name=\"coin\" index=0>";
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
private void EnsureListeners()
|
||||
{
|
||||
if (_listenersBound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_listenersBound = true;
|
||||
|
||||
CommonButton button = GetComponentInChildren<CommonButton>(true);
|
||||
button.onClick.AddListener(OnPurchaseClick);
|
||||
}
|
||||
|
||||
private void OnPurchaseClick()
|
||||
{
|
||||
if (_index < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Fire(this, ShopPurchaseEventArgs.Create(_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 271f2a0727c521c4280eb2d47fd8de33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
// <auto-generated />
|
||||
using UnityEngine;
|
||||
|
||||
namespace SepCore.UI
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public partial class ShopView : MonoBehaviour
|
||||
{
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI titleFormatText;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI playerCoinFormatText;
|
||||
[SerializeField] public global::SepCore.UI.CommonButton refreshButton;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI refreshButtonFormatText;
|
||||
[SerializeField] public global::UnityEngine.RectTransform GoodsItemRoot;
|
||||
[SerializeField] public global::SepCore.UI.ShopGoodsItem goodsItemTemplate;
|
||||
[SerializeField] public global::SepCore.UI.CommonButton continueButton;
|
||||
[SerializeField] public global::SepCore.UI.FormatTextUI continueButtonFormatText;
|
||||
[SerializeField] public global::SepCore.UI.DisplayListForm weaponList;
|
||||
[SerializeField] public global::SepCore.UI.DisplayListForm propList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10a546b2f753668479061813a7f5781f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user