补充参战防御塔进入战斗逻辑
- 将 Inventory 中参战的防御塔数据传递到 CombatSelectForm 中 - 为防御塔的图标展示添加 TowerRepoItem 与 TowerIconArea 适配防御塔的三层 Image
This commit is contained in:
@@ -0,0 +1,443 @@
|
||||
using GameFramework.DataTable;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomUtility
|
||||
{
|
||||
public static class IconColorGenerator
|
||||
{
|
||||
private const int StatCount = 5;
|
||||
private const float MinSaturation = 0.4f;
|
||||
private const float MinValue = 0.6f;
|
||||
private const float TinyValue = 0.0001f;
|
||||
|
||||
private static readonly float[] HueMap = { 0f, 30f, 200f, 120f, 270f };
|
||||
|
||||
private static StatRange _attackDamageRange = new StatRange(1f, 300f);
|
||||
private static StatRange _damageRandomRateRange = new StatRange(0f, 0.3f);
|
||||
private static StatRange _rotateSpeedRange = new StatRange(10f, 240f);
|
||||
private static StatRange _attackRangeRange = new StatRange(2f, 8f);
|
||||
private static StatRange _attackSpeedRange = new StatRange(0.5f, 4f);
|
||||
|
||||
private static bool s_HasMuzzleRange;
|
||||
private static bool s_HasBearingRange;
|
||||
private static bool s_HasBaseRange;
|
||||
|
||||
public static Color GenerateForComponent(TowerCompItemData item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
float attackDamage = 0f;
|
||||
float damageRandomRate = 0f;
|
||||
float rotateSpeed = 0f;
|
||||
float attackRange = 0f;
|
||||
float attackSpeed = 0f;
|
||||
|
||||
if (item is MuzzleCompItemData muzzle)
|
||||
{
|
||||
attackDamage = ResolveArrayValue(muzzle.AttackDamage);
|
||||
damageRandomRate = Mathf.Max(0f, muzzle.DamageRandomRate);
|
||||
}
|
||||
else if (item is BearingCompItemData bearing)
|
||||
{
|
||||
rotateSpeed = ResolveArrayValue(bearing.RotateSpeed);
|
||||
attackRange = ResolveArrayValue(bearing.AttackRange);
|
||||
}
|
||||
else if (item is BaseCompItemData baseComp)
|
||||
{
|
||||
attackSpeed = ResolveArrayValue(baseComp.AttackSpeed);
|
||||
}
|
||||
|
||||
return GenerateColor(
|
||||
attackDamage,
|
||||
damageRandomRate,
|
||||
rotateSpeed,
|
||||
attackRange,
|
||||
attackSpeed,
|
||||
item.SlotType);
|
||||
}
|
||||
|
||||
public static Color GenerateForTower(TowerItemData tower)
|
||||
{
|
||||
if (tower?.Stats == null)
|
||||
{
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
TowerStatsData stats = tower.Stats;
|
||||
return GenerateColor(
|
||||
ResolveArrayValue(stats.AttackDamage),
|
||||
stats.DamageRandomRate,
|
||||
ResolveArrayValue(stats.RotateSpeed),
|
||||
ResolveArrayValue(stats.AttackRange),
|
||||
ResolveArrayValue(stats.AttackSpeed),
|
||||
TowerCompSlotType.None);
|
||||
}
|
||||
|
||||
private static Color GenerateColor(
|
||||
float attackDamage,
|
||||
float damageRandomRate,
|
||||
float rotateSpeed,
|
||||
float attackRange,
|
||||
float attackSpeed,
|
||||
TowerCompSlotType slotType)
|
||||
{
|
||||
TryRefreshRangesFromDataTables();
|
||||
|
||||
float[] normalizedStats = new float[StatCount];
|
||||
normalizedStats[0] = Normalize(attackDamage, _attackDamageRange);
|
||||
normalizedStats[1] = Normalize(damageRandomRate, _damageRandomRateRange);
|
||||
normalizedStats[2] = Normalize(rotateSpeed, _rotateSpeedRange);
|
||||
normalizedStats[3] = Normalize(attackRange, _attackRangeRange);
|
||||
normalizedStats[4] = Normalize(attackSpeed, _attackSpeedRange);
|
||||
|
||||
FindTopTwoIndexes(normalizedStats, out int primaryIndex, out int secondaryIndex);
|
||||
|
||||
float denominator = normalizedStats[primaryIndex] + normalizedStats[secondaryIndex] + TinyValue;
|
||||
float blendWeight = normalizedStats[secondaryIndex] / denominator;
|
||||
float hue = LerpHue(HueMap[primaryIndex], HueMap[secondaryIndex], blendWeight);
|
||||
hue = NormalizeHue(hue + ResolveSlotHueOffset(slotType));
|
||||
|
||||
float purity = normalizedStats[primaryIndex] - normalizedStats[secondaryIndex];
|
||||
float saturation = Mathf.Lerp(0.45f, 0.9f, Mathf.Clamp01(purity * 1.5f));
|
||||
saturation = Mathf.Max(saturation, MinSaturation);
|
||||
|
||||
float intensity = 0f;
|
||||
for (int i = 0; i < normalizedStats.Length; i++)
|
||||
{
|
||||
intensity += normalizedStats[i];
|
||||
}
|
||||
|
||||
float value = Mathf.Lerp(0.55f, 0.95f, intensity / StatCount);
|
||||
value = Mathf.Max(value, MinValue);
|
||||
|
||||
Color color = Color.HSVToRGB(hue / 360f, saturation, value);
|
||||
color.a = 1f;
|
||||
return color;
|
||||
}
|
||||
|
||||
private static void TryRefreshRangesFromDataTables()
|
||||
{
|
||||
if (GameEntry.DataTable == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
_rotateSpeedRange = rotateSpeedRange;
|
||||
_attackRangeRange = attackRangeRange;
|
||||
s_HasBearingRange = true;
|
||||
}
|
||||
|
||||
if (!s_HasBaseRange && TryResolveBaseRange(out StatRange attackSpeedRange))
|
||||
{
|
||||
_attackSpeedRange = attackSpeedRange;
|
||||
s_HasBaseRange = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryResolveMuzzleRange(out StatRange attackDamageRange, out StatRange damageRandomRateRange)
|
||||
{
|
||||
attackDamageRange = _attackDamageRange;
|
||||
damageRandomRateRange = _damageRandomRateRange;
|
||||
|
||||
IDataTable<DRMuzzleComp> dataTable = GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
|
||||
if (dataTable == null || dataTable.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DRMuzzleComp[] rows = dataTable.GetAllDataRows();
|
||||
if (rows == null || rows.Length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasAttackDamage = false;
|
||||
bool hasDamageRandomRate = false;
|
||||
float minAttackDamage = float.MaxValue;
|
||||
float maxAttackDamage = float.MinValue;
|
||||
float minDamageRandomRate = float.MaxValue;
|
||||
float maxDamageRandomRate = float.MinValue;
|
||||
|
||||
for (int i = 0; i < rows.Length; i++)
|
||||
{
|
||||
DRMuzzleComp row = rows[i];
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IncludeValues(row.AttackDamage, ref minAttackDamage, ref maxAttackDamage, ref hasAttackDamage);
|
||||
IncludeValue(Mathf.Max(0f, row.DamageRandomRate), ref minDamageRandomRate, ref maxDamageRandomRate,
|
||||
ref hasDamageRandomRate);
|
||||
}
|
||||
|
||||
if (!hasAttackDamage || !hasDamageRandomRate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
attackDamageRange = BuildRange(minAttackDamage, maxAttackDamage, _attackDamageRange);
|
||||
damageRandomRateRange = BuildRange(minDamageRandomRate, maxDamageRandomRate, _damageRandomRateRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryResolveBearingRange(out StatRange rotateSpeedRange, out StatRange attackRangeRange)
|
||||
{
|
||||
rotateSpeedRange = _rotateSpeedRange;
|
||||
attackRangeRange = _attackRangeRange;
|
||||
|
||||
IDataTable<DRBearingComp> dataTable = GameEntry.DataTable.GetDataTable<DRBearingComp>();
|
||||
if (dataTable == null || dataTable.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DRBearingComp[] rows = dataTable.GetAllDataRows();
|
||||
if (rows == null || rows.Length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasRotateSpeed = false;
|
||||
bool hasAttackRange = false;
|
||||
float minRotateSpeed = float.MaxValue;
|
||||
float maxRotateSpeed = float.MinValue;
|
||||
float minAttackRange = float.MaxValue;
|
||||
float maxAttackRange = float.MinValue;
|
||||
|
||||
for (int i = 0; i < rows.Length; i++)
|
||||
{
|
||||
DRBearingComp row = rows[i];
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IncludeValues(row.RotateSpeed, ref minRotateSpeed, ref maxRotateSpeed, ref hasRotateSpeed);
|
||||
IncludeValues(row.AttackRange, ref minAttackRange, ref maxAttackRange, ref hasAttackRange);
|
||||
}
|
||||
|
||||
if (!hasRotateSpeed || !hasAttackRange)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rotateSpeedRange = BuildRange(minRotateSpeed, maxRotateSpeed, _rotateSpeedRange);
|
||||
attackRangeRange = BuildRange(minAttackRange, maxAttackRange, _attackRangeRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryResolveBaseRange(out StatRange attackSpeedRange)
|
||||
{
|
||||
attackSpeedRange = _attackSpeedRange;
|
||||
|
||||
IDataTable<DRBaseComp> dataTable = GameEntry.DataTable.GetDataTable<DRBaseComp>();
|
||||
if (dataTable == null || dataTable.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DRBaseComp[] rows = dataTable.GetAllDataRows();
|
||||
if (rows == null || rows.Length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasAttackSpeed = false;
|
||||
float minAttackSpeed = float.MaxValue;
|
||||
float maxAttackSpeed = float.MinValue;
|
||||
|
||||
for (int i = 0; i < rows.Length; i++)
|
||||
{
|
||||
DRBaseComp row = rows[i];
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IncludeValues(row.AttackSpeed, ref minAttackSpeed, ref maxAttackSpeed, ref hasAttackSpeed);
|
||||
}
|
||||
|
||||
if (!hasAttackSpeed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
attackSpeedRange = BuildRange(minAttackSpeed, maxAttackSpeed, _attackSpeedRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void FindTopTwoIndexes(float[] values, out int primaryIndex, out int secondaryIndex)
|
||||
{
|
||||
primaryIndex = 0;
|
||||
secondaryIndex = 1;
|
||||
|
||||
if (values[secondaryIndex] > values[primaryIndex])
|
||||
{
|
||||
(primaryIndex, secondaryIndex) = (secondaryIndex, primaryIndex);
|
||||
}
|
||||
|
||||
for (int i = 2; i < values.Length; i++)
|
||||
{
|
||||
float value = values[i];
|
||||
if (value > values[primaryIndex])
|
||||
{
|
||||
secondaryIndex = primaryIndex;
|
||||
primaryIndex = i;
|
||||
}
|
||||
else if (value > values[secondaryIndex])
|
||||
{
|
||||
secondaryIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float ResolveSlotHueOffset(TowerCompSlotType slotType)
|
||||
{
|
||||
return slotType switch
|
||||
{
|
||||
TowerCompSlotType.Bearing => 15f,
|
||||
TowerCompSlotType.Base => -15f,
|
||||
_ => 0f
|
||||
};
|
||||
}
|
||||
|
||||
private static float ResolveArrayValue(int[] values)
|
||||
{
|
||||
if (values == null || values.Length <= 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return Mathf.Max(0f, values[0]);
|
||||
}
|
||||
|
||||
private static float ResolveArrayValue(float[] values)
|
||||
{
|
||||
if (values == null || values.Length <= 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
return Mathf.Max(0f, values[0]);
|
||||
}
|
||||
|
||||
private static void IncludeValues(int[] values, ref float min, ref float max, ref bool hasValue)
|
||||
{
|
||||
if (values == null || values.Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
IncludeValue(Mathf.Max(0f, values[i]), ref min, ref max, ref hasValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static void IncludeValues(float[] values, ref float min, ref float max, ref bool hasValue)
|
||||
{
|
||||
if (values == null || values.Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
IncludeValue(Mathf.Max(0f, values[i]), ref min, ref max, ref hasValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static void IncludeValue(float value, ref float min, ref float max, ref bool hasValue)
|
||||
{
|
||||
if (float.IsNaN(value) || float.IsInfinity(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasValue)
|
||||
{
|
||||
min = value;
|
||||
max = value;
|
||||
hasValue = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < min)
|
||||
{
|
||||
min = value;
|
||||
}
|
||||
|
||||
if (value > max)
|
||||
{
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static float Normalize(float value, StatRange range)
|
||||
{
|
||||
float denominator = Mathf.Max(TinyValue, range.Max - range.Min);
|
||||
return Mathf.Clamp01((value - range.Min) / denominator);
|
||||
}
|
||||
|
||||
private static StatRange BuildRange(float min, float max, StatRange fallback)
|
||||
{
|
||||
if (float.IsNaN(min) || float.IsInfinity(min) || float.IsNaN(max) || float.IsInfinity(max))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if (max - min < TinyValue)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return new StatRange(min, max);
|
||||
}
|
||||
|
||||
private static float LerpHue(float fromDegree, float toDegree, float t)
|
||||
{
|
||||
float delta = Mathf.DeltaAngle(fromDegree, toDegree);
|
||||
return fromDegree + delta * Mathf.Clamp01(t);
|
||||
}
|
||||
|
||||
private static float NormalizeHue(float hueDegree)
|
||||
{
|
||||
float normalized = hueDegree % 360f;
|
||||
if (normalized < 0f)
|
||||
{
|
||||
normalized += 360f;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private readonly struct StatRange
|
||||
{
|
||||
public StatRange(float min, float max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public float Min { get; }
|
||||
public float Max { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fb39901e8409cd4db9dbde01bee1ced
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -162,7 +162,9 @@ namespace GeometryTD.CustomUtility
|
||||
MuzzleComponentInstanceId = source.MuzzleComponentInstanceId,
|
||||
BearingComponentInstanceId = source.BearingComponentInstanceId,
|
||||
BaseComponentInstanceId = source.BaseComponentInstanceId,
|
||||
Stats = CloneTowerStats(source.Stats)
|
||||
Stats = CloneTowerStats(source.Stats),
|
||||
ComposedIconSprite = source.ComposedIconSprite,
|
||||
ComposedIconKey = source.ComposedIconKey
|
||||
};
|
||||
}
|
||||
|
||||
@@ -205,3 +207,4 @@ namespace GeometryTD.CustomUtility
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,41 +19,12 @@ namespace GeometryTD.CustomUtility
|
||||
Rarity = RarityType.Green,
|
||||
Endurance = 90f,
|
||||
IsAssembledIntoTower = true,
|
||||
AttackDamage = new[] { 20, 30, 40, 50, 80 },
|
||||
AttackDamage = new[] { 200, 300, 400, 500, 800 },
|
||||
DamageRandomRate = 0.05f,
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
Constraint = string.Empty,
|
||||
Tags = new[] { TagType.Fire }
|
||||
};
|
||||
inventory.MuzzleComponents.Add(muzzle);
|
||||
inventory.MuzzleComponents.Add(new MuzzleCompItemData
|
||||
{
|
||||
InstanceId = 10002,
|
||||
ConfigId = 2,
|
||||
Name = "控制枪口",
|
||||
Rarity = RarityType.Blue,
|
||||
Endurance = 80,
|
||||
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
|
||||
{
|
||||
@@ -63,38 +34,11 @@ namespace GeometryTD.CustomUtility
|
||||
Rarity = RarityType.Green,
|
||||
Endurance = 1f,
|
||||
IsAssembledIntoTower = true,
|
||||
RotateSpeed = new[] { 10f, 12f, 13f, 14f, 15f },
|
||||
AttackRange = new[] { 2f, 2f, 2f, 2f, 2f },
|
||||
RotateSpeed = new[] { 100f, 120f, 130f, 140f, 150f },
|
||||
AttackRange = new[] { 3f, 4f, 5f, 6f, 8f },
|
||||
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 = 20,
|
||||
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
|
||||
{
|
||||
@@ -104,38 +48,11 @@ namespace GeometryTD.CustomUtility
|
||||
Rarity = RarityType.Green,
|
||||
Endurance = 88f,
|
||||
IsAssembledIntoTower = true,
|
||||
AttackSpeed = new[] { 2f, 1.5f, 1f, 0.8f, 0.7f },
|
||||
AttackSpeed = new[] { 1f, 2f, 3f, 3.5f, 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 = 50f,
|
||||
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 = 30f,
|
||||
IsAssembledIntoTower = false,
|
||||
AttackSpeed = new[] { 1f, 1f, 1f, 1f, 1f },
|
||||
AttackPropertyType = AttackPropertyType.Physics,
|
||||
Constraint = string.Empty,
|
||||
Tags = new[] { TagType.Pierce, TagType.Execution }
|
||||
});
|
||||
|
||||
TowerItemData tower = new TowerItemData
|
||||
{
|
||||
@@ -151,35 +68,103 @@ namespace GeometryTD.CustomUtility
|
||||
AttackDamage = new[] { 200, 220, 240, 260, 300 },
|
||||
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 },
|
||||
AttackRange = new[] { 4.5f, 4.5f, 4.5f, 4.5f, 4.5f },
|
||||
AttackSpeed = new[] { 1.0f, 1.2f, 1.3f, 1.4f, 0.5f },
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
AttackPropertyType = AttackPropertyType.Fire,
|
||||
Tags = new[] { TagType.Fire, TagType.BurnSpread }
|
||||
}
|
||||
};
|
||||
|
||||
inventory.MuzzleComponents.Add(muzzle);
|
||||
inventory.BaseComponents.Add(baseComp);
|
||||
inventory.BearingComponents.Add(bearing);
|
||||
inventory.Towers.Add(tower);
|
||||
|
||||
inventory.Towers.Add(new TowerItemData
|
||||
inventory.MuzzleComponents.Add(new MuzzleCompItemData
|
||||
{
|
||||
InstanceId = 90002,
|
||||
Name = "测试防御塔-B",
|
||||
InstanceId = 10002,
|
||||
ConfigId = 2,
|
||||
Name = "控制枪口",
|
||||
Rarity = RarityType.Blue,
|
||||
IsParticipatingInCombat = false,
|
||||
MuzzleComponentInstanceId = 0,
|
||||
BearingComponentInstanceId = 0,
|
||||
BaseComponentInstanceId = 0,
|
||||
Stats = new TowerStatsData
|
||||
{
|
||||
AttackDamage = new[] { 200, 220, 240, 260, 300 },
|
||||
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 },
|
||||
AttackMethodType = AttackMethodType.NormalBullet,
|
||||
AttackPropertyType = AttackPropertyType.Physics,
|
||||
Tags = new[] { TagType.Pierce }
|
||||
}
|
||||
Endurance = 80,
|
||||
IsAssembledIntoTower = false,
|
||||
AttackDamage = new[] { 200, 300, 400, 500, 600 },
|
||||
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 }
|
||||
});
|
||||
|
||||
inventory.BearingComponents.Add(new BearingCompItemData
|
||||
{
|
||||
InstanceId = 20002,
|
||||
ConfigId = 2,
|
||||
Name = "控制轴承",
|
||||
Rarity = RarityType.Blue,
|
||||
Endurance = 20,
|
||||
IsAssembledIntoTower = false,
|
||||
RotateSpeed = new[] { 200f, 250f, 300f, 320f, 350f },
|
||||
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 }
|
||||
});
|
||||
|
||||
inventory.BaseComponents.Add(new BaseCompItemData
|
||||
{
|
||||
InstanceId = 30002,
|
||||
ConfigId = 2,
|
||||
Name = "控制底座",
|
||||
Rarity = RarityType.Blue,
|
||||
Endurance = 50f,
|
||||
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 = 30f,
|
||||
IsAssembledIntoTower = false,
|
||||
AttackSpeed = new[] { 1f, 1f, 1f, 1f, 1f },
|
||||
AttackPropertyType = AttackPropertyType.Physics,
|
||||
Constraint = string.Empty,
|
||||
Tags = new[] { TagType.Pierce, TagType.Execution }
|
||||
});
|
||||
|
||||
inventory.ParticipantTowerInstanceIds.Add(90001);
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomUtility
|
||||
{
|
||||
public static class TowerComposedIconCacheUtility
|
||||
{
|
||||
private const string MuzzleAssetName = "Muzzle";
|
||||
private const string BearingAssetName = "Bearing";
|
||||
private const string BaseAssetName = "Base";
|
||||
|
||||
private static Sprite _muzzleSprite;
|
||||
private static Sprite _bearingSprite;
|
||||
private static Sprite _baseSprite;
|
||||
private static bool s_RequestedMuzzle;
|
||||
private static bool s_RequestedBearing;
|
||||
private static bool s_RequestedBase;
|
||||
|
||||
public static Sprite ResolveTowerIconSprite(
|
||||
TowerItemData tower,
|
||||
IReadOnlyDictionary<long, MuzzleCompItemData> muzzleMap,
|
||||
IReadOnlyDictionary<long, BearingCompItemData> bearingMap,
|
||||
IReadOnlyDictionary<long, BaseCompItemData> baseMap)
|
||||
{
|
||||
if (tower == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!TryGetComponents(tower, muzzleMap, bearingMap, baseMap,
|
||||
out MuzzleCompItemData muzzleComp,
|
||||
out BearingCompItemData bearingComp,
|
||||
out BaseCompItemData baseComp))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Color muzzleColor = IconColorGenerator.GenerateForComponent(muzzleComp);
|
||||
Color bearingColor = IconColorGenerator.GenerateForComponent(bearingComp);
|
||||
Color baseColor = IconColorGenerator.GenerateForComponent(baseComp);
|
||||
|
||||
string cacheKey = TowerIconComposeUtility.BuildCacheKey(
|
||||
tower.MuzzleComponentInstanceId,
|
||||
tower.BearingComponentInstanceId,
|
||||
tower.BaseComponentInstanceId,
|
||||
muzzleColor,
|
||||
bearingColor,
|
||||
baseColor);
|
||||
|
||||
if (tower.ComposedIconSprite != null && string.Equals(tower.ComposedIconKey, cacheKey))
|
||||
{
|
||||
return tower.ComposedIconSprite;
|
||||
}
|
||||
|
||||
EnsureBaseSpritesRequested();
|
||||
if (_muzzleSprite == null || _bearingSprite == null || _baseSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Sprite composedSprite = TowerIconComposeUtility.Compose(
|
||||
_muzzleSprite,
|
||||
muzzleColor,
|
||||
_bearingSprite,
|
||||
bearingColor,
|
||||
_baseSprite,
|
||||
baseColor);
|
||||
|
||||
if (composedSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
tower.ComposedIconSprite = composedSprite;
|
||||
tower.ComposedIconKey = cacheKey;
|
||||
return composedSprite;
|
||||
}
|
||||
|
||||
private static bool TryGetComponents(
|
||||
TowerItemData tower,
|
||||
IReadOnlyDictionary<long, MuzzleCompItemData> muzzleMap,
|
||||
IReadOnlyDictionary<long, BearingCompItemData> bearingMap,
|
||||
IReadOnlyDictionary<long, BaseCompItemData> baseMap,
|
||||
out MuzzleCompItemData muzzleComp,
|
||||
out BearingCompItemData bearingComp,
|
||||
out BaseCompItemData baseComp)
|
||||
{
|
||||
muzzleComp = null;
|
||||
bearingComp = null;
|
||||
baseComp = null;
|
||||
|
||||
if (tower == null || muzzleMap == null || bearingMap == null || baseMap == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return muzzleMap.TryGetValue(tower.MuzzleComponentInstanceId, out muzzleComp) &&
|
||||
bearingMap.TryGetValue(tower.BearingComponentInstanceId, out bearingComp) &&
|
||||
baseMap.TryGetValue(tower.BaseComponentInstanceId, out baseComp) &&
|
||||
muzzleComp != null && bearingComp != null && baseComp != null;
|
||||
}
|
||||
|
||||
private static void EnsureBaseSpritesRequested()
|
||||
{
|
||||
if (GameEntry.SpriteCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_RequestedMuzzle)
|
||||
{
|
||||
s_RequestedMuzzle = true;
|
||||
GameEntry.SpriteCache.GetSprite(MuzzleAssetName, sprite => { _muzzleSprite = sprite; });
|
||||
}
|
||||
|
||||
if (!s_RequestedBearing)
|
||||
{
|
||||
s_RequestedBearing = true;
|
||||
GameEntry.SpriteCache.GetSprite(BearingAssetName, sprite => { _bearingSprite = sprite; });
|
||||
}
|
||||
|
||||
if (!s_RequestedBase)
|
||||
{
|
||||
s_RequestedBase = true;
|
||||
GameEntry.SpriteCache.GetSprite(BaseAssetName, sprite => { _baseSprite = sprite; });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a705ccdd02d2d3440934d4ab06228547
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomUtility
|
||||
{
|
||||
public static class TowerIconComposeUtility
|
||||
{
|
||||
public static Sprite Compose(
|
||||
Sprite muzzleSprite,
|
||||
Color muzzleColor,
|
||||
Sprite bearingSprite,
|
||||
Color bearingColor,
|
||||
Sprite baseSprite,
|
||||
Color baseColor)
|
||||
{
|
||||
if (muzzleSprite == null || bearingSprite == null || baseSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Sprite referenceSprite = muzzleSprite ?? bearingSprite ?? baseSprite;
|
||||
if (referenceSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int width = Mathf.RoundToInt(referenceSprite.rect.width);
|
||||
int height = Mathf.RoundToInt(referenceSprite.rect.height);
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ComposeWithRenderTexture(
|
||||
muzzleSprite,
|
||||
muzzleColor,
|
||||
bearingSprite,
|
||||
bearingColor,
|
||||
baseSprite,
|
||||
baseColor,
|
||||
width,
|
||||
height,
|
||||
referenceSprite);
|
||||
}
|
||||
|
||||
public static string BuildCacheKey(
|
||||
long muzzleId,
|
||||
long bearingId,
|
||||
long baseId,
|
||||
Color muzzleColor,
|
||||
Color bearingColor,
|
||||
Color baseColor)
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"v2|{0}|{1}|{2}|{3}|{4}|{5}",
|
||||
muzzleId,
|
||||
bearingId,
|
||||
baseId,
|
||||
ToColorKey(muzzleColor),
|
||||
ToColorKey(bearingColor),
|
||||
ToColorKey(baseColor));
|
||||
}
|
||||
|
||||
private static Sprite ComposeWithRenderTexture(
|
||||
Sprite muzzleSprite,
|
||||
Color muzzleColor,
|
||||
Sprite bearingSprite,
|
||||
Color bearingColor,
|
||||
Sprite baseSprite,
|
||||
Color baseColor,
|
||||
int width,
|
||||
int height,
|
||||
Sprite referenceSprite)
|
||||
{
|
||||
RenderTexture rt = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGB32);
|
||||
RenderTexture previous = RenderTexture.active;
|
||||
try
|
||||
{
|
||||
RenderTexture.active = rt;
|
||||
GL.PushMatrix();
|
||||
GL.LoadPixelMatrix(0f, width, 0f, height);
|
||||
GL.Clear(true, true, Color.clear);
|
||||
|
||||
DrawSpriteToRenderTarget(baseSprite, baseColor, width, height);
|
||||
DrawSpriteToRenderTarget(bearingSprite, bearingColor, width, height);
|
||||
DrawSpriteToRenderTarget(muzzleSprite, muzzleColor, width, height);
|
||||
|
||||
GL.PopMatrix();
|
||||
|
||||
Texture2D composedTexture = new Texture2D(width, height, TextureFormat.RGBA32, false);
|
||||
composedTexture.ReadPixels(new Rect(0f, 0f, width, height), 0, 0);
|
||||
composedTexture.Apply(false, false);
|
||||
return CreateSprite(composedTexture, referenceSprite);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
RenderTexture.active = previous;
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawSpriteToRenderTarget(Sprite sprite, Color color, int width, int height)
|
||||
{
|
||||
if (sprite == null || sprite.texture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rect textureRect = sprite.textureRect;
|
||||
Texture texture = sprite.texture;
|
||||
Rect uvRect = new Rect(
|
||||
textureRect.x / texture.width,
|
||||
textureRect.y / texture.height,
|
||||
textureRect.width / texture.width,
|
||||
textureRect.height / texture.height);
|
||||
|
||||
Graphics.DrawTexture(new Rect(0f, 0f, width, height), texture, uvRect, 0, 0, 0, 0, color);
|
||||
}
|
||||
|
||||
private static Sprite CreateSprite(Texture2D texture, Sprite referenceSprite)
|
||||
{
|
||||
if (texture == null || referenceSprite == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
float width = Mathf.Max(1f, referenceSprite.rect.width);
|
||||
float height = Mathf.Max(1f, referenceSprite.rect.height);
|
||||
Vector2 pivot = new Vector2(referenceSprite.pivot.x / width, referenceSprite.pivot.y / height);
|
||||
return Sprite.Create(texture, new Rect(0f, 0f, texture.width, texture.height), pivot, referenceSprite.pixelsPerUnit);
|
||||
}
|
||||
|
||||
private static string ToColorKey(Color color)
|
||||
{
|
||||
Color32 c = color;
|
||||
return c.r.ToString("X2", CultureInfo.InvariantCulture) +
|
||||
c.g.ToString("X2", CultureInfo.InvariantCulture) +
|
||||
c.b.ToString("X2", CultureInfo.InvariantCulture) +
|
||||
c.a.ToString("X2", CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9256e2c4f74b7448afbdbac8f7cb26f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user