- 规范 UI 设计成为:
- UIForm 负责展示
- Controller 负责控制
- UseCase 负责业务逻辑
- RawData 负责提供原始数据
- Context 负责为 UIForm 提供数据
- 添加和补充一系列 UI 功能
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
using Definition.DataStruct;
|
||||
using Entity;
|
||||
using Game.Utility;
|
||||
using Procedure;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class DisplayItemInfoFormUseCase : IUIUseCase
|
||||
{
|
||||
private readonly ProcedureGame _procedureGame;
|
||||
|
||||
private Player Player => _procedureGame != null ? _procedureGame.Player : null;
|
||||
|
||||
public DisplayItemInfoFormUseCase(ProcedureGame procedureGame)
|
||||
{
|
||||
_procedureGame = procedureGame;
|
||||
}
|
||||
|
||||
public DisplayItemInfoFormRawData CreateModel(int index, bool isWeapon)
|
||||
{
|
||||
if (index < 0 || Player == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isWeapon)
|
||||
{
|
||||
var weapons = Player.Weapons;
|
||||
if (weapons == null || index >= weapons.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
WeaponBase weapon = weapons[index];
|
||||
if (weapon == null || weapon.WeaponData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DisplayItemInfoFormRawData
|
||||
{
|
||||
IconAssetName = weapon.WeaponData.IconAssetName,
|
||||
Title = weapon.WeaponData.Title,
|
||||
TypeText = "Weapon",
|
||||
Description = ItemDescUtility.CreateWeaponDescription(weapon),
|
||||
Price = 0,
|
||||
IsWeapon = true
|
||||
};
|
||||
}
|
||||
|
||||
var props = Player.Props;
|
||||
if (props == null || index >= props.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
PropItem prop = props[index];
|
||||
if (prop == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DisplayItemInfoFormRawData
|
||||
{
|
||||
IconAssetName = prop.IconAssetName,
|
||||
Title = prop.Title,
|
||||
TypeText = "Prop",
|
||||
Description = ItemDescUtility.CreatePropDescription(prop),
|
||||
Price = 0,
|
||||
IsWeapon = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48c1f32fe95e20c43b8015ac1438d54e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,137 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DataTable;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Entity;
|
||||
using Procedure;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
internal class LevelUpFormUseCase : IUIUseCase
|
||||
{
|
||||
private const int BaseRefreshPrice = 2;
|
||||
private readonly GameStateLevelUp _gameStateLevelUp;
|
||||
private readonly DRLevelUpReward[] _allRewards;
|
||||
|
||||
private int _refreshCount;
|
||||
private LevelUpFormRawData _currentModel;
|
||||
|
||||
private readonly Player _player;
|
||||
|
||||
public LevelUpFormUseCase(Player player, GameStateLevelUp gameStateLevelUp)
|
||||
{
|
||||
_player = player;
|
||||
_gameStateLevelUp = gameStateLevelUp;
|
||||
_allRewards = GameEntry.DataTable.GetDataTable<DRLevelUpReward>().ToArray();
|
||||
}
|
||||
|
||||
public LevelUpFormRawData CreateInitialModel(int count = 4)
|
||||
{
|
||||
_refreshCount = 0;
|
||||
|
||||
if (_gameStateLevelUp == null || _player.PendingLevelPoints <= 0)
|
||||
{
|
||||
_currentModel = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
_currentModel = BuildModel(BaseRefreshPrice, count);
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
public LevelUpFormRawData TryRefresh(int refreshCost, int count = 4)
|
||||
{
|
||||
if (_currentModel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_player == null || _player.Coin < refreshCost)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_player.Coin -= refreshCost;
|
||||
|
||||
_refreshCount++;
|
||||
_currentModel = BuildModel((_refreshCount + 1) * BaseRefreshPrice, count);
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
public LevelUpFormRawData SelectReward(int selectedIndex, int count = 4)
|
||||
{
|
||||
if (_currentModel?.Rewards == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (selectedIndex < 0 || selectedIndex >= _currentModel.Rewards.Count)
|
||||
{
|
||||
Log.Warning("LevelUpFormUseCase::SelectReward: Invalid index");
|
||||
return null;
|
||||
}
|
||||
|
||||
ApplyReward(_currentModel.Rewards[selectedIndex]);
|
||||
|
||||
if (--_player.PendingLevelPoints > 0)
|
||||
{
|
||||
_refreshCount = 0;
|
||||
_currentModel = BuildModel(BaseRefreshPrice, count);
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
_gameStateLevelUp.IsCompleted = true;
|
||||
_currentModel = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
private LevelUpFormRawData BuildModel(int refreshPrice, int count)
|
||||
{
|
||||
if (_allRewards == null || _allRewards.Length == 0)
|
||||
{
|
||||
Log.Error("LevelUpFormUseCase::BuildModel(): _allRewards is empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
int finalCount = count > 0 ? System.Math.Min(count, _allRewards.Length) : _allRewards.Length;
|
||||
List<DRLevelUpReward> selections = new List<DRLevelUpReward>(finalCount);
|
||||
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
int index = Random.Range(0, _allRewards.Length);
|
||||
selections.Add(_allRewards[index]);
|
||||
}
|
||||
|
||||
return new LevelUpFormRawData
|
||||
{
|
||||
Rewards = selections,
|
||||
RefreshPrice = refreshPrice
|
||||
};
|
||||
}
|
||||
|
||||
private void ApplyReward(DRLevelUpReward reward)
|
||||
{
|
||||
if (reward == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player == null)
|
||||
{
|
||||
Log.Warning("LevelUpFormUseCase::ApplyReward: Player is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (reward.Modifiers == null || reward.Modifiers.Length == 0)
|
||||
{
|
||||
Log.Warning("LevelUpFormUseCase::ApplyReward: Reward modifiers are empty");
|
||||
return;
|
||||
}
|
||||
|
||||
_player.AddProp(new PropItem(reward.Modifiers, ItemRarity.White, reward.Title, reward.IconAssetName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19d2fa806b03510498d072c5b8710bcb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,248 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DataTable;
|
||||
using Definition.DataStruct;
|
||||
using Definition.Enum;
|
||||
using Entity;
|
||||
using Game.Utility;
|
||||
using GameFramework.DataTable;
|
||||
using Procedure;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
internal sealed class ShopRefreshResult
|
||||
{
|
||||
public List<GoodsItemContext> GoodsItems;
|
||||
public int RefreshPrice;
|
||||
}
|
||||
|
||||
internal sealed class ShopPurchaseResult
|
||||
{
|
||||
public int GoodsIndex;
|
||||
public DisplayItemContext DisplayItem;
|
||||
}
|
||||
|
||||
internal class ShopFormUseCase : IUIUseCase
|
||||
{
|
||||
private readonly ProcedureGame _procedureGame;
|
||||
private readonly GameStateShop _gameStateShop;
|
||||
|
||||
private readonly DRGoods[] _allGoods;
|
||||
private readonly IDataTable<DRProp> _propDataTable;
|
||||
private readonly IDataTable<DRWeapon> _weaponDataTable;
|
||||
|
||||
private readonly List<ShopGoodsSelection> _selections = new();
|
||||
private int _refreshTime;
|
||||
|
||||
private Player Player => _procedureGame.Player;
|
||||
|
||||
private sealed class ShopGoodsSelection
|
||||
{
|
||||
public GoodsType GoodsType;
|
||||
public int GoodsTypeId;
|
||||
public int Price;
|
||||
}
|
||||
|
||||
public ShopFormUseCase(ProcedureGame procedureGame, GameStateShop gameStateShop)
|
||||
{
|
||||
_procedureGame = procedureGame;
|
||||
_gameStateShop = gameStateShop;
|
||||
_allGoods = GameEntry.DataTable.GetDataTable<DRGoods>().ToArray();
|
||||
_propDataTable = GameEntry.DataTable.GetDataTable<DRProp>();
|
||||
_weaponDataTable = GameEntry.DataTable.GetDataTable<DRWeapon>();
|
||||
}
|
||||
|
||||
public ShopFormRawData CreateInitialModel()
|
||||
{
|
||||
_refreshTime = 0;
|
||||
|
||||
Player player = Player;
|
||||
if (_procedureGame == null || player == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int currentLevel = _procedureGame.CurrentLevel;
|
||||
return new ShopFormRawData
|
||||
{
|
||||
CurrentLevel = currentLevel,
|
||||
RefreshPrice = currentLevel,
|
||||
PlayerCoin = player.Coin,
|
||||
GoodsItems = BuildRandomGoodsItems(4),
|
||||
PropItems = player.Props,
|
||||
PropMaxCount = -1,
|
||||
WeaponItems = player.Weapons,
|
||||
WeaponMaxCount = player.WeaponCapacity
|
||||
};
|
||||
}
|
||||
|
||||
public ShopRefreshResult TryRefresh(int cost)
|
||||
{
|
||||
Player player = Player;
|
||||
if (player == null || _procedureGame == null || player.Coin < cost)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
player.Coin -= cost;
|
||||
|
||||
int refreshPrice = (_refreshTime + 1) * _procedureGame.CurrentLevel;
|
||||
_refreshTime++;
|
||||
|
||||
return new ShopRefreshResult
|
||||
{
|
||||
GoodsItems = BuildRandomGoodsItems(4),
|
||||
RefreshPrice = refreshPrice
|
||||
};
|
||||
}
|
||||
|
||||
public ShopPurchaseResult TryPurchase(int goodsIndex)
|
||||
{
|
||||
if (goodsIndex < 0 || goodsIndex >= _selections.Count)
|
||||
{
|
||||
Log.Warning("ShopFormUseCase::TryPurchase: Invalid index");
|
||||
return null;
|
||||
}
|
||||
|
||||
ShopGoodsSelection selection = _selections[goodsIndex];
|
||||
if (selection == null)
|
||||
{
|
||||
Log.Warning("ShopFormUseCase::TryPurchase: Item already purchased.");
|
||||
return null;
|
||||
}
|
||||
|
||||
Player player = Player;
|
||||
if (player == null || player.Coin < selection.Price)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
player.Coin -= selection.Price;
|
||||
DisplayItemContext displayItem = ApplyGoodsPurchase(selection);
|
||||
_selections[goodsIndex] = null;
|
||||
|
||||
return new ShopPurchaseResult
|
||||
{
|
||||
GoodsIndex = goodsIndex,
|
||||
DisplayItem = displayItem
|
||||
};
|
||||
}
|
||||
|
||||
public void Continue()
|
||||
{
|
||||
_gameStateShop.ShopFinish = true;
|
||||
}
|
||||
|
||||
private List<GoodsItemContext> BuildRandomGoodsItems(int count)
|
||||
{
|
||||
if (_allGoods == null || _allGoods.Length == 0)
|
||||
{
|
||||
Log.Error("ShopFormUseCase::BuildRandomGoodsItems(): _allGoods is empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
int finalCount = Mathf.Min(count, _allGoods.Length);
|
||||
List<GoodsItemContext> goodsItems = new List<GoodsItemContext>(finalCount);
|
||||
_selections.Clear();
|
||||
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
int index = Random.Range(0, _allGoods.Length);
|
||||
DRGoods drGoods = _allGoods[index];
|
||||
GoodsItemContext goodsItem = new GoodsItemContext();
|
||||
int price;
|
||||
|
||||
if (drGoods.GoodsType == GoodsType.Prop)
|
||||
{
|
||||
DRProp drProp = _propDataTable.GetDataRow(drGoods.GoodsTypeId);
|
||||
if (drProp == null)
|
||||
{
|
||||
Log.Warning($"ShopFormUseCase::BuildRandomGoodsItems: Missing DRProp, id = {drGoods.GoodsTypeId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
price = CalculateRandomizedPrice(drProp.Price, drProp.PriceRandomPercent);
|
||||
goodsItem.Title = drProp.Title;
|
||||
goodsItem.Type = "Prop";
|
||||
goodsItem.Rarity = drProp.Rarity;
|
||||
GameEntry.SpriteCache.GetSprite(drProp.IconAssetName, sprite => goodsItem.Icon = sprite);
|
||||
goodsItem.Description = ItemDescUtility.CreatePropDescription(drProp.Modifiers);
|
||||
}
|
||||
else if (drGoods.GoodsType == GoodsType.Weapon)
|
||||
{
|
||||
DRWeapon drWeapon = _weaponDataTable.GetDataRow(drGoods.GoodsTypeId);
|
||||
if (drWeapon == null)
|
||||
{
|
||||
Log.Warning($"ShopFormUseCase::BuildRandomGoodsItems: Missing DRWeapon, id = {drGoods.GoodsTypeId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
price = CalculateRandomizedPrice(drWeapon.Price, drWeapon.PriceRandomPercent);
|
||||
goodsItem.Title = drWeapon.Title;
|
||||
goodsItem.Type = "Weapon";
|
||||
goodsItem.Rarity = drWeapon.Rarity;
|
||||
GameEntry.SpriteCache.GetSprite(drWeapon.IconAssetName, sprite => goodsItem.Icon = sprite);
|
||||
goodsItem.Description = ItemDescUtility.CreateWeaponDescription(drWeapon);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"ShopFormUseCase::BuildRandomGoodsItems: Unsupported goods type = {drGoods.GoodsType}");
|
||||
continue;
|
||||
}
|
||||
|
||||
goodsItem.Price = price;
|
||||
|
||||
_selections.Add(new ShopGoodsSelection
|
||||
{
|
||||
GoodsType = drGoods.GoodsType,
|
||||
GoodsTypeId = drGoods.GoodsTypeId,
|
||||
Price = price
|
||||
});
|
||||
|
||||
goodsItems.Add(goodsItem);
|
||||
}
|
||||
|
||||
return goodsItems;
|
||||
}
|
||||
|
||||
private static int CalculateRandomizedPrice(int basePrice, float priceRandomPercent)
|
||||
{
|
||||
float normalizedPercent = priceRandomPercent > 1f ? priceRandomPercent / 100f : priceRandomPercent;
|
||||
normalizedPercent = Mathf.Max(0f, normalizedPercent);
|
||||
float offsetPercent = Random.Range(-normalizedPercent, normalizedPercent);
|
||||
int finalPrice = Mathf.RoundToInt(basePrice * (1f + offsetPercent));
|
||||
return Mathf.Max(1, finalPrice);
|
||||
}
|
||||
|
||||
private DisplayItemContext ApplyGoodsPurchase(ShopGoodsSelection goods)
|
||||
{
|
||||
if (goods == null || Player == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (goods.GoodsType == GoodsType.Prop)
|
||||
{
|
||||
DRProp drProp = _propDataTable != null ? _propDataTable.GetDataRow(goods.GoodsTypeId) : null;
|
||||
if (drProp == null || drProp.Modifiers == null || drProp.Modifiers.Length == 0)
|
||||
{
|
||||
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Prop modifiers are empty");
|
||||
return null;
|
||||
}
|
||||
|
||||
Player.AddProp(new PropItem(drProp.Modifiers, drProp.Rarity, drProp.Title, drProp.IconAssetName));
|
||||
return new DisplayItemContext
|
||||
{
|
||||
IconAssetName = drProp.IconAssetName,
|
||||
Rarity = drProp.Rarity,
|
||||
IsWeapon = false
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e973748d1bd846349d42e219a80f07c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user