重构
- 重构 `RepoItem`,统一点击事件
- 重构 `PlayerInventoryComponent`:
- `PlayerInventoryStateStore`:状态与库存写模型
- `PlayerInventoryTowerAssemblyService`:炮塔组装域
- `PlayerInventoryTowerRosterService`:编队/耐久域
- `InventoryParticipantUtility`/`InventoryCloneUtility`/`InventorySeedUtility`:通用工具类
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 584eceb75fd347b7952c8117c8738fd4
|
||||
timeCreated: 1772632229
|
||||
@@ -0,0 +1,128 @@
|
||||
using GeometryTD.CustomUtility;
|
||||
using GeometryTD.Definition;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
public class PlayerInventoryComponent : GameFrameworkComponent
|
||||
{
|
||||
private const int MaxParticipantTowerCount = 4;
|
||||
|
||||
private PlayerInventoryState _state;
|
||||
private PlayerInventoryQueryModel _queryModel;
|
||||
private PlayerInventoryCommandModel _commandModel;
|
||||
private PlayerInventoryTowerRosterService _towerRosterService;
|
||||
private PlayerInventoryTowerAssemblyService _towerAssemblyService;
|
||||
|
||||
public int Gold
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _queryModel.Gold;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
EnsureServices();
|
||||
_commandModel.Initialize(InventorySeedUtility.CreateSampleInventory(), MaxParticipantTowerCount);
|
||||
|
||||
BackpackInventoryData inventory = _queryModel.Inventory;
|
||||
Log.Info(
|
||||
"PlayerInventory initialized. Gold={0}, Tower={1}, Muzzle={2}, Bearing={3}, Base={4}.",
|
||||
inventory.Gold,
|
||||
inventory.Towers.Count,
|
||||
inventory.MuzzleComponents.Count,
|
||||
inventory.BearingComponents.Count,
|
||||
inventory.BaseComponents.Count);
|
||||
}
|
||||
|
||||
public BackpackInventoryData GetInventorySnapshot()
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _queryModel.GetSnapshot();
|
||||
}
|
||||
|
||||
public void MergeInventory(BackpackInventoryData gainedInventory)
|
||||
{
|
||||
EnsureInitialized();
|
||||
PlayerInventoryMergeSummary summary = _commandModel.MergeInventory(gainedInventory);
|
||||
if (!summary.HasAnyGain)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info(
|
||||
"PlayerInventory merged reward. Gold+{0}, Tower+{1}, Muzzle+{2}, Bearing+{3}, Base+{4}.",
|
||||
summary.GainedGold,
|
||||
summary.GainedTowerCount,
|
||||
summary.GainedMuzzleCount,
|
||||
summary.GainedBearingCount,
|
||||
summary.GainedBaseCount);
|
||||
}
|
||||
|
||||
public bool TryConsumeGold(int costGold)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _commandModel.TryConsumeGold(costGold);
|
||||
}
|
||||
|
||||
public void AddGold(int gainGold)
|
||||
{
|
||||
EnsureInitialized();
|
||||
_commandModel.AddGold(gainGold);
|
||||
}
|
||||
|
||||
public bool TryAddParticipantTower(long towerInstanceId, int maxCount = 4)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _towerRosterService.TryAddParticipantTower(towerInstanceId, maxCount);
|
||||
}
|
||||
|
||||
public bool TryRemoveParticipantTower(long towerInstanceId)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _towerRosterService.TryRemoveParticipantTower(towerInstanceId);
|
||||
}
|
||||
|
||||
public bool TryAssembleTower(
|
||||
long muzzleInstanceId,
|
||||
long bearingInstanceId,
|
||||
long baseInstanceId,
|
||||
out TowerItemData assembledTower)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _towerAssemblyService.TryAssembleTower(
|
||||
muzzleInstanceId,
|
||||
bearingInstanceId,
|
||||
baseInstanceId,
|
||||
out assembledTower);
|
||||
}
|
||||
|
||||
public int ReduceAllTowerEndurance(float enduranceLoss)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _towerRosterService.ReduceAllTowerEndurance(enduranceLoss);
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (_queryModel.IsInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnInit();
|
||||
}
|
||||
|
||||
private void EnsureServices()
|
||||
{
|
||||
_state ??= new PlayerInventoryState();
|
||||
_queryModel ??= new PlayerInventoryQueryModel(_state);
|
||||
_commandModel ??= new PlayerInventoryCommandModel(_state);
|
||||
_towerRosterService ??= new PlayerInventoryTowerRosterService(_queryModel, MaxParticipantTowerCount);
|
||||
_towerAssemblyService ??= new PlayerInventoryTowerAssemblyService(_queryModel, _commandModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.CustomUtility;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal struct PlayerInventoryMergeSummary
|
||||
{
|
||||
public int GainedGold;
|
||||
public int GainedMuzzleCount;
|
||||
public int GainedBearingCount;
|
||||
public int GainedBaseCount;
|
||||
public int GainedTowerCount;
|
||||
|
||||
public bool HasAnyGain =>
|
||||
GainedGold > 0 || GainedMuzzleCount > 0 || GainedBearingCount > 0 || GainedBaseCount > 0 ||
|
||||
GainedTowerCount > 0;
|
||||
}
|
||||
|
||||
internal sealed class PlayerInventoryState
|
||||
{
|
||||
public BackpackInventoryData Inventory = new BackpackInventoryData();
|
||||
public long NextInstanceId = 1;
|
||||
public bool IsInitialized;
|
||||
}
|
||||
|
||||
internal sealed class PlayerInventoryQueryModel
|
||||
{
|
||||
private readonly PlayerInventoryState _state;
|
||||
|
||||
public PlayerInventoryQueryModel(PlayerInventoryState state)
|
||||
{
|
||||
_state = state;
|
||||
}
|
||||
|
||||
public BackpackInventoryData Inventory => _state.Inventory;
|
||||
public bool IsInitialized => _state.IsInitialized;
|
||||
public int Gold => _state.Inventory.Gold;
|
||||
|
||||
public BackpackInventoryData GetSnapshot()
|
||||
{
|
||||
return InventoryCloneUtility.CloneInventory(_state.Inventory);
|
||||
}
|
||||
|
||||
public bool TryGetTowerById(long towerInstanceId, out TowerItemData tower)
|
||||
{
|
||||
return InventoryParticipantUtility.TryGetTowerById(_state.Inventory, towerInstanceId, out tower);
|
||||
}
|
||||
|
||||
public bool TryGetComponentById<TComp>(List<TComp> components, long instanceId, out TComp result)
|
||||
where TComp : TowerCompItemData
|
||||
{
|
||||
result = null;
|
||||
if (components == null || instanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < components.Count; i++)
|
||||
{
|
||||
TComp component = components[i];
|
||||
if (component != null && component.InstanceId == instanceId)
|
||||
{
|
||||
result = component;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlayerInventoryCommandModel
|
||||
{
|
||||
private readonly PlayerInventoryState _state;
|
||||
|
||||
public PlayerInventoryCommandModel(PlayerInventoryState state)
|
||||
{
|
||||
_state = state;
|
||||
}
|
||||
|
||||
public void Initialize(BackpackInventoryData sourceInventory, int maxParticipantTowerCount)
|
||||
{
|
||||
_state.Inventory = InventoryCloneUtility.CloneInventory(sourceInventory);
|
||||
InventoryParticipantUtility.NormalizeParticipantState(_state.Inventory, maxParticipantTowerCount);
|
||||
RebuildNextInstanceId();
|
||||
_state.IsInitialized = true;
|
||||
}
|
||||
|
||||
public PlayerInventoryMergeSummary MergeInventory(BackpackInventoryData gainedInventory)
|
||||
{
|
||||
PlayerInventoryMergeSummary summary = default;
|
||||
if (gainedInventory == null)
|
||||
{
|
||||
return summary;
|
||||
}
|
||||
|
||||
summary.GainedGold = Mathf.Max(0, gainedInventory.Gold);
|
||||
if (summary.GainedGold > 0)
|
||||
{
|
||||
_state.Inventory.Gold += summary.GainedGold;
|
||||
}
|
||||
|
||||
if (gainedInventory.MuzzleComponents != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.MuzzleComponents.Count; i++)
|
||||
{
|
||||
MuzzleCompItemData source = gainedInventory.MuzzleComponents[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MuzzleCompItemData cloned = InventoryCloneUtility.CloneMuzzleComp(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_state.Inventory.MuzzleComponents.Add(cloned);
|
||||
summary.GainedMuzzleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gainedInventory.BearingComponents != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.BearingComponents.Count; i++)
|
||||
{
|
||||
BearingCompItemData source = gainedInventory.BearingComponents[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BearingCompItemData cloned = InventoryCloneUtility.CloneBearingComp(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_state.Inventory.BearingComponents.Add(cloned);
|
||||
summary.GainedBearingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gainedInventory.BaseComponents != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.BaseComponents.Count; i++)
|
||||
{
|
||||
BaseCompItemData source = gainedInventory.BaseComponents[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BaseCompItemData cloned = InventoryCloneUtility.CloneBaseComp(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_state.Inventory.BaseComponents.Add(cloned);
|
||||
summary.GainedBaseCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gainedInventory.Towers != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData source = gainedInventory.Towers[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TowerItemData cloned = InventoryCloneUtility.CloneTower(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_state.Inventory.Towers.Add(cloned);
|
||||
summary.GainedTowerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
public bool TryConsumeGold(int costGold)
|
||||
{
|
||||
int resolvedCost = Mathf.Max(0, costGold);
|
||||
if (resolvedCost <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_state.Inventory.Gold < resolvedCost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_state.Inventory.Gold -= resolvedCost;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddGold(int gainGold)
|
||||
{
|
||||
int resolvedGain = Mathf.Max(0, gainGold);
|
||||
if (resolvedGain <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_state.Inventory.Gold += resolvedGain;
|
||||
}
|
||||
|
||||
public long AllocateInstanceId()
|
||||
{
|
||||
if (_state.NextInstanceId < 1)
|
||||
{
|
||||
_state.NextInstanceId = 1;
|
||||
}
|
||||
|
||||
return _state.NextInstanceId++;
|
||||
}
|
||||
|
||||
private void RebuildNextInstanceId()
|
||||
{
|
||||
long maxInstanceId = 0;
|
||||
BackpackInventoryData inventory = _state.Inventory;
|
||||
if (inventory.Towers != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData item = inventory.Towers[i];
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.MuzzleComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.MuzzleComponents.Count; i++)
|
||||
{
|
||||
MuzzleCompItemData item = inventory.MuzzleComponents[i];
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.BearingComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.BearingComponents.Count; i++)
|
||||
{
|
||||
BearingCompItemData item = inventory.BearingComponents[i];
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory.BaseComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.BaseComponents.Count; i++)
|
||||
{
|
||||
BaseCompItemData item = inventory.BaseComponents[i];
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_state.NextInstanceId = Math.Max(1, maxInstanceId + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aa101c4dd7c30845b0a84b2a04f9b9e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.DataTable;
|
||||
using GeometryTD.CustomUtility;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class PlayerInventoryTowerAssemblyService
|
||||
{
|
||||
private const int TowerLevelCount = 5;
|
||||
|
||||
private readonly PlayerInventoryQueryModel _queryModel;
|
||||
private readonly PlayerInventoryCommandModel _commandModel;
|
||||
private IDataTable<DRMuzzleComp> _drMuzzleComp;
|
||||
private IDataTable<DRBearingComp> _drBearingComp;
|
||||
private IDataTable<DRBaseComp> _drBaseComp;
|
||||
|
||||
public PlayerInventoryTowerAssemblyService(
|
||||
PlayerInventoryQueryModel queryModel,
|
||||
PlayerInventoryCommandModel commandModel)
|
||||
{
|
||||
_queryModel = queryModel;
|
||||
_commandModel = commandModel;
|
||||
}
|
||||
|
||||
public bool TryAssembleTower(
|
||||
long muzzleInstanceId,
|
||||
long bearingInstanceId,
|
||||
long baseInstanceId,
|
||||
out TowerItemData assembledTower)
|
||||
{
|
||||
assembledTower = null;
|
||||
BackpackInventoryData inventory = _queryModel.Inventory;
|
||||
if (muzzleInstanceId <= 0 || bearingInstanceId <= 0 || baseInstanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_queryModel.TryGetComponentById(inventory.MuzzleComponents, muzzleInstanceId, out MuzzleCompItemData muzzleComp) ||
|
||||
!_queryModel.TryGetComponentById(inventory.BearingComponents, bearingInstanceId, out BearingCompItemData bearingComp) ||
|
||||
!_queryModel.TryGetComponentById(inventory.BaseComponents, baseInstanceId, out BaseCompItemData baseComp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (muzzleComp.IsAssembledIntoTower || bearingComp.IsAssembledIntoTower || baseComp.IsAssembledIntoTower)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryBuildTowerStats(muzzleComp, bearingComp, baseComp, out TowerStatsData stats))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long towerInstanceId = _commandModel.AllocateInstanceId();
|
||||
TowerItemData tower = new TowerItemData
|
||||
{
|
||||
InstanceId = towerInstanceId,
|
||||
Name = $"组装防御塔-{towerInstanceId}",
|
||||
Rarity = ResolveAverageRarity(muzzleComp.Rarity, bearingComp.Rarity, baseComp.Rarity),
|
||||
MuzzleComponentInstanceId = muzzleComp.InstanceId,
|
||||
BearingComponentInstanceId = bearingComp.InstanceId,
|
||||
BaseComponentInstanceId = baseComp.InstanceId,
|
||||
Stats = stats
|
||||
};
|
||||
|
||||
muzzleComp.IsAssembledIntoTower = true;
|
||||
bearingComp.IsAssembledIntoTower = true;
|
||||
baseComp.IsAssembledIntoTower = true;
|
||||
inventory.Towers.Add(tower);
|
||||
assembledTower = InventoryCloneUtility.CloneTower(tower);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryBuildTowerStats(
|
||||
MuzzleCompItemData muzzleComp,
|
||||
BearingCompItemData bearingComp,
|
||||
BaseCompItemData baseComp,
|
||||
out TowerStatsData stats)
|
||||
{
|
||||
stats = null;
|
||||
if (muzzleComp == null || bearingComp == null || baseComp == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DRMuzzleComp muzzleConfig = EnsureMuzzleTable()?.GetDataRow(muzzleComp.ConfigId);
|
||||
DRBearingComp bearingConfig = EnsureBearingTable()?.GetDataRow(bearingComp.ConfigId);
|
||||
DRBaseComp baseConfig = EnsureBaseTable()?.GetDataRow(baseComp.ConfigId);
|
||||
if (muzzleConfig == null || bearingConfig == null || baseConfig == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
stats = new TowerStatsData
|
||||
{
|
||||
AttackDamage = BuildLevelIntArray(muzzleComp.AttackDamage, muzzleComp.Rarity, muzzleConfig.AttackDamagePerLevel),
|
||||
DamageRandomRate = Mathf.Max(0f, muzzleComp.DamageRandomRate),
|
||||
RotateSpeed = BuildLevelFloatArray(bearingComp.RotateSpeed, bearingComp.Rarity, bearingConfig.RotateSpeedPerLevel),
|
||||
AttackRange = BuildLevelFloatArray(bearingComp.AttackRange, bearingComp.Rarity, bearingConfig.AttackRangePerLevel),
|
||||
AttackSpeed = BuildLevelFloatArray(baseComp.AttackSpeed, baseComp.Rarity, baseConfig.AttackSpeedPerLevel),
|
||||
AttackMethodType = muzzleComp.AttackMethodType,
|
||||
AttackPropertyType = baseComp.AttackPropertyType,
|
||||
Tags = MergeTags(muzzleComp.Tags, bearingComp.Tags, baseComp.Tags)
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int[] BuildLevelIntArray(int[] rarityBaseArray, RarityType rarity, int perLevel)
|
||||
{
|
||||
int baseValue = ResolveRarityBaseValue(rarityBaseArray, rarity);
|
||||
int[] values = new int[TowerLevelCount];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] = baseValue + perLevel * i;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static float[] BuildLevelFloatArray(float[] rarityBaseArray, RarityType rarity, float perLevel)
|
||||
{
|
||||
float baseValue = ResolveRarityBaseValue(rarityBaseArray, rarity);
|
||||
float[] values = new float[TowerLevelCount];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] = baseValue + perLevel * i;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static int ResolveRarityBaseValue(int[] rarityBaseArray, RarityType rarity)
|
||||
{
|
||||
if (rarityBaseArray == null || rarityBaseArray.Length <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rarityIndex = Mathf.Clamp((int)rarity - 1, 0, rarityBaseArray.Length - 1);
|
||||
return rarityBaseArray[rarityIndex];
|
||||
}
|
||||
|
||||
private static float ResolveRarityBaseValue(float[] rarityBaseArray, RarityType rarity)
|
||||
{
|
||||
if (rarityBaseArray == null || rarityBaseArray.Length <= 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
int rarityIndex = Mathf.Clamp((int)rarity - 1, 0, rarityBaseArray.Length - 1);
|
||||
return rarityBaseArray[rarityIndex];
|
||||
}
|
||||
|
||||
private static TagType[] MergeTags(params TagType[][] sources)
|
||||
{
|
||||
HashSet<TagType> uniqueTags = new HashSet<TagType>();
|
||||
if (sources != null)
|
||||
{
|
||||
for (int i = 0; i < sources.Length; i++)
|
||||
{
|
||||
TagType[] tags = sources[i];
|
||||
if (tags == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < tags.Length; j++)
|
||||
{
|
||||
uniqueTags.Add(tags[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TagType[] mergedTags = new TagType[uniqueTags.Count];
|
||||
uniqueTags.CopyTo(mergedTags);
|
||||
return mergedTags;
|
||||
}
|
||||
|
||||
private static RarityType ResolveAverageRarity(RarityType muzzleRarity, RarityType bearingRarity, RarityType baseRarity)
|
||||
{
|
||||
float avg = ((int)muzzleRarity + (int)bearingRarity + (int)baseRarity) / 3f;
|
||||
int rounded = Mathf.RoundToInt(avg);
|
||||
int clamped = Mathf.Clamp(rounded, (int)RarityType.White, (int)RarityType.Red);
|
||||
return (RarityType)clamped;
|
||||
}
|
||||
|
||||
private IDataTable<DRMuzzleComp> EnsureMuzzleTable()
|
||||
{
|
||||
_drMuzzleComp ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
||||
return _drMuzzleComp;
|
||||
}
|
||||
|
||||
private IDataTable<DRBearingComp> EnsureBearingTable()
|
||||
{
|
||||
_drBearingComp ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
||||
return _drBearingComp;
|
||||
}
|
||||
|
||||
private IDataTable<DRBaseComp> EnsureBaseTable()
|
||||
{
|
||||
_drBaseComp ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
||||
return _drBaseComp;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b20e5ad5952075449bc89c483cd4723b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.CustomUtility;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class PlayerInventoryTowerRosterService
|
||||
{
|
||||
private readonly PlayerInventoryQueryModel _queryModel;
|
||||
private readonly int _maxParticipantTowerCount;
|
||||
|
||||
public PlayerInventoryTowerRosterService(PlayerInventoryQueryModel queryModel, int maxParticipantTowerCount)
|
||||
{
|
||||
_queryModel = queryModel;
|
||||
_maxParticipantTowerCount = Mathf.Max(1, maxParticipantTowerCount);
|
||||
}
|
||||
|
||||
public bool TryAddParticipantTower(long towerInstanceId, int maxCount)
|
||||
{
|
||||
int resolvedMaxCount = Mathf.Max(1, maxCount);
|
||||
resolvedMaxCount = Mathf.Min(resolvedMaxCount, _maxParticipantTowerCount);
|
||||
return InventoryParticipantUtility.TryAddParticipantTower(
|
||||
_queryModel.Inventory,
|
||||
towerInstanceId,
|
||||
resolvedMaxCount);
|
||||
}
|
||||
|
||||
public bool TryRemoveParticipantTower(long towerInstanceId)
|
||||
{
|
||||
return InventoryParticipantUtility.TryRemoveParticipantTower(
|
||||
_queryModel.Inventory,
|
||||
towerInstanceId,
|
||||
_maxParticipantTowerCount);
|
||||
}
|
||||
|
||||
public int ReduceAllTowerEndurance(float enduranceLoss)
|
||||
{
|
||||
float resolvedLoss = Mathf.Max(0f, enduranceLoss);
|
||||
BackpackInventoryData inventory = _queryModel.Inventory;
|
||||
if (resolvedLoss <= 0f || inventory.Towers == null || inventory.Towers.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Dictionary<long, MuzzleCompItemData> muzzleMap = BuildComponentMap(inventory.MuzzleComponents);
|
||||
Dictionary<long, BearingCompItemData> bearingMap = BuildComponentMap(inventory.BearingComponents);
|
||||
Dictionary<long, BaseCompItemData> baseMap = BuildComponentMap(inventory.BaseComponents);
|
||||
|
||||
int affectedCount = 0;
|
||||
for (int i = 0; i < inventory.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData tower = inventory.Towers[i];
|
||||
if (tower == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool towerAffected = false;
|
||||
if (muzzleMap.TryGetValue(tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzleComp))
|
||||
{
|
||||
towerAffected |= TryReduceComponentEndurance(muzzleComp, resolvedLoss);
|
||||
}
|
||||
|
||||
if (bearingMap.TryGetValue(tower.BearingComponentInstanceId, out BearingCompItemData bearingComp))
|
||||
{
|
||||
towerAffected |= TryReduceComponentEndurance(bearingComp, resolvedLoss);
|
||||
}
|
||||
|
||||
if (baseMap.TryGetValue(tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
|
||||
{
|
||||
towerAffected |= TryReduceComponentEndurance(baseComp, resolvedLoss);
|
||||
}
|
||||
|
||||
if (towerAffected)
|
||||
{
|
||||
affectedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return affectedCount;
|
||||
}
|
||||
|
||||
private static bool TryReduceComponentEndurance(TowerCompItemData component, float enduranceLoss)
|
||||
{
|
||||
if (component == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float originalEndurance = component.Endurance;
|
||||
float nextEndurance = Mathf.Clamp(originalEndurance - Mathf.Max(0f, enduranceLoss), 0f, 100f);
|
||||
if (nextEndurance >= originalEndurance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
component.Endurance = nextEndurance;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Dictionary<long, TComp> BuildComponentMap<TComp>(List<TComp> components)
|
||||
where TComp : TowerCompItemData
|
||||
{
|
||||
Dictionary<long, TComp> map = new Dictionary<long, TComp>();
|
||||
if (components == null || components.Count <= 0)
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
||||
for (int i = 0; i < components.Count; i++)
|
||||
{
|
||||
TComp component = components[i];
|
||||
if (component == null || component.InstanceId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
map[component.InstanceId] = component;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45c950a847f55a04f9f780218f735d42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,856 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.DataTable;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using GeometryTD.UI;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
public class PlayerInventoryComponent : GameFrameworkComponent
|
||||
{
|
||||
private const int TowerLevelCount = 5;
|
||||
private const int MaxParticipantTowerCount = 4;
|
||||
private BackpackInventoryData _inventory = new BackpackInventoryData();
|
||||
private long _nextInstanceId = 1;
|
||||
private bool _initialized;
|
||||
private IDataTable<DRMuzzleComp> _drMuzzleComp;
|
||||
private IDataTable<DRBearingComp> _drBearingComp;
|
||||
private IDataTable<DRBaseComp> _drBaseComp;
|
||||
|
||||
public int Gold
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _inventory.Gold;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
_inventory = CloneInventory(RepoFormUseCase.SampleInventory());
|
||||
NormalizeParticipantState();
|
||||
RebuildNextInstanceId();
|
||||
_initialized = true;
|
||||
Log.Info(
|
||||
"PlayerInventory initialized. Gold={0}, Tower={1}, Muzzle={2}, Bearing={3}, Base={4}.",
|
||||
_inventory.Gold,
|
||||
_inventory.Towers.Count,
|
||||
_inventory.MuzzleComponents.Count,
|
||||
_inventory.BearingComponents.Count,
|
||||
_inventory.BaseComponents.Count);
|
||||
}
|
||||
|
||||
public BackpackInventoryData GetInventorySnapshot()
|
||||
{
|
||||
EnsureInitialized();
|
||||
return CloneInventory(_inventory);
|
||||
}
|
||||
|
||||
public void MergeInventory(BackpackInventoryData gainedInventory)
|
||||
{
|
||||
EnsureInitialized();
|
||||
if (gainedInventory == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int gainedGold = Mathf.Max(0, gainedInventory.Gold);
|
||||
int gainedMuzzleCount = 0;
|
||||
int gainedBearingCount = 0;
|
||||
int gainedBaseCount = 0;
|
||||
int gainedTowerCount = 0;
|
||||
|
||||
if (gainedGold > 0)
|
||||
{
|
||||
_inventory.Gold += gainedGold;
|
||||
}
|
||||
|
||||
if (gainedInventory.MuzzleComponents != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.MuzzleComponents.Count; i++)
|
||||
{
|
||||
MuzzleCompItemData source = gainedInventory.MuzzleComponents[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MuzzleCompItemData cloned = CloneMuzzleComp(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_inventory.MuzzleComponents.Add(cloned);
|
||||
gainedMuzzleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gainedInventory.BearingComponents != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.BearingComponents.Count; i++)
|
||||
{
|
||||
BearingCompItemData source = gainedInventory.BearingComponents[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BearingCompItemData cloned = CloneBearingComp(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_inventory.BearingComponents.Add(cloned);
|
||||
gainedBearingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gainedInventory.BaseComponents != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.BaseComponents.Count; i++)
|
||||
{
|
||||
BaseCompItemData source = gainedInventory.BaseComponents[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BaseCompItemData cloned = CloneBaseComp(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_inventory.BaseComponents.Add(cloned);
|
||||
gainedBaseCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gainedInventory.Towers != null)
|
||||
{
|
||||
for (int i = 0; i < gainedInventory.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData source = gainedInventory.Towers[i];
|
||||
if (source == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TowerItemData cloned = CloneTower(source);
|
||||
cloned.InstanceId = AllocateInstanceId();
|
||||
_inventory.Towers.Add(cloned);
|
||||
gainedTowerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gainedGold > 0 || gainedMuzzleCount > 0 || gainedBearingCount > 0 || gainedBaseCount > 0 ||
|
||||
gainedTowerCount > 0)
|
||||
{
|
||||
Log.Info(
|
||||
"PlayerInventory merged reward. Gold+{0}, Tower+{1}, Muzzle+{2}, Bearing+{3}, Base+{4}.",
|
||||
gainedGold,
|
||||
gainedTowerCount,
|
||||
gainedMuzzleCount,
|
||||
gainedBearingCount,
|
||||
gainedBaseCount);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryConsumeGold(int costGold)
|
||||
{
|
||||
EnsureInitialized();
|
||||
int resolvedCost = Mathf.Max(0, costGold);
|
||||
if (resolvedCost <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_inventory.Gold < resolvedCost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_inventory.Gold -= resolvedCost;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddGold(int gainGold)
|
||||
{
|
||||
EnsureInitialized();
|
||||
int resolvedGain = Mathf.Max(0, gainGold);
|
||||
if (resolvedGain <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_inventory.Gold += resolvedGain;
|
||||
}
|
||||
|
||||
public bool TryAddParticipantTower(long towerInstanceId, int maxCount = 4)
|
||||
{
|
||||
EnsureInitialized();
|
||||
int resolvedMaxCount = Mathf.Max(1, maxCount);
|
||||
resolvedMaxCount = Mathf.Min(resolvedMaxCount, MaxParticipantTowerCount);
|
||||
if (!TryGetTowerById(towerInstanceId, out TowerItemData tower))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_inventory.ParticipantTowerInstanceIds ??= new List<long>();
|
||||
if (_inventory.ParticipantTowerInstanceIds.Contains(towerInstanceId))
|
||||
{
|
||||
tower.IsParticipatingInCombat = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_inventory.ParticipantTowerInstanceIds.Count >= resolvedMaxCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_inventory.ParticipantTowerInstanceIds.Add(towerInstanceId);
|
||||
tower.IsParticipatingInCombat = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryRemoveParticipantTower(long towerInstanceId)
|
||||
{
|
||||
EnsureInitialized();
|
||||
if (towerInstanceId <= 0 || _inventory.ParticipantTowerInstanceIds == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool removed = _inventory.ParticipantTowerInstanceIds.Remove(towerInstanceId);
|
||||
if (!removed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryGetTowerById(towerInstanceId, out TowerItemData tower))
|
||||
{
|
||||
tower.IsParticipatingInCombat = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAssembleTower(
|
||||
long muzzleInstanceId,
|
||||
long bearingInstanceId,
|
||||
long baseInstanceId,
|
||||
out TowerItemData assembledTower)
|
||||
{
|
||||
EnsureInitialized();
|
||||
assembledTower = null;
|
||||
if (muzzleInstanceId <= 0 || bearingInstanceId <= 0 || baseInstanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetComponentById(_inventory.MuzzleComponents, muzzleInstanceId,
|
||||
out MuzzleCompItemData muzzleComp) ||
|
||||
!TryGetComponentById(_inventory.BearingComponents, bearingInstanceId,
|
||||
out BearingCompItemData bearingComp) ||
|
||||
!TryGetComponentById(_inventory.BaseComponents, baseInstanceId, out BaseCompItemData baseComp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (muzzleComp.IsAssembledIntoTower || bearingComp.IsAssembledIntoTower || baseComp.IsAssembledIntoTower)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryBuildTowerStats(muzzleComp, bearingComp, baseComp, out TowerStatsData stats))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
long towerInstanceId = AllocateInstanceId();
|
||||
TowerItemData tower = new TowerItemData
|
||||
{
|
||||
InstanceId = towerInstanceId,
|
||||
Name = $"组装防御塔-{towerInstanceId}",
|
||||
Rarity = ResolveAverageRarity(muzzleComp.Rarity, bearingComp.Rarity, baseComp.Rarity),
|
||||
MuzzleComponentInstanceId = muzzleComp.InstanceId,
|
||||
BearingComponentInstanceId = bearingComp.InstanceId,
|
||||
BaseComponentInstanceId = baseComp.InstanceId,
|
||||
Stats = stats
|
||||
};
|
||||
|
||||
muzzleComp.IsAssembledIntoTower = true;
|
||||
bearingComp.IsAssembledIntoTower = true;
|
||||
baseComp.IsAssembledIntoTower = true;
|
||||
_inventory.Towers.Add(tower);
|
||||
assembledTower = CloneTower(tower);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int ReduceAllTowerEndurance(float enduranceLoss)
|
||||
{
|
||||
EnsureInitialized();
|
||||
float resolvedLoss = Mathf.Max(0f, enduranceLoss);
|
||||
if (resolvedLoss <= 0f || _inventory.Towers == null || _inventory.Towers.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Dictionary<long, MuzzleCompItemData> muzzleMap = BuildComponentMap(_inventory.MuzzleComponents);
|
||||
Dictionary<long, BearingCompItemData> bearingMap = BuildComponentMap(_inventory.BearingComponents);
|
||||
Dictionary<long, BaseCompItemData> baseMap = BuildComponentMap(_inventory.BaseComponents);
|
||||
|
||||
int affectedCount = 0;
|
||||
foreach (var tower in _inventory.Towers)
|
||||
{
|
||||
if (tower == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool towerAffected = false;
|
||||
if (muzzleMap.TryGetValue(tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzleComp))
|
||||
{
|
||||
towerAffected |= TryReduceComponentEndurance(muzzleComp, resolvedLoss);
|
||||
}
|
||||
|
||||
if (bearingMap.TryGetValue(tower.BearingComponentInstanceId, out BearingCompItemData bearingComp))
|
||||
{
|
||||
towerAffected |= TryReduceComponentEndurance(bearingComp, resolvedLoss);
|
||||
}
|
||||
|
||||
if (baseMap.TryGetValue(tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
|
||||
{
|
||||
towerAffected |= TryReduceComponentEndurance(baseComp, resolvedLoss);
|
||||
}
|
||||
|
||||
if (towerAffected)
|
||||
{
|
||||
affectedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return affectedCount;
|
||||
}
|
||||
|
||||
private static bool TryReduceComponentEndurance(TowerCompItemData component, float enduranceLoss)
|
||||
{
|
||||
if (component == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float originalEndurance = component.Endurance;
|
||||
float nextEndurance = Mathf.Clamp(originalEndurance - Mathf.Max(0f, enduranceLoss), 0f, 100f);
|
||||
if (nextEndurance >= originalEndurance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
component.Endurance = nextEndurance;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Dictionary<long, TComp> BuildComponentMap<TComp>(List<TComp> components)
|
||||
where TComp : TowerCompItemData
|
||||
{
|
||||
Dictionary<long, TComp> map = new Dictionary<long, TComp>();
|
||||
if (components == null || components.Count <= 0)
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
||||
foreach (var component in components)
|
||||
{
|
||||
if (component == null || component.InstanceId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
map[component.InstanceId] = component;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static bool TryGetComponentById<TComp>(List<TComp> components, long instanceId, out TComp result)
|
||||
where TComp : TowerCompItemData
|
||||
{
|
||||
result = null;
|
||||
if (components == null || instanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (TComp component in components)
|
||||
{
|
||||
if (component != null && component.InstanceId == instanceId)
|
||||
{
|
||||
result = component;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryGetTowerById(long towerInstanceId, out TowerItemData tower)
|
||||
{
|
||||
tower = null;
|
||||
if (towerInstanceId <= 0 || _inventory.Towers == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _inventory.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData candidate = _inventory.Towers[i];
|
||||
if (candidate != null && candidate.InstanceId == towerInstanceId)
|
||||
{
|
||||
tower = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryBuildTowerStats(
|
||||
MuzzleCompItemData muzzleComp,
|
||||
BearingCompItemData bearingComp,
|
||||
BaseCompItemData baseComp,
|
||||
out TowerStatsData stats)
|
||||
{
|
||||
stats = null;
|
||||
if (muzzleComp == null || bearingComp == null || baseComp == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DRMuzzleComp muzzleConfig = EnsureMuzzleTable()?.GetDataRow(muzzleComp.ConfigId);
|
||||
DRBearingComp bearingConfig = EnsureBearingTable()?.GetDataRow(bearingComp.ConfigId);
|
||||
DRBaseComp baseConfig = EnsureBaseTable()?.GetDataRow(baseComp.ConfigId);
|
||||
if (muzzleConfig == null || bearingConfig == null || baseConfig == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
stats = new TowerStatsData
|
||||
{
|
||||
AttackDamage = BuildLevelIntArray(muzzleComp.AttackDamage, muzzleComp.Rarity,
|
||||
muzzleConfig.AttackDamagePerLevel),
|
||||
DamageRandomRate = Mathf.Max(0f, muzzleComp.DamageRandomRate),
|
||||
RotateSpeed = BuildLevelFloatArray(bearingComp.RotateSpeed, bearingComp.Rarity,
|
||||
bearingConfig.RotateSpeedPerLevel),
|
||||
AttackRange = BuildLevelFloatArray(bearingComp.AttackRange, bearingComp.Rarity,
|
||||
bearingConfig.AttackRangePerLevel),
|
||||
AttackSpeed =
|
||||
BuildLevelFloatArray(baseComp.AttackSpeed, baseComp.Rarity, baseConfig.AttackSpeedPerLevel),
|
||||
AttackMethodType = muzzleComp.AttackMethodType,
|
||||
AttackPropertyType = baseComp.AttackPropertyType,
|
||||
Tags = MergeTags(muzzleComp.Tags, bearingComp.Tags, baseComp.Tags)
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int[] BuildLevelIntArray(int[] rarityBaseArray, RarityType rarity, int perLevel)
|
||||
{
|
||||
int baseValue = ResolveRarityBaseValue(rarityBaseArray, rarity);
|
||||
int[] values = new int[TowerLevelCount];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] = baseValue + perLevel * i;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static float[] BuildLevelFloatArray(float[] rarityBaseArray, RarityType rarity, float perLevel)
|
||||
{
|
||||
float baseValue = ResolveRarityBaseValue(rarityBaseArray, rarity);
|
||||
float[] values = new float[TowerLevelCount];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] = baseValue + perLevel * i;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static float[] BuildConstantLevelFloatArray(float value)
|
||||
{
|
||||
float[] values = new float[TowerLevelCount];
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] = value;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static int ResolveRarityBaseValue(int[] rarityBaseArray, RarityType rarity)
|
||||
{
|
||||
if (rarityBaseArray == null || rarityBaseArray.Length <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rarityIndex = Mathf.Clamp((int)rarity - 1, 0, rarityBaseArray.Length - 1);
|
||||
return rarityBaseArray[rarityIndex];
|
||||
}
|
||||
|
||||
private static float ResolveRarityBaseValue(float[] rarityBaseArray, RarityType rarity)
|
||||
{
|
||||
if (rarityBaseArray == null || rarityBaseArray.Length <= 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
int rarityIndex = Mathf.Clamp((int)rarity - 1, 0, rarityBaseArray.Length - 1);
|
||||
return rarityBaseArray[rarityIndex];
|
||||
}
|
||||
|
||||
private static TagType[] MergeTags(params TagType[][] sources)
|
||||
{
|
||||
HashSet<TagType> uniqueTags = new HashSet<TagType>();
|
||||
if (sources != null)
|
||||
{
|
||||
for (int i = 0; i < sources.Length; i++)
|
||||
{
|
||||
TagType[] tags = sources[i];
|
||||
if (tags == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < tags.Length; j++)
|
||||
{
|
||||
uniqueTags.Add(tags[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TagType[] mergedTags = new TagType[uniqueTags.Count];
|
||||
uniqueTags.CopyTo(mergedTags);
|
||||
return mergedTags;
|
||||
}
|
||||
|
||||
private static RarityType ResolveAverageRarity(RarityType muzzleRarity, RarityType bearingRarity,
|
||||
RarityType baseRarity)
|
||||
{
|
||||
float avg = ((int)muzzleRarity + (int)bearingRarity + (int)baseRarity) / 3f;
|
||||
int rounded = Mathf.RoundToInt(avg);
|
||||
int clamped = Mathf.Clamp(rounded, (int)RarityType.White, (int)RarityType.Red);
|
||||
return (RarityType)clamped;
|
||||
}
|
||||
|
||||
private IDataTable<DRMuzzleComp> EnsureMuzzleTable()
|
||||
{
|
||||
_drMuzzleComp ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
||||
return _drMuzzleComp;
|
||||
}
|
||||
|
||||
private IDataTable<DRBearingComp> EnsureBearingTable()
|
||||
{
|
||||
_drBearingComp ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
||||
return _drBearingComp;
|
||||
}
|
||||
|
||||
private IDataTable<DRBaseComp> EnsureBaseTable()
|
||||
{
|
||||
_drBaseComp ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
||||
return _drBaseComp;
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnInit();
|
||||
}
|
||||
|
||||
private long AllocateInstanceId()
|
||||
{
|
||||
if (_nextInstanceId < 1)
|
||||
{
|
||||
_nextInstanceId = 1;
|
||||
}
|
||||
|
||||
return _nextInstanceId++;
|
||||
}
|
||||
|
||||
private void RebuildNextInstanceId()
|
||||
{
|
||||
long maxInstanceId = 0;
|
||||
if (_inventory.Towers != null)
|
||||
{
|
||||
foreach (var item in _inventory.Towers)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_inventory.MuzzleComponents != null)
|
||||
{
|
||||
foreach (var item in _inventory.MuzzleComponents)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_inventory.BearingComponents != null)
|
||||
{
|
||||
foreach (var item in _inventory.BearingComponents)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_inventory.BaseComponents != null)
|
||||
{
|
||||
foreach (var item in _inventory.BaseComponents)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_nextInstanceId = Math.Max(1, maxInstanceId + 1);
|
||||
}
|
||||
|
||||
private void NormalizeParticipantState()
|
||||
{
|
||||
if (_inventory == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_inventory.ParticipantTowerInstanceIds ??= new List<long>();
|
||||
Dictionary<long, TowerItemData> towerMap = new Dictionary<long, TowerItemData>();
|
||||
if (_inventory.Towers != null)
|
||||
{
|
||||
for (int i = 0; i < _inventory.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData tower = _inventory.Towers[i];
|
||||
if (tower == null || tower.InstanceId <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tower.IsParticipatingInCombat = false;
|
||||
towerMap[tower.InstanceId] = tower;
|
||||
}
|
||||
}
|
||||
|
||||
List<long> normalizedIds = new List<long>(_inventory.ParticipantTowerInstanceIds.Count);
|
||||
HashSet<long> uniqueIds = new HashSet<long>();
|
||||
for (int i = 0; i < _inventory.ParticipantTowerInstanceIds.Count; i++)
|
||||
{
|
||||
if (normalizedIds.Count >= MaxParticipantTowerCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
long id = _inventory.ParticipantTowerInstanceIds[i];
|
||||
if (id <= 0 || !uniqueIds.Add(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!towerMap.TryGetValue(id, out TowerItemData tower))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tower.IsParticipatingInCombat = true;
|
||||
normalizedIds.Add(id);
|
||||
}
|
||||
|
||||
_inventory.ParticipantTowerInstanceIds = normalizedIds;
|
||||
}
|
||||
|
||||
private static BackpackInventoryData CloneInventory(BackpackInventoryData source)
|
||||
{
|
||||
BackpackInventoryData cloned = new BackpackInventoryData();
|
||||
if (source == null)
|
||||
{
|
||||
return cloned;
|
||||
}
|
||||
|
||||
cloned.Gold = Mathf.Max(0, source.Gold);
|
||||
|
||||
if (source.MuzzleComponents != null)
|
||||
{
|
||||
for (int i = 0; i < source.MuzzleComponents.Count; i++)
|
||||
{
|
||||
MuzzleCompItemData item = source.MuzzleComponents[i];
|
||||
if (item != null)
|
||||
{
|
||||
cloned.MuzzleComponents.Add(CloneMuzzleComp(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source.BearingComponents != null)
|
||||
{
|
||||
for (int i = 0; i < source.BearingComponents.Count; i++)
|
||||
{
|
||||
BearingCompItemData item = source.BearingComponents[i];
|
||||
if (item != null)
|
||||
{
|
||||
cloned.BearingComponents.Add(CloneBearingComp(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source.BaseComponents != null)
|
||||
{
|
||||
for (int i = 0; i < source.BaseComponents.Count; i++)
|
||||
{
|
||||
BaseCompItemData item = source.BaseComponents[i];
|
||||
if (item != null)
|
||||
{
|
||||
cloned.BaseComponents.Add(CloneBaseComp(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source.Towers != null)
|
||||
{
|
||||
for (int i = 0; i < source.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData item = source.Towers[i];
|
||||
if (item != null)
|
||||
{
|
||||
cloned.Towers.Add(CloneTower(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source.ParticipantTowerInstanceIds != null)
|
||||
{
|
||||
for (int i = 0; i < source.ParticipantTowerInstanceIds.Count; i++)
|
||||
{
|
||||
long id = source.ParticipantTowerInstanceIds[i];
|
||||
if (id > 0)
|
||||
{
|
||||
cloned.ParticipantTowerInstanceIds.Add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
private static MuzzleCompItemData CloneMuzzleComp(MuzzleCompItemData source)
|
||||
{
|
||||
return new MuzzleCompItemData
|
||||
{
|
||||
InstanceId = source.InstanceId,
|
||||
ConfigId = source.ConfigId,
|
||||
Name = source.Name,
|
||||
Rarity = source.Rarity,
|
||||
Endurance = source.Endurance,
|
||||
IsAssembledIntoTower = source.IsAssembledIntoTower,
|
||||
Constraint = source.Constraint,
|
||||
Tags = CloneTags(source.Tags),
|
||||
AttackDamage = CloneIntArray(source.AttackDamage),
|
||||
DamageRandomRate = source.DamageRandomRate,
|
||||
AttackMethodType = source.AttackMethodType
|
||||
};
|
||||
}
|
||||
|
||||
private static BearingCompItemData CloneBearingComp(BearingCompItemData source)
|
||||
{
|
||||
return new BearingCompItemData
|
||||
{
|
||||
InstanceId = source.InstanceId,
|
||||
ConfigId = source.ConfigId,
|
||||
Name = source.Name,
|
||||
Rarity = source.Rarity,
|
||||
Endurance = source.Endurance,
|
||||
IsAssembledIntoTower = source.IsAssembledIntoTower,
|
||||
Constraint = source.Constraint,
|
||||
Tags = CloneTags(source.Tags),
|
||||
RotateSpeed = CloneFloatArray(source.RotateSpeed),
|
||||
AttackRange = CloneFloatArray(source.AttackRange)
|
||||
};
|
||||
}
|
||||
|
||||
private static BaseCompItemData CloneBaseComp(BaseCompItemData source)
|
||||
{
|
||||
return new BaseCompItemData
|
||||
{
|
||||
InstanceId = source.InstanceId,
|
||||
ConfigId = source.ConfigId,
|
||||
Name = source.Name,
|
||||
Rarity = source.Rarity,
|
||||
Endurance = source.Endurance,
|
||||
IsAssembledIntoTower = source.IsAssembledIntoTower,
|
||||
Constraint = source.Constraint,
|
||||
Tags = CloneTags(source.Tags),
|
||||
AttackSpeed = CloneFloatArray(source.AttackSpeed),
|
||||
AttackPropertyType = source.AttackPropertyType
|
||||
};
|
||||
}
|
||||
|
||||
private static TowerItemData CloneTower(TowerItemData source)
|
||||
{
|
||||
return new TowerItemData
|
||||
{
|
||||
InstanceId = source.InstanceId,
|
||||
Name = source.Name,
|
||||
Rarity = source.Rarity,
|
||||
IsParticipatingInCombat = source.IsParticipatingInCombat,
|
||||
MuzzleComponentInstanceId = source.MuzzleComponentInstanceId,
|
||||
BearingComponentInstanceId = source.BearingComponentInstanceId,
|
||||
BaseComponentInstanceId = source.BaseComponentInstanceId,
|
||||
Stats = CloneTowerStats(source.Stats)
|
||||
};
|
||||
}
|
||||
|
||||
private static TowerStatsData CloneTowerStats(TowerStatsData source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return new TowerStatsData();
|
||||
}
|
||||
|
||||
return new TowerStatsData
|
||||
{
|
||||
AttackDamage = CloneIntArray(source.AttackDamage),
|
||||
DamageRandomRate = source.DamageRandomRate,
|
||||
RotateSpeed = CloneFloatArray(source.RotateSpeed),
|
||||
AttackRange = CloneFloatArray(source.AttackRange),
|
||||
AttackSpeed = CloneFloatArray(source.AttackSpeed),
|
||||
AttackMethodType = source.AttackMethodType,
|
||||
AttackPropertyType = source.AttackPropertyType,
|
||||
Tags = CloneTags(source.Tags)
|
||||
};
|
||||
}
|
||||
|
||||
private static int[] CloneIntArray(int[] source)
|
||||
{
|
||||
return source != null ? (int[])source.Clone() : Array.Empty<int>();
|
||||
}
|
||||
|
||||
private static float[] CloneFloatArray(float[] source)
|
||||
{
|
||||
return source != null ? (float[])source.Clone() : Array.Empty<float>();
|
||||
}
|
||||
|
||||
private static TagType[] CloneTags(TagType[] source)
|
||||
{
|
||||
return source != null ? (TagType[])source.Clone() : Array.Empty<TagType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user