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

- 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
@@ -17,6 +17,7 @@ namespace GeometryTD.UI
{
public string Title;
public string TypeText;
public string Description;
public TagType[] Tags;
}
@@ -128,7 +129,11 @@ namespace GeometryTD.UI
IconAreaContext = BuildIconAreaContext(tower)
});
AddItemDescSeed(tower.InstanceId, tower.Name, "Tower",
AddItemDescSeed(
tower.InstanceId,
tower.Name,
"Tower",
ItemDescUtility.BuildTowerDesc(tower.Stats) ?? string.Empty,
tower.Stats != null ? tower.Stats.Tags : null);
}
}
@@ -151,7 +156,12 @@ namespace GeometryTD.UI
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(item.InstanceId, item.Name, BuildComponentTypeText(item.SlotType), item.Tags);
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildMuzzleDesc(item) ?? string.Empty,
item.Tags);
}
}
@@ -172,7 +182,12 @@ namespace GeometryTD.UI
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(item.InstanceId, item.Name, BuildComponentTypeText(item.SlotType), item.Tags);
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildBearingDesc(item) ?? string.Empty,
item.Tags);
}
}
@@ -194,14 +209,19 @@ namespace GeometryTD.UI
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(item.InstanceId, item.Name, BuildComponentTypeText(item.SlotType), item.Tags);
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildBaseDesc(item) ?? string.Empty,
item.Tags);
}
}
return itemContexts.ToArray();
}
private void AddItemDescSeed(long itemId, string title, string typeText, TagType[] tags)
private void AddItemDescSeed(long itemId, string title, string typeText, string description, TagType[] tags)
{
if (itemId <= 0)
{
@@ -212,6 +232,7 @@ namespace GeometryTD.UI
{
Title = string.IsNullOrWhiteSpace(title) ? $"Item {itemId}" : title,
TypeText = typeText ?? string.Empty,
Description = description ?? string.Empty,
Tags = CloneTags(tags)
};
}
@@ -233,7 +254,7 @@ namespace GeometryTD.UI
};
}
private static IconAreaContext BuildIconAreaContext(DefenseTowerItemData tower)
private static IconAreaContext BuildIconAreaContext(TowerItemData tower)
{
if (tower == null)
{
@@ -308,7 +329,7 @@ namespace GeometryTD.UI
{
Title = seed.Title,
TypeText = seed.TypeText,
Description = string.Empty,
Description = seed.Description ?? string.Empty,
Price = 0,
TargetPos = args.TargetPos,
Tags = CloneTags(seed.Tags)
@@ -19,6 +19,7 @@ namespace GeometryTD.UI
{
public string Title;
public string TypeText;
public string Description;
public TagType[] Tags;
}
@@ -34,6 +35,7 @@ namespace GeometryTD.UI
GameEntry.Event.Subscribe(RepoItemDetailRequestedEventArgs.EventId, OnRepoItemDetailRequested);
GameEntry.Event.Subscribe(RepoItemDragEndedEventArgs.EventId, OnRepoItemDragEnded);
GameEntry.Event.Subscribe(CombineSlotClickedEventArgs.EventId, OnCombineSlotClicked);
GameEntry.Event.Subscribe(RepoCombineRequestedEventArgs.EventId, OnRepoCombineRequested);
GameEntry.Event.Subscribe(RepoFormReturnEventArgs.EventId, OnRepoFormReturn);
}
@@ -42,6 +44,7 @@ namespace GeometryTD.UI
GameEntry.Event.Unsubscribe(RepoItemDetailRequestedEventArgs.EventId, OnRepoItemDetailRequested);
GameEntry.Event.Unsubscribe(RepoItemDragEndedEventArgs.EventId, OnRepoItemDragEnded);
GameEntry.Event.Unsubscribe(CombineSlotClickedEventArgs.EventId, OnCombineSlotClicked);
GameEntry.Event.Unsubscribe(RepoCombineRequestedEventArgs.EventId, OnRepoCombineRequested);
GameEntry.Event.Unsubscribe(RepoFormReturnEventArgs.EventId, OnRepoFormReturn);
}
@@ -103,7 +106,7 @@ namespace GeometryTD.UI
if (rawData.Inventory.Towers != null)
{
foreach (DefenseTowerItemData tower in rawData.Inventory.Towers)
foreach (TowerItemData tower in rawData.Inventory.Towers)
{
if (tower == null)
{
@@ -117,7 +120,11 @@ namespace GeometryTD.UI
ComponentSlotType = TowerCompSlotType.None,
IconAreaContext = BuildIconAreaContext(tower)
});
AddItemDescSeed(tower.InstanceId, tower.Name, "Tower",
AddItemDescSeed(
tower.InstanceId,
tower.Name,
"Tower",
ItemDescUtility.BuildTowerDesc(tower.Stats) ?? string.Empty,
tower.Stats != null ? tower.Stats.Tags : null);
}
}
@@ -126,7 +133,7 @@ namespace GeometryTD.UI
{
foreach (MuzzleCompItemData item in rawData.Inventory.MuzzleComponents)
{
if (item == null)
if (item == null || item.IsAssembledIntoTower)
{
continue;
}
@@ -138,7 +145,12 @@ namespace GeometryTD.UI
ComponentSlotType = TowerCompSlotType.Muzzle,
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(item.InstanceId, item.Name, BuildComponentTypeText(item.SlotType), item.Tags);
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildMuzzleDesc(item) ?? string.Empty,
item.Tags);
}
}
@@ -146,7 +158,7 @@ namespace GeometryTD.UI
{
foreach (BearingCompItemData item in rawData.Inventory.BearingComponents)
{
if (item == null)
if (item == null || item.IsAssembledIntoTower)
{
continue;
}
@@ -158,7 +170,12 @@ namespace GeometryTD.UI
ComponentSlotType = TowerCompSlotType.Bearing,
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(item.InstanceId, item.Name, BuildComponentTypeText(item.SlotType), item.Tags);
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildBearingDesc(item) ?? string.Empty,
item.Tags);
}
}
@@ -166,7 +183,7 @@ namespace GeometryTD.UI
{
foreach (BaseCompItemData item in rawData.Inventory.BaseComponents)
{
if (item == null)
if (item == null || item.IsAssembledIntoTower)
{
continue;
}
@@ -178,7 +195,12 @@ namespace GeometryTD.UI
ComponentSlotType = TowerCompSlotType.Base,
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(item.InstanceId, item.Name, BuildComponentTypeText(item.SlotType), item.Tags);
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildBaseDesc(item) ?? string.Empty,
item.Tags);
}
}
@@ -203,7 +225,7 @@ namespace GeometryTD.UI
_itemContextMap[itemContext.InstanceId] = itemContext;
}
private void AddItemDescSeed(long itemId, string title, string typeText, TagType[] tags)
private void AddItemDescSeed(long itemId, string title, string typeText, string description, TagType[] tags)
{
if (itemId <= 0)
{
@@ -214,6 +236,7 @@ namespace GeometryTD.UI
{
Title = string.IsNullOrWhiteSpace(title) ? $"Item {itemId}" : title,
TypeText = typeText ?? string.Empty,
Description = description ?? string.Empty,
Tags = CloneTags(tags)
};
}
@@ -235,7 +258,7 @@ namespace GeometryTD.UI
};
}
private static IconAreaContext BuildIconAreaContext(DefenseTowerItemData tower)
private static IconAreaContext BuildIconAreaContext(TowerItemData tower)
{
if (tower == null)
{
@@ -302,7 +325,7 @@ namespace GeometryTD.UI
{
Title = seed.Title,
TypeText = seed.TypeText,
Description = string.Empty,
Description = seed.Description ?? string.Empty,
Price = 0,
TargetPos = args.TargetPos,
Tags = CloneTags(seed.Tags)
@@ -367,6 +390,36 @@ namespace GeometryTD.UI
this.CloseUI();
}
private void OnRepoCombineRequested(object sender, GameEventArgs e)
{
if (!IsEventFromCurrentForm(sender))
{
return;
}
if (!(e is RepoCombineRequestedEventArgs args))
{
return;
}
if (_useCase == null || Form == null)
{
return;
}
if (!_useCase.TryAssembleTower(args.MuzzleItemId, args.BearingItemId, args.BaseItemId))
{
return;
}
RepoFormRawData latestRawData = _useCase.CreateInitialModel();
RepoFormContext latestContext = BuildContext(latestRawData);
if (latestContext != null)
{
Form.RefreshUI(latestContext);
}
}
private bool IsEventFromCurrentForm(object sender)
{
if (Form == null)
@@ -15,6 +15,20 @@ namespace GeometryTD.UI
};
}
public bool TryAssembleTower(long muzzleItemId, long bearingItemId, long baseItemId)
{
if (GameEntry.PlayerInventory == null)
{
return false;
}
return GameEntry.PlayerInventory.TryAssembleTower(
muzzleItemId,
bearingItemId,
baseItemId,
out _);
}
public static BackpackInventoryData SampleInventory()
{
BackpackInventoryData inventory = new BackpackInventoryData
@@ -29,6 +43,7 @@ namespace GeometryTD.UI
Name = "元素枪口",
Rarity = RarityType.Green,
Endurance = 90f,
IsAssembledIntoTower = true,
AttackDamage = new[] { 20, 30, 40, 50, 80 },
DamageRandomRate = 0.05f,
AttackMethodType = AttackMethodType.NormalBullet,
@@ -36,6 +51,34 @@ namespace GeometryTD.UI
Tags = new[] { TagType.Fire }
};
inventory.MuzzleComponents.Add(muzzle);
inventory.MuzzleComponents.Add(new MuzzleCompItemData
{
InstanceId = 10002,
ConfigId = 2,
Name = "控制枪口",
Rarity = RarityType.Blue,
Endurance = 100f,
IsAssembledIntoTower = false,
AttackDamage = new[] { 30, 50, 70, 90, 100 },
DamageRandomRate = 0.01f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = new[] { TagType.Ice, TagType.FreezeMask }
});
inventory.MuzzleComponents.Add(new MuzzleCompItemData
{
InstanceId = 10003,
ConfigId = 3,
Name = "穿透枪口",
Rarity = RarityType.Purple,
Endurance = 97f,
IsAssembledIntoTower = false,
AttackDamage = new[] { 50, 55, 60, 80, 90 },
DamageRandomRate = 0.02f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = new[] { TagType.Pierce, TagType.Crit }
});
BearingCompItemData bearing = new BearingCompItemData
{
@@ -44,12 +87,39 @@ namespace GeometryTD.UI
Name = "元素轴承",
Rarity = RarityType.Green,
Endurance = 95f,
IsAssembledIntoTower = true,
RotateSpeed = new[] { 10f, 12f, 13f, 14f, 15f },
AttackRange = new[] { 2f, 2f, 2f, 2f, 2f },
Constraint = string.Empty,
Tags = new[] { TagType.Fire }
};
inventory.BearingComponents.Add(bearing);
inventory.BearingComponents.Add(new BearingCompItemData
{
InstanceId = 20002,
ConfigId = 2,
Name = "控制轴承",
Rarity = RarityType.Blue,
Endurance = 100f,
IsAssembledIntoTower = false,
RotateSpeed = new[] { 20f, 25f, 30f, 32f, 35f },
AttackRange = new[] { 6f, 6.5f, 7f, 8f, 8f },
Constraint = string.Empty,
Tags = new[] { TagType.Ice, TagType.Shatter }
});
inventory.BearingComponents.Add(new BearingCompItemData
{
InstanceId = 20003,
ConfigId = 3,
Name = "穿透轴承",
Rarity = RarityType.Purple,
Endurance = 96f,
IsAssembledIntoTower = false,
RotateSpeed = new[] { 60f, 70f, 80f, 90f, 100f },
AttackRange = new[] { 4f, 4.5f, 5f, 5.5f, 6f },
Constraint = string.Empty,
Tags = new[] { TagType.Pierce, TagType.Overpenetrate }
});
BaseCompItemData baseComp = new BaseCompItemData
{
@@ -58,26 +128,52 @@ namespace GeometryTD.UI
Name = "元素底座",
Rarity = RarityType.Green,
Endurance = 88f,
IsAssembledIntoTower = true,
AttackSpeed = new[] { 2f, 1.5f, 1f, 0.8f, 0.7f },
AttackPropertyType = AttackPropertyType.Fire,
Constraint = string.Empty,
Tags = new[] { TagType.Fire }
};
inventory.BaseComponents.Add(baseComp);
inventory.BaseComponents.Add(new BaseCompItemData
{
InstanceId = 30002,
ConfigId = 2,
Name = "控制底座",
Rarity = RarityType.Blue,
Endurance = 100f,
IsAssembledIntoTower = false,
AttackSpeed = new[] { 4f, 4.2f, 4.4f, 4.6f, 4.8f },
AttackPropertyType = AttackPropertyType.Ice,
Constraint = string.Empty,
Tags = new[] { TagType.Ice, TagType.AbsoluteZero }
});
inventory.BaseComponents.Add(new BaseCompItemData
{
InstanceId = 30003,
ConfigId = 3,
Name = "穿透底座",
Rarity = RarityType.Purple,
Endurance = 95f,
IsAssembledIntoTower = false,
AttackSpeed = new[] { 1f, 1f, 1f, 1f, 1f },
AttackPropertyType = AttackPropertyType.Physics,
Constraint = string.Empty,
Tags = new[] { TagType.Pierce, TagType.Execution }
});
DefenseTowerItemData tower = new DefenseTowerItemData
TowerItemData tower = new TowerItemData
{
InstanceId = 90001,
Name = "测试防御塔-A",
Rarity = RarityType.Green,
Endurance = 92f,
MuzzleComponentInstanceId = muzzle.InstanceId,
BearingComponentInstanceId = bearing.InstanceId,
BaseComponentInstanceId = baseComp.InstanceId,
Stats = new DefenseTowerStatsData
Stats = new TowerStatsData
{
AttackDamage = new[] { 200, 220, 240, 260, 300 },
DamageRandomRate = new[] { 0f, 0f, 0f, 0f, 0f },
DamageRandomRate = 0f,
RotateSpeed = new[] { 200f, 210f, 220f, 230f, 240f },
AttackRange = new[] { 4.5f, 4.7f, 4.9f, 5.1f, 5.3f },
AttackSpeed = new[] { 1.5f, 1.2f, 1.1f, 1.0f, 0.8f },
@@ -88,19 +184,18 @@ namespace GeometryTD.UI
};
inventory.Towers.Add(tower);
inventory.Towers.Add(new DefenseTowerItemData
inventory.Towers.Add(new TowerItemData
{
InstanceId = 90002,
Name = "测试防御塔-B",
Rarity = RarityType.Blue,
Endurance = 100f,
MuzzleComponentInstanceId = 0,
BearingComponentInstanceId = 0,
BaseComponentInstanceId = 0,
Stats = new DefenseTowerStatsData
Stats = new TowerStatsData
{
AttackDamage = new[] { 200, 220, 240, 260, 300 },
DamageRandomRate = new[] { 0f, 0f, 0f, 0f, 0f },
DamageRandomRate = 0.1f,
RotateSpeed = new[] { 200f, 210f, 220f, 230f, 240f },
AttackRange = new[] { 4.5f, 4.7f, 4.9f, 5.1f, 5.3f },
AttackSpeed = new[] { 1.5f, 1.2f, 1.1f, 1.0f, 0.8f },
@@ -1,4 +1,5 @@
using GeometryTD.Definition;
using GeometryTD.CustomEvent;
using UnityEngine;
using UnityEngine.EventSystems;
@@ -42,7 +43,14 @@ namespace GeometryTD.UI
public void OnCombineClick()
{
// TODO: Combine logic.
if (!TryGetBoundItemId(TowerCompSlotType.Muzzle, out long muzzleItemId) ||
!TryGetBoundItemId(TowerCompSlotType.Bearing, out long bearingItemId) ||
!TryGetBoundItemId(TowerCompSlotType.Base, out long baseItemId))
{
return;
}
GameEntry.Event.Fire(this, RepoCombineRequestedEventArgs.Create(muzzleItemId, bearingItemId, baseItemId));
}
public bool TryAssignItem(RepoItemContext itemContext)
@@ -127,5 +135,18 @@ namespace GeometryTD.UI
return null;
}
private bool TryGetBoundItemId(TowerCompSlotType slotType, out long boundItemId)
{
boundItemId = 0;
CombineSlotItem slot = FindSlot(slotType);
if (slot == null || !slot.HasItem || slot.BoundItemId <= 0)
{
return false;
}
boundItemId = slot.BoundItemId;
return true;
}
}
}
@@ -61,17 +61,17 @@ namespace GeometryTD.CustomUtility
item.SlotType);
}
public static Color GenerateForTower(DefenseTowerItemData tower)
public static Color GenerateForTower(TowerItemData tower)
{
if (tower?.Stats == null)
{
return Color.white;
}
DefenseTowerStatsData stats = tower.Stats;
TowerStatsData stats = tower.Stats;
return GenerateColor(
ResolveArrayValue(stats.AttackDamage),
ResolveArrayValue(stats.DamageRandomRate),
stats.DamageRandomRate,
ResolveArrayValue(stats.RotateSpeed),
ResolveArrayValue(stats.AttackRange),
ResolveArrayValue(stats.AttackSpeed),
@@ -127,14 +127,16 @@ namespace GeometryTD.CustomUtility
return;
}
if (!s_HasMuzzleRange && TryResolveMuzzleRange(out StatRange attackDamageRange, out StatRange damageRandomRateRange))
if (!s_HasMuzzleRange &&
TryResolveMuzzleRange(out StatRange attackDamageRange, out StatRange damageRandomRateRange))
{
_attackDamageRange = attackDamageRange;
_damageRandomRateRange = damageRandomRateRange;
s_HasMuzzleRange = true;
}
if (!s_HasBearingRange && TryResolveBearingRange(out StatRange rotateSpeedRange, out StatRange attackRangeRange))
if (!s_HasBearingRange &&
TryResolveBearingRange(out StatRange rotateSpeedRange, out StatRange attackRangeRange))
{
_rotateSpeedRange = rotateSpeedRange;
_attackRangeRange = attackRangeRange;
@@ -181,7 +183,8 @@ namespace GeometryTD.CustomUtility
}
IncludeValues(row.AttackDamage, ref minAttackDamage, ref maxAttackDamage, ref hasAttackDamage);
IncludeValue(Mathf.Max(0f, row.DamageRandomRate), ref minDamageRandomRate, ref maxDamageRandomRate, ref hasDamageRandomRate);
IncludeValue(Mathf.Max(0f, row.DamageRandomRate), ref minDamageRandomRate, ref maxDamageRandomRate,
ref hasDamageRandomRate);
}
if (!hasAttackDamage || !hasDamageRandomRate)
@@ -437,4 +440,4 @@ namespace GeometryTD.CustomUtility
public float Max { get; }
}
}
}
}