第一批重构,主要是物理迁移脚本和部分命名空间的调整

- 结构上迁移文件,将脚本分成 Base/Runtime/Presentation/Procedure/Editor 五大文件夹
- 调整 DataTable 和 Definition 下脚本的命名空间
- 依据 UI-5层架构设计规范 拆分原先的 UI 到 Runtime 和 Presentation 下
This commit is contained in:
SepComet
2026-06-02 19:12:34 +08:00
parent 56f74eee9a
commit 34c0784190
655 changed files with 1272 additions and 516 deletions
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e71c46052b43081479b67b14e2e449d3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using SepCore.Definition;
using UnityEngine;
namespace UI
{
public class DisplayItemInfoFormRawData
{
public int Index;
public string IconAssetName;
public string Title;
public string TypeText;
public ItemRarity Rarity;
public string Description;
public int Price;
public bool IsWeapon;
public Vector3 TargetPos;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 012ded06be40bcf48b3e79ce89df4e2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,75 @@
using SepCore.Definition;
using Entity;
using CustomUtility;
using Entity.Weapon;
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,8 @@
fileFormatVersion: 2
guid: a3d54de71bcad264da3c1ca92ff54e4f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 30e2cee5e6a424c42a01db1b7e582a8a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using SepCore.DataTable;
namespace UI
{
public class LevelUpFormRawData
{
public List<DRLevelUpReward> Rewards;
public int RefreshPrice;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a3ece8e2fb75f424cb4885d4344434d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,194 @@
using System.Collections.Generic;
using System.Linq;
using SepCore.DataTable;
using SepCore.Definition;
using Entity;
using Procedure;
using UnityEngine;
using UnityGameFramework.Runtime;
using CustomUtility;
using GameFramework.DataTable;
namespace UI
{
internal class LevelUpFormUseCase : IUIUseCase
{
private const int BaseRefreshPrice = 2;
private readonly GameStateLevelUp _gameStateLevelUp;
private readonly DRLevelUpReward[] _allRewards;
private readonly IDataTable<DRLevelRarity> _levelRarityTable;
private readonly Dictionary<ItemRarity, List<DRLevelUpReward>> _rewardsByRarity = new();
private readonly List<DRLevelUpReward> _validRewards = new();
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();
_levelRarityTable = GameEntry.DataTable.GetDataTable<DRLevelRarity>();
BuildRewardRarityPools();
}
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);
int currentLevel = _player != null ? _player.CurrentLevel : 1;
for (int i = 0; i < finalCount; i++)
{
ItemRarity rarity = RarityUtility.SelectRarityForLevel(_levelRarityTable, currentLevel);
DRLevelUpReward reward = PickRewardByRarity(rarity);
if (reward == null)
{
Log.Warning("LevelUpFormUseCase::BuildModel(): No available reward for selection.");
break;
}
selections.Add(reward);
}
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, reward.Rarity, reward.Title, reward.IconAssetName));
}
private void BuildRewardRarityPools()
{
_rewardsByRarity.Clear();
_validRewards.Clear();
if (_allRewards == null) return;
foreach (DRLevelUpReward reward in _allRewards)
{
if (reward == null)
{
continue;
}
ItemRarity rarity = reward.Rarity;
_validRewards.Add(reward);
if (!_rewardsByRarity.TryGetValue(rarity, out List<DRLevelUpReward> list))
{
list = new List<DRLevelUpReward>();
_rewardsByRarity.Add(rarity, list);
}
list.Add(reward);
}
}
private DRLevelUpReward PickRewardByRarity(ItemRarity rarity)
{
if (_rewardsByRarity.TryGetValue(rarity, out List<DRLevelUpReward> list) && list.Count > 0)
{
return list[Random.Range(0, list.Count)];
}
if (_validRewards.Count > 0)
{
return _validRewards[Random.Range(0, _validRewards.Count)];
}
return null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19d2fa806b03510498d072c5b8710bcb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c3ed3acef12ff8d40935205a69fb63ca
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using SepCore.Definition;
using Entity.Weapon;
namespace UI
{
public class ShopFormRawData
{
public int CurrentLevel;
public int RefreshPrice;
public int PlayerCoin;
public List<GoodsItemContext> GoodsItems;
public IReadOnlyList<PropItem> PropItems;
public int PropMaxCount = -1;
public IReadOnlyList<WeaponBase> WeaponItems;
public int WeaponMaxCount = -1;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 533bff5f9d3bc7847af44dcd1cdffba3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,445 @@
using System.Collections.Generic;
using System.Linq;
using SepCore.DataTable;
using SepCore.Definition;
using Entity;
using Entity.EntityData;
using CustomUtility;
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 IDataTable<DRLevelRarity> _levelRarityTable;
private readonly List<DRGoods> _validGoods = new();
private readonly Dictionary<ItemRarity, List<DRGoods>> _goodsByRarity = new();
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>();
_levelRarityTable = GameEntry.DataTable.GetDataTable<DRLevelRarity>();
BuildGoodsRarityPools();
}
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;
}
DisplayItemContext displayItem = ApplyGoodsPurchase(selection);
if (displayItem == null)
{
return null;
}
player.Coin -= selection.Price;
_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();
int currentLevel = _procedureGame != null ? _procedureGame.CurrentLevel : 1;
for (int i = 0; i < finalCount; i++)
{
ItemRarity rarity = RarityUtility.SelectRarityForLevel(_levelRarityTable, currentLevel);
DRGoods drGoods = PickGoodsByRarity(rarity);
if (drGoods == null)
{
Log.Warning("ShopFormUseCase::BuildRandomGoodsItems: No available goods for selection.");
break;
}
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 void BuildGoodsRarityPools()
{
_goodsByRarity.Clear();
_validGoods.Clear();
if (_allGoods == null) return;
foreach (DRGoods goods in _allGoods)
{
if (goods == null) continue;
if (!TryGetGoodsRarity(goods, out ItemRarity rarity))
{
continue;
}
_validGoods.Add(goods);
if (!_goodsByRarity.TryGetValue(rarity, out List<DRGoods> list))
{
list = new List<DRGoods>();
_goodsByRarity.Add(rarity, list);
}
list.Add(goods);
}
}
private DRGoods PickGoodsByRarity(ItemRarity rarity)
{
if (_goodsByRarity.TryGetValue(rarity, out List<DRGoods> list) && list.Count > 0)
{
return list[Random.Range(0, list.Count)];
}
if (_validGoods.Count > 0)
{
return _validGoods[Random.Range(0, _validGoods.Count)];
}
return null;
}
private bool TryGetGoodsRarity(DRGoods goods, out ItemRarity rarity)
{
rarity = ItemRarity.None;
if (goods == null)
{
return false;
}
if (goods.GoodsType == GoodsType.Prop)
{
DRProp drProp = _propDataTable != null ? _propDataTable.GetDataRow(goods.GoodsTypeId) : null;
if (drProp == null)
{
Log.Warning($"ShopFormUseCase::TryGetGoodsRarity: Missing DRProp, id = {goods.GoodsTypeId}");
return false;
}
rarity = drProp.Rarity;
return true;
}
if (goods.GoodsType == GoodsType.Weapon)
{
DRWeapon drWeapon = _weaponDataTable != null ? _weaponDataTable.GetDataRow(goods.GoodsTypeId) : null;
if (drWeapon == null)
{
Log.Warning($"ShopFormUseCase::TryGetGoodsRarity: Missing DRWeapon, id = {goods.GoodsTypeId}");
return false;
}
rarity = drWeapon.Rarity;
return true;
}
Log.Warning($"ShopFormUseCase::TryGetGoodsRarity: Unsupported goods type = {goods.GoodsType}");
return false;
}
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
};
}
if (goods.GoodsType == GoodsType.Weapon)
{
if (Player.Weapons != null && Player.Weapons.Count >= Player.WeaponCapacity)
{
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Weapon capacity is full.");
return null;
}
DRWeapon drWeapon = _weaponDataTable != null ? _weaponDataTable.GetDataRow(goods.GoodsTypeId) : null;
if (drWeapon == null)
{
Log.Warning($"ShopFormUseCase::ApplyGoodsPurchase: Missing DRWeapon, id = {goods.GoodsTypeId}");
return null;
}
var weaponData = CreateWeaponData(goods.GoodsTypeId);
if (weaponData == null)
{
Log.Warning(
$"ShopFormUseCase::ApplyGoodsPurchase: Unsupported weapon type id = {goods.GoodsTypeId}");
return null;
}
GameEntry.Entity.ShowWeapon(weaponData);
return new DisplayItemContext
{
IconAssetName = drWeapon.IconAssetName,
Rarity = drWeapon.Rarity,
IsWeapon = true
};
}
return null;
}
private WeaponData CreateWeaponData(int weaponTypeId)
{
int entityId = GameEntry.Entity.GenerateSerialId();
int ownerId = Player.Id;
CampType ownerCamp = CampType.Player;
WeaponType weaponType = (WeaponType)weaponTypeId;
switch (weaponType)
{
case WeaponType.WeaponKnife:
return new WeaponKnifeData(entityId, ownerId, ownerCamp);
case WeaponType.WeaponHandgun:
return new WeaponHandgunData(entityId, ownerId, ownerCamp);
case WeaponType.WeaponSlash:
return new WeaponSlashData(entityId, ownerId, ownerCamp);
case WeaponType.WeaponLightning:
return new WeaponLightningData(entityId, ownerId, ownerCamp);
case WeaponType.WeaponLance:
return new WeaponLanceData(entityId, ownerId, ownerCamp);
default:
return null;
}
}
public bool TryRecycleWeapon(int argsIndex, int argsPrice)
{
Player player = Player;
if (player == null)
{
return false;
}
var weapons = player.Weapons;
if (weapons == null || weapons.Count <= 1)
{
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Can not recycle the last weapon.");
return false;
}
if (argsIndex < 0 || argsIndex >= weapons.Count)
{
Log.Warning($"ShopFormUseCase::TryRecycleWeapon: Invalid weapon index = {argsIndex}");
return false;
}
var weapon = weapons[argsIndex];
if (weapon == null)
{
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Weapon is null.");
return false;
}
int recyclePrice = Mathf.Max(0, argsPrice);
if (!player.RemoveWeapon(weapon))
{
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Remove weapon failed.");
return false;
}
player.Coin += recyclePrice;
return true;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e973748d1bd846349d42e219a80f07c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: