This commit is contained in:
2026-04-30 19:33:16 +08:00
commit fb2252f688
556 changed files with 69721 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using GameFramework;
namespace GeometryTD.CustomUtility
{
public static class AssetUtility
{
public static string GetConfigAsset(string assetName, bool fromBytes)
{
return Utility.Text.Format("Assets/GameMain/Configs/{0}.{1}", assetName, fromBytes ? "bytes" : "txt");
}
public static string GetDataTableAsset(string assetName, bool fromBytes)
{
return Utility.Text.Format("Assets/GameMain/DataTables/{0}.{1}", assetName, fromBytes ? "bytes" : "txt");
}
public static string GetDictionaryAsset(string assetName, bool fromBytes)
{
return Utility.Text.Format("Assets/GameMain/Localization/{0}/Dictionaries/{1}.{2}",
GameEntry.Localization.Language, assetName, fromBytes ? "bytes" : "xml");
}
public static string GetFontAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/Fonts/{0}.ttf", assetName);
}
public static string GetTMPFontAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/Fonts/{0}.asset", assetName);
}
public static string GetSceneAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/Scenes/{0}.unity", assetName);
}
public static string GetMusicAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/Music/{0}.mp3", assetName);
}
public static string GetSoundAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/Sounds/{0}.wav", assetName);
}
public static string GetEntityAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/Entities/{0}.prefab", assetName);
}
public static string GetUIFormAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/UI/UIForms/{0}.prefab", assetName);
}
public static string GetUISoundAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/UI/UISounds/{0}.wav", assetName);
}
public static string GetLevelMapAsset(string assetName)
{
return Utility.Text.Format("Assets/GameMain/Entities/LevelMap/Level{0}.prefab", assetName);
}
public static string GetTextureAsset(string assetName)
{
// Assets/GameMain/Textures/Capsule.png
return Utility.Text.Format("Assets/GameMain/Textures/{0}.png", assetName);
}
}
}
+27
View File
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using UnityGameFramework.Runtime;
namespace GeometryTD.CustomUtility
{
public static class EnumUtility<T> where T : struct, System.Enum
{
private static readonly Dictionary<string, T> _enumCache = new();
public static T Get(string value)
{
if (!_enumCache.TryGetValue(value, out T result))
{
if (System.Enum.TryParse(value, true, out result))
{
_enumCache[value] = result;
}
else
{
Log.Error($"Enum 解析失败:类型:{typeof(T).Name} 不包含值 {value}");
}
}
return result;
}
}
}
+443
View File
@@ -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; }
}
}
}
+236
View File
@@ -0,0 +1,236 @@
using System;
using GeometryTD.Definition;
using UnityEngine;
namespace GeometryTD.CustomUtility
{
public static class InventoryCloneUtility
{
public 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;
}
public static MuzzleCompItemData CloneMuzzleComp(MuzzleCompItemData source)
{
if (source == null)
{
return null;
}
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
};
}
public static BearingCompItemData CloneBearingComp(BearingCompItemData source)
{
if (source == null)
{
return null;
}
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)
};
}
public static BaseCompItemData CloneBaseComp(BaseCompItemData source)
{
if (source == null)
{
return null;
}
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
};
}
public static TowerItemData CloneTower(TowerItemData source)
{
if (source == null)
{
return null;
}
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),
ComposedIconSprite = source.ComposedIconSprite,
ComposedIconKey = source.ComposedIconKey
};
}
public 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,
TagRuntimes = CloneTagRuntimes(source.TagRuntimes),
Tags = CloneTags(source.Tags)
};
}
public static int[] CloneIntArray(int[] source)
{
return source != null ? (int[])source.Clone() : Array.Empty<int>();
}
public static float[] CloneFloatArray(float[] source)
{
return source != null ? (float[])source.Clone() : Array.Empty<float>();
}
public static TagType[] CloneTags(TagType[] source)
{
return source != null ? (TagType[])source.Clone() : Array.Empty<TagType>();
}
public static TagRuntimeData[] CloneTagRuntimes(TagRuntimeData[] source)
{
if (source == null || source.Length <= 0)
{
return Array.Empty<TagRuntimeData>();
}
TagRuntimeData[] cloned = new TagRuntimeData[source.Length];
for (int i = 0; i < source.Length; i++)
{
TagRuntimeData runtime = source[i];
if (runtime == null)
{
continue;
}
cloned[i] = new TagRuntimeData
{
TagType = runtime.TagType,
TotalStack = runtime.TotalStack
};
}
return cloned;
}
}
}
@@ -0,0 +1,180 @@
using System.Collections.Generic;
using GeometryTD.Definition;
using UnityEngine;
namespace GeometryTD.CustomUtility
{
public static class InventoryParticipantUtility
{
public static ParticipantTowerAssignResult TryAddParticipantTower(
BackpackInventoryData inventory,
long towerInstanceId,
int maxCount)
{
if (inventory == null || towerInstanceId <= 0)
{
return new ParticipantTowerAssignResult
{
TowerInstanceId = towerInstanceId,
FailureReason = ParticipantTowerAssignFailureReason.TowerMissing,
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
};
}
int resolvedMaxCount = Mathf.Max(1, maxCount);
NormalizeParticipantState(inventory, resolvedMaxCount);
CombatParticipantTowerValidationResult validationResult =
CombatParticipantTowerValidationService.ValidateTower(inventory, towerInstanceId);
if (!validationResult.IsValid)
{
return new ParticipantTowerAssignResult
{
TowerInstanceId = towerInstanceId,
FailureReason = validationResult.FailureReason == CombatParticipantTowerValidationFailureReason.TowerMissing
? ParticipantTowerAssignFailureReason.TowerMissing
: ParticipantTowerAssignFailureReason.InvalidTower,
ValidationFailureReason = validationResult.FailureReason
};
}
inventory.ParticipantTowerInstanceIds ??= new List<long>();
if (inventory.ParticipantTowerInstanceIds.Contains(towerInstanceId))
{
if (validationResult.Tower != null)
{
validationResult.Tower.IsParticipatingInCombat = true;
}
return new ParticipantTowerAssignResult
{
TowerInstanceId = towerInstanceId,
FailureReason = ParticipantTowerAssignFailureReason.AlreadyAssigned,
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
};
}
if (inventory.ParticipantTowerInstanceIds.Count >= resolvedMaxCount)
{
return new ParticipantTowerAssignResult
{
TowerInstanceId = towerInstanceId,
FailureReason = ParticipantTowerAssignFailureReason.ParticipantAreaFull,
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
};
}
inventory.ParticipantTowerInstanceIds.Add(towerInstanceId);
if (validationResult.Tower != null)
{
validationResult.Tower.IsParticipatingInCombat = true;
}
return new ParticipantTowerAssignResult
{
TowerInstanceId = towerInstanceId,
FailureReason = ParticipantTowerAssignFailureReason.None,
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
};
}
public static bool TryRemoveParticipantTower(BackpackInventoryData inventory, long towerInstanceId, int maxCount)
{
if (inventory == null || towerInstanceId <= 0)
{
return false;
}
NormalizeParticipantState(inventory, maxCount);
if (inventory.ParticipantTowerInstanceIds == null)
{
return false;
}
bool removed = inventory.ParticipantTowerInstanceIds.Remove(towerInstanceId);
if (!removed)
{
return false;
}
if (TryGetTowerById(inventory, towerInstanceId, out TowerItemData tower))
{
tower.IsParticipatingInCombat = false;
}
return true;
}
public static void NormalizeParticipantState(BackpackInventoryData inventory, int maxCount)
{
if (inventory == null)
{
return;
}
int resolvedMaxCount = Mathf.Max(1, maxCount);
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;
}
}
HashSet<long> uniqueIds = new HashSet<long>();
List<long> normalizedIds = new List<long>(inventory.ParticipantTowerInstanceIds.Count);
for (int i = 0; i < inventory.ParticipantTowerInstanceIds.Count; i++)
{
if (normalizedIds.Count >= resolvedMaxCount)
{
break;
}
long towerId = inventory.ParticipantTowerInstanceIds[i];
if (towerId <= 0 || !uniqueIds.Add(towerId))
{
continue;
}
if (!towerMap.TryGetValue(towerId, out TowerItemData tower))
{
continue;
}
tower.IsParticipatingInCombat = true;
normalizedIds.Add(towerId);
}
inventory.ParticipantTowerInstanceIds = normalizedIds;
}
public static bool TryGetTowerById(BackpackInventoryData inventory, long towerInstanceId, out TowerItemData tower)
{
tower = null;
if (inventory?.Towers == null || towerInstanceId <= 0)
{
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;
}
}
}
+198
View File
@@ -0,0 +1,198 @@
using GeometryTD.Definition;
namespace GeometryTD.CustomUtility
{
public static class InventorySeedUtility
{
private static readonly TagType[] FirePool = { TagType.Fire };
private static readonly TagType[] IceFreezePool = { TagType.Ice, TagType.FreezeMask };
private static readonly TagType[] CritPiercePool = { TagType.Pierce, TagType.Crit };
private static readonly TagType[] IceShatterPool = { TagType.Ice, TagType.Shatter };
private static readonly TagType[] PierceOverpenetratePool = { TagType.Pierce, TagType.Overpenetrate };
private static readonly TagType[] IceAbsoluteZeroPool = { TagType.Ice, TagType.AbsoluteZero };
private static readonly TagType[] PierceExecutionPool = { TagType.Pierce, TagType.Execution };
public static BackpackInventoryData CreateSampleInventory(int runSeed = 0)
{
BackpackInventoryData inventory = new BackpackInventoryData
{
Gold = 500
};
MuzzleCompItemData muzzle = new MuzzleCompItemData
{
InstanceId = 10001,
ConfigId = 1,
Name = "元素枪口",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 90f,
IsAssembledIntoTower = true,
AttackDamage = new[] { 200, 300, 400, 500, 800 },
DamageRandomRate = 0.05f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = ResolveSeedTags(FirePool, RarityType.Blue, runSeed, 10001, 1)
};
BearingCompItemData bearing = new BearingCompItemData
{
InstanceId = 20001,
ConfigId = 1,
Name = "元素轴承",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 1f,
IsAssembledIntoTower = true,
RotateSpeed = new[] { 100f, 120f, 130f, 140f, 150f },
AttackRange = new[] { 3f, 4f, 5f, 6f, 8f },
Constraint = string.Empty,
Tags = ResolveSeedTags(FirePool, RarityType.Blue, runSeed, 20001, 1)
};
BaseCompItemData baseComp = new BaseCompItemData
{
InstanceId = 30001,
ConfigId = 1,
Name = "元素底座",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 88f,
IsAssembledIntoTower = true,
AttackSpeed = new[] { 1f, 2f, 3f, 3.5f, 0.7f },
AttackPropertyType = AttackPropertyType.Fire,
Constraint = string.Empty,
Tags = ResolveSeedTags(FirePool, RarityType.Blue, runSeed, 30001, 1)
};
TowerItemData tower = new TowerItemData
{
InstanceId = 90001,
Name = "测试防御塔-A",
Rarity = InventoryRarityRuleService.ResolveTowerRarity(
muzzle.Rarity,
bearing.Rarity,
baseComp.Rarity),
IsParticipatingInCombat = true,
MuzzleComponentInstanceId = muzzle.InstanceId,
BearingComponentInstanceId = bearing.InstanceId,
BaseComponentInstanceId = baseComp.InstanceId,
Stats = new TowerStatsData
{
AttackDamage = new[] { 200, 220, 240, 260, 300 },
DamageRandomRate = 0f,
RotateSpeed = new[] { 200f, 210f, 220f, 230f, 240f },
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,
TagRuntimes = TowerTagAggregationService.AggregateTowerTags(
muzzle.Tags,
bearing.Tags,
baseComp.Tags)
}
};
tower.Stats.Tags = TowerTagAggregationService.FlattenUniqueTags(tower.Stats.TagRuntimes);
inventory.MuzzleComponents.Add(muzzle);
inventory.BaseComponents.Add(baseComp);
inventory.BearingComponents.Add(bearing);
inventory.Towers.Add(tower);
inventory.MuzzleComponents.Add(new MuzzleCompItemData
{
InstanceId = 10002,
ConfigId = 2,
Name = "控制枪口",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 80,
IsAssembledIntoTower = false,
AttackDamage = new[] { 200, 300, 400, 500, 600 },
DamageRandomRate = 0.01f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = ResolveSeedTags(IceFreezePool, RarityType.Blue, runSeed, 10002, 2)
});
inventory.MuzzleComponents.Add(new MuzzleCompItemData
{
InstanceId = 10003,
ConfigId = 3,
Name = "穿透枪口",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Purple),
Endurance = 1f,
IsAssembledIntoTower = false,
AttackDamage = new[] { 50, 55, 60, 80, 90 },
DamageRandomRate = 0.02f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = ResolveSeedTags(CritPiercePool, RarityType.Purple, runSeed, 10003, 3)
});
inventory.BearingComponents.Add(new BearingCompItemData
{
InstanceId = 20002,
ConfigId = 2,
Name = "控制轴承",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(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 = ResolveSeedTags(IceShatterPool, RarityType.Blue, runSeed, 20002, 2)
});
inventory.BearingComponents.Add(new BearingCompItemData
{
InstanceId = 20003,
ConfigId = 3,
Name = "穿透轴承",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(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 = ResolveSeedTags(PierceOverpenetratePool, RarityType.Purple, runSeed, 20003, 3)
});
inventory.BaseComponents.Add(new BaseCompItemData
{
InstanceId = 30002,
ConfigId = 2,
Name = "控制底座",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 50f,
IsAssembledIntoTower = false,
AttackSpeed = new[] { 4f, 4.2f, 4.4f, 4.6f, 4.8f },
AttackPropertyType = AttackPropertyType.Ice,
Constraint = string.Empty,
Tags = ResolveSeedTags(IceAbsoluteZeroPool, RarityType.Blue, runSeed, 30002, 2)
});
inventory.BaseComponents.Add(new BaseCompItemData
{
InstanceId = 30003,
ConfigId = 3,
Name = "穿透底座",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Purple),
Endurance = 30f,
IsAssembledIntoTower = false,
AttackSpeed = new[] { 1f, 1f, 1f, 1f, 1f },
AttackPropertyType = AttackPropertyType.Physics,
Constraint = string.Empty,
Tags = ResolveSeedTags(PierceExecutionPool, RarityType.Purple, runSeed, 30003, 3)
});
inventory.ParticipantTowerInstanceIds.Add(90001);
return inventory;
}
private static TagType[] ResolveSeedTags(TagType[] possibleTags, RarityType rarity, int runSeed, long instanceId, int configId)
{
return ComponentTagGenerationService.ResolveComponentTags(
possibleTags,
rarity,
InventoryTagRandomContext.CreateSeed(runSeed, instanceId, configId));
}
}
}
+282
View File
@@ -0,0 +1,282 @@
using System;
using System.Collections.Generic;
using System.Text;
using GeometryTD.Definition;
namespace GeometryTD.CustomUtility
{
public static class ItemDescUtility
{
public static string BuildTowerDesc(TowerStatsData towerData)
{
StringBuilder sb = new StringBuilder();
// MuzzleComp
sb.Append("攻击伤害:");
for (int i = 0; i < towerData.AttackDamage.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(towerData.AttackDamage[i]);
}
sb.Append('\n');
sb.Append($"伤害浮动:{towerData.DamageRandomRate:P0}\n");
sb.Append($"攻击方式:{ConvertAttackMethod(towerData.AttackMethodType)}\n");
// BearingComp
sb.Append("旋转速度:");
for (int i = 0; i < towerData.RotateSpeed.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(towerData.RotateSpeed[i]);
}
sb.Append('\n');
sb.Append("攻击范围:");
for (int i = 0; i < towerData.AttackRange.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(towerData.AttackRange[i]);
}
sb.Append('\n');
// BaseComp
sb.Append("攻击速度:");
for (int i = 0; i < towerData.AttackSpeed.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(towerData.AttackSpeed[i]);
}
sb.Append('\n');
sb.Append($"伤害属性:{ConvertAttackProperty(towerData.AttackPropertyType)}\n");
return sb.ToString();
}
public static string BuildTowerDesc(
TowerItemData tower,
IReadOnlyDictionary<long, MuzzleCompItemData> muzzleMap,
IReadOnlyDictionary<long, BearingCompItemData> bearingMap,
IReadOnlyDictionary<long, BaseCompItemData> baseMap)
{
if (tower == null)
{
return string.Empty;
}
StringBuilder sb = new StringBuilder();
sb.Append(BuildTowerDesc(tower.Stats ?? new TowerStatsData()));
float enduranceRate = ResolveTowerEnduranceRate(tower, muzzleMap, bearingMap, baseMap);
sb.AppendLine($"平均耐久: {enduranceRate * 100f:0.#}");
CombatParticipantTowerValidationFailureReason failureReason =
ResolveTowerValidationFailureReason(tower, muzzleMap, bearingMap, baseMap);
if (failureReason != CombatParticipantTowerValidationFailureReason.None)
{
sb.AppendLine("状态:已损坏");
sb.Append($"参战限制:{CombatParticipantTowerValidationText.GetFailureReasonMessage(failureReason)}");
}
return sb.ToString();
}
public static float ResolveComponentEnduranceRate(TowerCompItemData item)
{
if (item == null)
{
return 1f;
}
return UnityEngine.Mathf.Clamp01(item.Endurance / 100f);
}
public static float ResolveTowerEnduranceRate(
TowerItemData tower,
IReadOnlyDictionary<long, MuzzleCompItemData> muzzleMap,
IReadOnlyDictionary<long, BearingCompItemData> bearingMap,
IReadOnlyDictionary<long, BaseCompItemData> baseMap)
{
if (tower == null)
{
return 1f;
}
float sum = 0f;
int count = 0;
if (muzzleMap != null && muzzleMap.TryGetValue(tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzle))
{
sum += ResolveComponentEnduranceRate(muzzle);
count++;
}
if (bearingMap != null && bearingMap.TryGetValue(tower.BearingComponentInstanceId, out BearingCompItemData bearing))
{
sum += ResolveComponentEnduranceRate(bearing);
count++;
}
if (baseMap != null && baseMap.TryGetValue(tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
{
sum += ResolveComponentEnduranceRate(baseComp);
count++;
}
if (count <= 0)
{
return 1f;
}
return UnityEngine.Mathf.Clamp01(sum / count);
}
public static string BuildMuzzleDesc(MuzzleCompItemData muzzleData)
{
StringBuilder sb = new StringBuilder();
sb.Append("攻击伤害:");
for (int i = 0; i < muzzleData.AttackDamage.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(muzzleData.AttackDamage[i]);
}
sb.Append('\n');
sb.AppendLine($"伤害浮动:{muzzleData.DamageRandomRate:P0}");
sb.AppendLine($"攻击方式:{ConvertAttackMethod(muzzleData.AttackMethodType)}");
sb.AppendLine($"当前耐久:{muzzleData.Endurance}");
AppendComponentBrokenStatus(sb, muzzleData);
return sb.ToString();
}
public static string BuildBearingDesc(BearingCompItemData bearingData)
{
StringBuilder sb = new StringBuilder();
sb.Append("旋转速度:");
for (int i = 0; i < bearingData.RotateSpeed.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(bearingData.RotateSpeed[i]);
}
sb.Append('\n');
sb.Append("攻击范围:");
for (int i = 0; i < bearingData.AttackRange.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(bearingData.AttackRange[i]);
}
sb.Append('\n');
sb.AppendLine($"当前耐久:{bearingData.Endurance}");
AppendComponentBrokenStatus(sb, bearingData);
return sb.ToString();
}
public static string BuildBaseDesc(BaseCompItemData baseData)
{
StringBuilder sb = new StringBuilder();
sb.Append("攻击速度:");
for (int i = 0; i < baseData.AttackSpeed.Length; i++)
{
if (i != 0) sb.Append("|");
sb.Append(baseData.AttackSpeed[i]);
}
sb.Append('\n');
sb.AppendLine($"伤害属性:{ConvertAttackProperty(baseData.AttackPropertyType)}");
sb.AppendLine($"当前耐久:{baseData.Endurance}");
AppendComponentBrokenStatus(sb, baseData);
return sb.ToString();
}
private static void AppendComponentBrokenStatus(StringBuilder sb, TowerCompItemData component)
{
if (sb == null || component == null || component.Endurance > 0f)
{
return;
}
sb.AppendLine("状态:已损坏,无法参战。");
}
private static CombatParticipantTowerValidationFailureReason ResolveTowerValidationFailureReason(
TowerItemData tower,
IReadOnlyDictionary<long, MuzzleCompItemData> muzzleMap,
IReadOnlyDictionary<long, BearingCompItemData> bearingMap,
IReadOnlyDictionary<long, BaseCompItemData> baseMap)
{
if (tower == null)
{
return CombatParticipantTowerValidationFailureReason.TowerMissing;
}
if (muzzleMap == null || !muzzleMap.TryGetValue(tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzle))
{
return CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent;
}
if (muzzle.Endurance <= 0f)
{
return CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent;
}
if (bearingMap == null || !bearingMap.TryGetValue(tower.BearingComponentInstanceId, out BearingCompItemData bearing))
{
return CombatParticipantTowerValidationFailureReason.MissingBearingComponent;
}
if (bearing.Endurance <= 0f)
{
return CombatParticipantTowerValidationFailureReason.BrokenBearingComponent;
}
if (baseMap == null || !baseMap.TryGetValue(tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
{
return CombatParticipantTowerValidationFailureReason.MissingBaseComponent;
}
if (baseComp.Endurance <= 0f)
{
return CombatParticipantTowerValidationFailureReason.BrokenBaseComponent;
}
return CombatParticipantTowerValidationFailureReason.None;
}
public static string ConvertAttackMethod(AttackMethodType type)
{
return type switch
{
AttackMethodType.None => "无效",
AttackMethodType.NormalBullet => "发送子弹",
AttackMethodType.Range => "范围攻击",
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
public static string ConvertAttackProperty(AttackPropertyType type)
{
return type switch
{
AttackPropertyType.None => "无效",
AttackPropertyType.Physics => "<color=#ffff00ff>物理</color>", //yellow
AttackPropertyType.Fire => "<color=#ff0000ff>火</color>", //red
AttackPropertyType.Water => "<color=#00ffffff>水</color>", //cyan
AttackPropertyType.Earth => "<color=#00ff00ff>自然</color>", //lime
AttackPropertyType.Poison => "<color=#008000ff>毒</color>", //green
AttackPropertyType.Ice => "<color=#0000a0ff>冰</color>", //darkblue
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
}
}
+26
View File
@@ -0,0 +1,26 @@
using System;
using Newtonsoft.Json;
namespace GeometryTD.CustomUtility
{
/// <summary>
/// Newtonsoft.Json 函数集辅助器。
/// </summary>
public class JsonNetUtility : GameFramework.Utility.Json.IJsonHelper
{
public string ToJson(object obj)
{
return JsonConvert.SerializeObject(obj);
}
public T ToObject<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
public object ToObject(Type objectType, string json)
{
return JsonConvert.DeserializeObject(json, objectType);
}
}
}
+122
View File
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using UnityEngine;
using Random = System.Random;
namespace GeometryTD.CustomUtility
{
public static class ShopPriceRuleService
{
public static int ResolveRandomBuyPrice(IReadOnlyList<DRShopPrice> shopPriceRows, RarityType rarity, Random random)
{
if (!TryFindPriceRow(shopPriceRows, rarity, out DRShopPrice row) || row == null)
{
return 0;
}
int min = Mathf.Max(0, row.MinPrice);
int max = Mathf.Max(min, row.MaxPrice);
return random != null ? random.Next(min, max + 1) : min;
}
public static int ResolveComponentSalePrice(TowerCompItemData component, IDataTable<DRShopPrice> shopPriceTable = null)
{
if (component == null)
{
return 0;
}
return ResolveBasePrice(component.Rarity, shopPriceTable);
}
public static bool TryResolveTowerSalePrice(
TowerItemData tower,
BackpackInventoryData inventory,
out int price,
IDataTable<DRShopPrice> shopPriceTable = null)
{
price = 0;
if (tower == null || inventory == null)
{
return false;
}
if (!TryGetComponentById(inventory.MuzzleComponents, tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzleComp) ||
!TryGetComponentById(inventory.BearingComponents, tower.BearingComponentInstanceId, out BearingCompItemData bearingComp) ||
!TryGetComponentById(inventory.BaseComponents, tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
{
return false;
}
price = ResolveComponentSalePrice(muzzleComp, shopPriceTable) +
ResolveComponentSalePrice(bearingComp, shopPriceTable) +
ResolveComponentSalePrice(baseComp, shopPriceTable);
return price > 0;
}
public static int ResolveBasePrice(RarityType rarity, IDataTable<DRShopPrice> shopPriceTable = null)
{
IDataTable<DRShopPrice> resolvedTable = shopPriceTable ?? GameEntry.DataTable.GetDataTable<DRShopPrice>();
if (resolvedTable == null)
{
return 0;
}
DRShopPrice[] rows = resolvedTable.GetAllDataRows();
if (!TryFindPriceRow(rows, rarity, out DRShopPrice row) || row == null)
{
return 0;
}
int min = Mathf.Max(0, row.MinPrice);
int max = Mathf.Max(min, row.MaxPrice);
return Mathf.RoundToInt((min + max) * 0.5f);
}
private static bool TryFindPriceRow(IReadOnlyList<DRShopPrice> rows, RarityType rarity, out DRShopPrice result)
{
result = null;
if (rows == null)
{
return false;
}
for (int i = 0; i < rows.Count; i++)
{
DRShopPrice row = rows[i];
if (row != null && row.Rarity == rarity)
{
result = row;
return true;
}
}
return false;
}
private static bool TryGetComponentById<TComp>(IReadOnlyList<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;
}
}
}
@@ -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; });
}
}
}
}
+148
View File
@@ -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);
}
}
}
+24
View File
@@ -0,0 +1,24 @@
//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:ellan@gameframework.cn
//------------------------------------------------------------
using System;
namespace GeometryTD.CustomUtility
{
public static class WebUtility
{
public static string EscapeString(string stringToEscape)
{
return Uri.EscapeDataString(stringToEscape);
}
public static string UnescapeString(string stringToUnescape)
{
return Uri.UnescapeDataString(stringToUnescape);
}
}
}