- S4-03 | 先固化 Tag 系统设计与首发范围
- S4-04 | 实现组件实例 Tag 的统一生成入口 - S4-05 | 实现组塔后的 Tag 汇总与展示入口
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace GeometryTD.Definition
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class TagRuntimeData
|
||||
{
|
||||
public TagType TagType { get; set; }
|
||||
public int TotalStack { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71784829e4844ae3acbd58fb4f88a1ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -18,6 +18,7 @@ namespace GeometryTD.Definition
|
||||
public float[] AttackSpeed { get; set; }
|
||||
public AttackMethodType AttackMethodType { get; set; }
|
||||
public AttackPropertyType AttackPropertyType { get; set; }
|
||||
public TagRuntimeData[] TagRuntimes { get; set; }
|
||||
public TagType[] Tags { get; set; }
|
||||
}
|
||||
|
||||
@@ -68,4 +69,4 @@ namespace GeometryTD.Definition
|
||||
|
||||
[JsonIgnore] public string ComposedIconKey { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace GeometryTD.Definition
|
||||
{
|
||||
public enum InventoryTagSourceType : byte
|
||||
{
|
||||
Seed = 1,
|
||||
Shop = 2,
|
||||
Drop = 3,
|
||||
Reward = 4,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a31a31d8d96e4d659fb5be2a8f76836d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GeometryTD.Definition
|
||||
{
|
||||
public static class InventoryTagRuleService
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<TagType, RarityType> DefaultMinRarityByTag =
|
||||
new Dictionary<TagType, RarityType>
|
||||
{
|
||||
{ TagType.Fire, RarityType.White },
|
||||
{ TagType.Ice, RarityType.White },
|
||||
{ TagType.Crit, RarityType.White },
|
||||
{ TagType.Shatter, RarityType.Green },
|
||||
{ TagType.Inferno, RarityType.Purple },
|
||||
{ TagType.AbsoluteZero, RarityType.Purple },
|
||||
{ TagType.Execution, RarityType.Purple },
|
||||
};
|
||||
|
||||
private static readonly HashSet<TagType> SupportedLaunchTags = new HashSet<TagType>(DefaultMinRarityByTag.Keys);
|
||||
|
||||
public static TagType[] ResolveComponentTags(
|
||||
IReadOnlyList<TagType> possibleTags,
|
||||
RarityType rarity,
|
||||
InventoryTagSourceType sourceType,
|
||||
long itemInstanceId,
|
||||
int configId,
|
||||
IReadOnlyDictionary<TagType, RarityType> minRarityByTag = null)
|
||||
{
|
||||
TagType[] eligibleTags = GetEligibleTags(possibleTags, rarity, minRarityByTag);
|
||||
if (eligibleTags.Length <= 0)
|
||||
{
|
||||
return Array.Empty<TagType>();
|
||||
}
|
||||
|
||||
Random random = new Random(BuildStableSeed(rarity, sourceType, itemInstanceId, configId));
|
||||
int tagBudget = ResolveTagBudget(rarity, random);
|
||||
if (tagBudget <= 0)
|
||||
{
|
||||
return Array.Empty<TagType>();
|
||||
}
|
||||
|
||||
int finalCount = Math.Min(tagBudget, eligibleTags.Length);
|
||||
List<TagType> pool = new List<TagType>(eligibleTags);
|
||||
TagType[] result = new TagType[finalCount];
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
int index = random.Next(0, pool.Count);
|
||||
result[i] = pool[index];
|
||||
pool.RemoveAt(index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TagType[] GetEligibleTags(
|
||||
IReadOnlyList<TagType> possibleTags,
|
||||
RarityType rarity,
|
||||
IReadOnlyDictionary<TagType, RarityType> minRarityByTag = null)
|
||||
{
|
||||
if (possibleTags == null || possibleTags.Count <= 0)
|
||||
{
|
||||
return Array.Empty<TagType>();
|
||||
}
|
||||
|
||||
RarityType normalizedRarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity);
|
||||
IReadOnlyDictionary<TagType, RarityType> rarityLookup = minRarityByTag ?? DefaultMinRarityByTag;
|
||||
HashSet<TagType> uniqueTags = new HashSet<TagType>();
|
||||
|
||||
for (int i = 0; i < possibleTags.Count; i++)
|
||||
{
|
||||
TagType tagType = possibleTags[i];
|
||||
if (!IsSupportedLaunchTag(tagType) || tagType == TagType.None || !Enum.IsDefined(typeof(TagType), tagType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryGetMinRarity(tagType, rarityLookup, out RarityType minRarity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (normalizedRarity < InventoryRarityRuleService.NormalizeComponentRarity(minRarity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uniqueTags.Add(tagType);
|
||||
}
|
||||
|
||||
if (uniqueTags.Count <= 0)
|
||||
{
|
||||
return Array.Empty<TagType>();
|
||||
}
|
||||
|
||||
TagType[] result = new TagType[uniqueTags.Count];
|
||||
uniqueTags.CopyTo(result);
|
||||
Array.Sort(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int ResolveTagBudget(RarityType rarity, Random random)
|
||||
{
|
||||
RarityType normalizedRarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity);
|
||||
random ??= new Random(0);
|
||||
|
||||
switch (normalizedRarity)
|
||||
{
|
||||
case RarityType.White:
|
||||
return random.Next(0, 2);
|
||||
case RarityType.Green:
|
||||
return 1;
|
||||
case RarityType.Blue:
|
||||
return random.Next(1, 3);
|
||||
case RarityType.Purple:
|
||||
return 2;
|
||||
case RarityType.Red:
|
||||
return random.Next(2, 4);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSupportedLaunchTag(TagType tagType)
|
||||
{
|
||||
return SupportedLaunchTags.Contains(tagType);
|
||||
}
|
||||
|
||||
private static bool TryGetMinRarity(
|
||||
TagType tagType,
|
||||
IReadOnlyDictionary<TagType, RarityType> rarityLookup,
|
||||
out RarityType minRarity)
|
||||
{
|
||||
if (rarityLookup != null && rarityLookup.TryGetValue(tagType, out minRarity))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DefaultMinRarityByTag.TryGetValue(tagType, out minRarity))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
minRarity = RarityType.White;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int BuildStableSeed(
|
||||
RarityType rarity,
|
||||
InventoryTagSourceType sourceType,
|
||||
long itemInstanceId,
|
||||
int configId)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int seed = 17;
|
||||
seed = seed * 31 + (int)InventoryRarityRuleService.NormalizeComponentRarity(rarity);
|
||||
seed = seed * 31 + (int)sourceType;
|
||||
seed = seed * 31 + configId;
|
||||
seed = seed * 31 + (int)itemInstanceId;
|
||||
seed = seed * 31 + (int)(itemInstanceId >> 32);
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56c9c8fd48d246a882bfae5f8c8c02e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GeometryTD.Definition
|
||||
{
|
||||
public static class TowerTagAggregationService
|
||||
{
|
||||
public static TagRuntimeData[] AggregateTowerTags(params TagType[][] componentTags)
|
||||
{
|
||||
Dictionary<TagType, int> stackByTag = new Dictionary<TagType, int>();
|
||||
if (componentTags != null)
|
||||
{
|
||||
for (int i = 0; i < componentTags.Length; i++)
|
||||
{
|
||||
TagType[] tags = componentTags[i];
|
||||
if (tags == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < tags.Length; j++)
|
||||
{
|
||||
TagType tagType = tags[j];
|
||||
if (tagType == TagType.None || !Enum.IsDefined(typeof(TagType), tagType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stackByTag.TryGetValue(tagType, out int stack))
|
||||
{
|
||||
stackByTag[tagType] = stack + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
stackByTag[tagType] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stackByTag.Count <= 0)
|
||||
{
|
||||
return Array.Empty<TagRuntimeData>();
|
||||
}
|
||||
|
||||
List<TagRuntimeData> runtimes = new List<TagRuntimeData>(stackByTag.Count);
|
||||
foreach (KeyValuePair<TagType, int> pair in stackByTag)
|
||||
{
|
||||
runtimes.Add(new TagRuntimeData
|
||||
{
|
||||
TagType = pair.Key,
|
||||
TotalStack = Math.Max(1, pair.Value)
|
||||
});
|
||||
}
|
||||
|
||||
runtimes.Sort((left, right) => left.TagType.CompareTo(right.TagType));
|
||||
return runtimes.ToArray();
|
||||
}
|
||||
|
||||
public static TagRuntimeData[] BuildRuntimeTagsFromUniqueTags(IReadOnlyList<TagType> tags)
|
||||
{
|
||||
if (tags == null || tags.Count <= 0)
|
||||
{
|
||||
return Array.Empty<TagRuntimeData>();
|
||||
}
|
||||
|
||||
HashSet<TagType> uniqueTags = new HashSet<TagType>();
|
||||
for (int i = 0; i < tags.Count; i++)
|
||||
{
|
||||
TagType tagType = tags[i];
|
||||
if (tagType == TagType.None || !Enum.IsDefined(typeof(TagType), tagType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uniqueTags.Add(tagType);
|
||||
}
|
||||
|
||||
if (uniqueTags.Count <= 0)
|
||||
{
|
||||
return Array.Empty<TagRuntimeData>();
|
||||
}
|
||||
|
||||
TagType[] orderedTags = new TagType[uniqueTags.Count];
|
||||
uniqueTags.CopyTo(orderedTags);
|
||||
Array.Sort(orderedTags);
|
||||
|
||||
TagRuntimeData[] runtimes = new TagRuntimeData[orderedTags.Length];
|
||||
for (int i = 0; i < orderedTags.Length; i++)
|
||||
{
|
||||
runtimes[i] = new TagRuntimeData
|
||||
{
|
||||
TagType = orderedTags[i],
|
||||
TotalStack = 1
|
||||
};
|
||||
}
|
||||
|
||||
return runtimes;
|
||||
}
|
||||
|
||||
public static TagType[] FlattenUniqueTags(IReadOnlyList<TagRuntimeData> tagRuntimes)
|
||||
{
|
||||
if (tagRuntimes == null || tagRuntimes.Count <= 0)
|
||||
{
|
||||
return Array.Empty<TagType>();
|
||||
}
|
||||
|
||||
List<TagType> tags = new List<TagType>(tagRuntimes.Count);
|
||||
for (int i = 0; i < tagRuntimes.Count; i++)
|
||||
{
|
||||
TagRuntimeData runtime = tagRuntimes[i];
|
||||
if (runtime == null || runtime.TagType == TagType.None || !Enum.IsDefined(typeof(TagType), runtime.TagType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (runtime.TotalStack <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tags.Add(runtime.TagType);
|
||||
}
|
||||
|
||||
if (tags.Count <= 0)
|
||||
{
|
||||
return Array.Empty<TagType>();
|
||||
}
|
||||
|
||||
tags.Sort();
|
||||
return tags.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a2b16218c9c4f90a90c12b975d9a5c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user