引入 UISerializationRoot 和 Item

- 完成 MenuForm 和 SelectRoleForm 的迁移
This commit is contained in:
2026-09-13 22:18:29 +08:00
parent e2fdb660ab
commit 091a29db63
101 changed files with 5281 additions and 5753 deletions
@@ -25,8 +25,6 @@ namespace SepCore.UI
public class ShopUseCase : IUIUseCase
{
private const float WeaponRecycleRate = 0.3f;
private readonly Player _player;
private readonly int _currentLevel;
private readonly Action _onShopFinish;
@@ -39,8 +37,6 @@ namespace SepCore.UI
private readonly List<ShopGoodsSelection> _selections = new();
private int _refreshTime;
private Player Player => _player;
private sealed class ShopGoodsSelection
{
public GoodsType GoodsType;
@@ -63,42 +59,34 @@ namespace SepCore.UI
{
_refreshTime = 0;
Player player = Player;
if (_player == null || player == null)
{
return null;
}
int currentLevel = _currentLevel;
return new ShopRawData
{
CurrentLevel = currentLevel,
RefreshPrice = currentLevel,
PlayerCoin = player.Coin,
GoodsItems = BuildRandomGoodsItems(4),
PropItems = player.Props,
PropMaxCount = -1,
WeaponItems = player.Weapons,
WeaponMaxCount = player.WeaponCapacity
CurrentLevel = _currentLevel,
RefreshPrice = _currentLevel,
PlayerCoin = _player.Coin,
GoodsItems = BuildRandomGoodsItems(GameEntry.Luban.Global.ShopGoodsCount),
PropItems = _player.Props,
PropMaxCount = GameEntry.Luban.Global.PropMaxCount,
WeaponItems = _player.Weapons,
WeaponMaxCount = _player.WeaponCapacity
};
}
public ShopRefreshResult TryRefresh(int cost)
{
Player player = Player;
if (player == null || _player == null || player.Coin < cost)
if (_player.Coin < cost)
{
return null;
}
player.Coin -= cost;
_player.Coin -= cost;
int refreshPrice = (_refreshTime + 1) * _currentLevel;
_refreshTime++;
return new ShopRefreshResult
{
GoodsItems = BuildRandomGoodsItems(4),
GoodsItems = BuildRandomGoodsItems(GameEntry.Luban.Global.ShopGoodsCount),
RefreshPrice = refreshPrice
};
}
@@ -107,7 +95,7 @@ namespace SepCore.UI
{
if (goodsIndex < 0 || goodsIndex >= _selections.Count)
{
Log.Warning("ShopFormUseCase::TryPurchase: Invalid index");
Log.Error("ShopFormUseCase::TryPurchase: Invalid index");
return null;
}
@@ -118,8 +106,7 @@ namespace SepCore.UI
return null;
}
Player player = Player;
if (player == null || player.Coin < selection.Price)
if (_player.Coin < selection.Price)
{
return null;
}
@@ -130,7 +117,7 @@ namespace SepCore.UI
return null;
}
player.Coin -= selection.Price;
_player.Coin -= selection.Price;
_selections[goodsIndex] = null;
return new ShopPurchaseResult
@@ -147,7 +134,7 @@ namespace SepCore.UI
private List<GoodsItemRawData> BuildRandomGoodsItems(int count)
{
if (_allGoods == null || _allGoods.Count == 0)
if (_allGoods.Count == 0)
{
Log.Error("ShopFormUseCase::BuildRandomGoodsItems(): _allGoods is empty");
return null;
@@ -157,7 +144,8 @@ namespace SepCore.UI
List<GoodsItemRawData> goodsItems = new List<GoodsItemRawData>(finalCount);
_selections.Clear();
int currentLevel = _currentLevel > 0 ? _currentLevel : 1;
int currentLevel = _currentLevel;
float priceRandomPercent = GameEntry.Luban.Global.ShopPriceRandomPercent / 1000f;
for (int i = 0; i < finalCount; i++)
{
@@ -165,7 +153,7 @@ namespace SepCore.UI
GoodsConfig goodsConfig = PickGoodsByRarity(rarity);
if (goodsConfig == null)
{
Log.Warning("ShopFormUseCase::BuildRandomGoodsItems: No available goods for selection.");
Log.Error("ShopFormUseCase::BuildRandomGoodsItems: No available goods for selection.");
break;
}
@@ -176,9 +164,8 @@ namespace SepCore.UI
{
PropConfig propConfig = GameEntry.Luban.Get<PropConfig>(goodsConfig.PropId);
int basePrice = propConfig.Price;
float randomPercent = 0.05f;
price = CalculateRandomizedPrice(basePrice, randomPercent);
price = CalculateRandomizedPrice(basePrice, priceRandomPercent);
goodsItem.ItemType = ItemType.Prop;
goodsItem.Rarity = propConfig.Rarity;
goodsItem.DataRow = propConfig;
@@ -187,9 +174,8 @@ namespace SepCore.UI
{
WeaponConfig weaponConfig = GameEntry.Luban.Get<WeaponConfig>((int)goodsConfig.WeaponType);
int basePrice = weaponConfig.Price;
float randomPercent = 0.05f;
price = CalculateRandomizedPrice(basePrice, randomPercent);
price = CalculateRandomizedPrice(basePrice, priceRandomPercent);
goodsItem.ItemType = ItemType.Weapon;
goodsItem.Rarity = weaponConfig.Rarity;
goodsItem.DataRow = weaponConfig;
@@ -222,12 +208,8 @@ namespace SepCore.UI
_goodsByRarity.Clear();
_validGoods.Clear();
if (_allGoods == null) return;
foreach (GoodsConfig goods in _allGoods)
{
if (goods == null) continue;
if (!TryGetGoodsRarity(goods, out Rarity rarity))
{
continue;
@@ -263,10 +245,6 @@ namespace SepCore.UI
private bool TryGetGoodsRarity(GoodsConfig goods, out Rarity rarity)
{
rarity = Rarity.None;
if (goods == null)
{
return false;
}
if (goods.GoodsType == GoodsType.Prop)
{
@@ -293,30 +271,17 @@ namespace SepCore.UI
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);
float offsetPercent = Random.Range(-priceRandomPercent, priceRandomPercent);
int finalPrice = Mathf.RoundToInt(basePrice * (1f + offsetPercent));
return Mathf.Max(1, finalPrice);
return Mathf.Max(GameEntry.Luban.Global.ShopMinPrice, finalPrice);
}
private async UniTask<DisplayItemRawData> ApplyGoodsPurchaseAsync(ShopGoodsSelection goods)
{
if (goods == null || Player == null)
{
return null;
}
if (goods.GoodsType == GoodsType.Prop)
{
PropConfig propConfig = GameEntry.Luban.Get<PropConfig>(goods.PropId);
if (propConfig.Modifiers == null || propConfig.Modifiers.Count == 0)
{
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Prop modifiers are empty");
return null;
}
Player.AddProp(new PropItem(propConfig));
_player.AddProp(new PropItem(propConfig));
return new DisplayItemRawData
{
IconAssetName = propConfig.Sprite,
@@ -327,7 +292,7 @@ namespace SepCore.UI
if (goods.GoodsType == GoodsType.Weapon)
{
if (Player.Weapons != null && Player.Weapons.Count >= Player.WeaponCapacity)
if (_player.Weapons.Count >= _player.WeaponCapacity)
{
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Weapon capacity is full.");
return null;
@@ -335,9 +300,9 @@ namespace SepCore.UI
WeaponConfig weaponConfig = GameEntry.Luban.Get<WeaponConfig>((int)goods.WeaponType);
int id = GameEntry.Entity.NextId();
var weaponData = EntityDataFactory.Create(id, weaponConfig, Player.Id, CampType.Player);
var weaponData = EntityDataFactory.Create(id, weaponConfig, _player.Id, CampType.Player);
WeaponBase weapon = await Player.AddWeaponAsync(weaponData);
WeaponBase weapon = await _player.AddWeaponAsync(weaponData);
if (weapon == null)
{
Log.Warning(
@@ -358,7 +323,7 @@ namespace SepCore.UI
public bool TryRecycleWeapon(int argsIndex, int argsPrice)
{
var weapons = Player.Weapons;
var weapons = _player.Weapons;
if (weapons.Count <= 1)
{
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Can not recycle the last weapon.");
@@ -368,19 +333,19 @@ namespace SepCore.UI
var weapon = weapons[argsIndex];
int recyclePrice = Mathf.Max(0, argsPrice);
if (!Player.RemoveWeapon(weapon))
if (!_player.RemoveWeapon(weapon))
{
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Remove weapon failed.");
return false;
}
Player.Coin += recyclePrice;
_player.Coin += recyclePrice;
return true;
}
public int CalculateRecyclePrice(int basePrice)
{
return Mathf.FloorToInt(basePrice * WeaponRecycleRate);
return Mathf.FloorToInt(basePrice * (GameEntry.Luban.Global.WeaponRecycleRate / 1000f));
}
}
}