补全防御塔组装与介绍描述功能

- RepoForm.CombineArea:防御塔组装,直接继承所有组件的属性
- ItemDescUtility:根据属性构建不同的 ItemDescForm 的文本
This commit is contained in:
SepComet
2026-03-04 18:17:10 +08:00
parent e12d2e73b2
commit 1e5803f4c5
29 changed files with 832 additions and 168 deletions
@@ -54,7 +54,7 @@ namespace GeometryTD.CustomComponent
MuzzleComponents = new List<MuzzleCompItemData>(_rewardInventory.MuzzleComponents),
BearingComponents = new List<BearingCompItemData>(_rewardInventory.BearingComponents),
BaseComponents = new List<BaseCompItemData>(_rewardInventory.BaseComponents),
Towers = new List<DefenseTowerItemData>(_rewardInventory.Towers)
Towers = new List<TowerItemData>(_rewardInventory.Towers)
};
}
@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using GeometryTD.UI;
using UnityEngine;
@@ -8,9 +11,13 @@ namespace GeometryTD.CustomComponent
{
public class PlayerInventoryComponent : GameFrameworkComponent
{
private const int TowerLevelCount = 5;
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
{
@@ -115,13 +122,13 @@ namespace GeometryTD.CustomComponent
{
for (int i = 0; i < gainedInventory.Towers.Count; i++)
{
DefenseTowerItemData source = gainedInventory.Towers[i];
TowerItemData source = gainedInventory.Towers[i];
if (source == null)
{
continue;
}
DefenseTowerItemData cloned = CloneTower(source);
TowerItemData cloned = CloneTower(source);
cloned.InstanceId = AllocateInstanceId();
_inventory.Towers.Add(cloned);
gainedTowerCount++;
@@ -171,6 +178,58 @@ namespace GeometryTD.CustomComponent
_inventory.Gold += resolvedGain;
}
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();
@@ -180,27 +239,250 @@ namespace GeometryTD.CustomComponent
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++)
foreach (var tower in _inventory.Towers)
{
DefenseTowerItemData tower = _inventory.Towers[i];
if (tower == null)
{
continue;
}
float originalEndurance = tower.Endurance;
float nextEndurance = Mathf.Clamp(originalEndurance - resolvedLoss, 0f, 100f);
if (nextEndurance >= originalEndurance)
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;
}
tower.Endurance = nextEndurance;
affectedCount++;
map[component.InstanceId] = component;
}
return affectedCount;
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 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()
@@ -228,9 +510,8 @@ namespace GeometryTD.CustomComponent
long maxInstanceId = 0;
if (_inventory.Towers != null)
{
for (int i = 0; i < _inventory.Towers.Count; i++)
foreach (var item in _inventory.Towers)
{
DefenseTowerItemData item = _inventory.Towers[i];
if (item != null)
{
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
@@ -240,9 +521,8 @@ namespace GeometryTD.CustomComponent
if (_inventory.MuzzleComponents != null)
{
for (int i = 0; i < _inventory.MuzzleComponents.Count; i++)
foreach (var item in _inventory.MuzzleComponents)
{
MuzzleCompItemData item = _inventory.MuzzleComponents[i];
if (item != null)
{
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
@@ -252,9 +532,8 @@ namespace GeometryTD.CustomComponent
if (_inventory.BearingComponents != null)
{
for (int i = 0; i < _inventory.BearingComponents.Count; i++)
foreach (var item in _inventory.BearingComponents)
{
BearingCompItemData item = _inventory.BearingComponents[i];
if (item != null)
{
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
@@ -264,9 +543,8 @@ namespace GeometryTD.CustomComponent
if (_inventory.BaseComponents != null)
{
for (int i = 0; i < _inventory.BaseComponents.Count; i++)
foreach (var item in _inventory.BaseComponents)
{
BaseCompItemData item = _inventory.BaseComponents[i];
if (item != null)
{
maxInstanceId = Math.Max(maxInstanceId, item.InstanceId);
@@ -327,7 +605,7 @@ namespace GeometryTD.CustomComponent
{
for (int i = 0; i < source.Towers.Count; i++)
{
DefenseTowerItemData item = source.Towers[i];
TowerItemData item = source.Towers[i];
if (item != null)
{
cloned.Towers.Add(CloneTower(item));
@@ -347,6 +625,7 @@ namespace GeometryTD.CustomComponent
Name = source.Name,
Rarity = source.Rarity,
Endurance = source.Endurance,
IsAssembledIntoTower = source.IsAssembledIntoTower,
Constraint = source.Constraint,
Tags = CloneTags(source.Tags),
AttackDamage = CloneIntArray(source.AttackDamage),
@@ -364,6 +643,7 @@ namespace GeometryTD.CustomComponent
Name = source.Name,
Rarity = source.Rarity,
Endurance = source.Endurance,
IsAssembledIntoTower = source.IsAssembledIntoTower,
Constraint = source.Constraint,
Tags = CloneTags(source.Tags),
RotateSpeed = CloneFloatArray(source.RotateSpeed),
@@ -380,6 +660,7 @@ namespace GeometryTD.CustomComponent
Name = source.Name,
Rarity = source.Rarity,
Endurance = source.Endurance,
IsAssembledIntoTower = source.IsAssembledIntoTower,
Constraint = source.Constraint,
Tags = CloneTags(source.Tags),
AttackSpeed = CloneFloatArray(source.AttackSpeed),
@@ -387,14 +668,13 @@ namespace GeometryTD.CustomComponent
};
}
private static DefenseTowerItemData CloneTower(DefenseTowerItemData source)
private static TowerItemData CloneTower(TowerItemData source)
{
return new DefenseTowerItemData
return new TowerItemData
{
InstanceId = source.InstanceId,
Name = source.Name,
Rarity = source.Rarity,
Endurance = source.Endurance,
MuzzleComponentInstanceId = source.MuzzleComponentInstanceId,
BearingComponentInstanceId = source.BearingComponentInstanceId,
BaseComponentInstanceId = source.BaseComponentInstanceId,
@@ -402,20 +682,20 @@ namespace GeometryTD.CustomComponent
};
}
private static DefenseTowerStatsData CloneTowerStats(DefenseTowerStatsData source)
private static TowerStatsData CloneTowerStats(TowerStatsData source)
{
if (source == null)
{
return new DefenseTowerStatsData();
return new TowerStatsData();
}
return new DefenseTowerStatsData
return new TowerStatsData
{
AttackDamage = source.AttackDamage,
AttackDamage = CloneIntArray(source.AttackDamage),
DamageRandomRate = source.DamageRandomRate,
RotateSpeed = source.RotateSpeed,
AttackRange = source.AttackRange,
AttackSpeed = source.AttackSpeed,
RotateSpeed = CloneFloatArray(source.RotateSpeed),
AttackRange = CloneFloatArray(source.AttackRange),
AttackSpeed = CloneFloatArray(source.AttackSpeed),
AttackMethodType = source.AttackMethodType,
AttackPropertyType = source.AttackPropertyType,
Tags = CloneTags(source.Tags)
@@ -437,4 +717,4 @@ namespace GeometryTD.CustomComponent
return source != null ? (TagType[])source.Clone() : Array.Empty<TagType>();
}
}
}
}