Files
2026-08-17 18:18:13 +08:00

167 lines
10 KiB
C#

#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace ReplacedPerson.Core
{
public sealed class ReplacedContentCatalog
{
public Dictionary<string, ReplacedCardDefinition> Cards { get; } = new(StringComparer.Ordinal);
public Dictionary<string, ReplacedEndCardDefinition> EndCards { get; } = new(StringComparer.Ordinal);
public Dictionary<string, ReplacedEnemyDefinition> Enemies { get; } = new(StringComparer.Ordinal);
public Dictionary<string, IReplacedPersonEffectResolver> EffectResolvers { get; } = new(StringComparer.Ordinal);
public ReplacedContentCatalog Clone()
{
var clone = new ReplacedContentCatalog();
foreach (var pair in Cards)
clone.Cards.Add(pair.Key, Clone(pair.Value));
foreach (var pair in EndCards)
clone.EndCards.Add(pair.Key, Clone(pair.Value));
foreach (var pair in Enemies)
clone.Enemies.Add(pair.Key, Clone(pair.Value));
foreach (var pair in EffectResolvers)
clone.EffectResolvers.Add(pair.Key, pair.Value);
return clone;
}
public string ComputeHash()
{
var text = new StringBuilder();
foreach (var card in Cards.Values.OrderBy(value => value.Id, StringComparer.Ordinal))
text.Append("C|").Append(card.Id).Append('|').Append(card.Name).Append('|').Append((int)card.EffectType)
.Append('|').Append(card.Cost).Append('|').Append(card.BaseValue).Append('|').Append(card.Factor)
.Append('|').Append(card.SpecialEffectId).Append('\n');
foreach (var card in EndCards.Values.OrderBy(value => value.Id, StringComparer.Ordinal))
text.Append("E|").Append(card.Id).Append('|').Append(card.Name).Append('|').Append(card.Cost).Append('|')
.Append(string.Join(",", card.SlotBase)).Append('|').Append(string.Join(",", card.SlotFactor)).Append('\n');
foreach (var enemy in Enemies.Values.OrderBy(value => value.Id, StringComparer.Ordinal))
text.Append("N|").Append(enemy.Id).Append('|').Append(enemy.Name).Append('|').Append(enemy.MaxHealth).Append('|')
.Append(string.Join(",", enemy.NormalDeck)).Append('|').Append(string.Join(",", enemy.EndDeck)).Append('\n');
foreach (var effect in EffectResolvers.OrderBy(value => value.Key, StringComparer.Ordinal))
text.Append("R|").Append(effect.Key).Append('|').Append(effect.Value.GetType().FullName).Append('\n');
using var sha = SHA256.Create();
var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(text.ToString()));
var hash = new StringBuilder(bytes.Length * 2);
foreach (var value in bytes) hash.Append(value.ToString("x2"));
return hash.ToString();
}
private static ReplacedCardDefinition Clone(ReplacedCardDefinition value) => new()
{
Id = value.Id, Name = value.Name, Description = value.Description, EffectType = value.EffectType,
Cost = value.Cost, BaseValue = value.BaseValue, Factor = value.Factor, SpecialEffectId = value.SpecialEffectId
};
private static ReplacedEndCardDefinition Clone(ReplacedEndCardDefinition value) => new()
{
Id = value.Id, Name = value.Name, Description = value.Description, Cost = value.Cost,
SlotBase = (int[])value.SlotBase.Clone(), SlotFactor = (int[])value.SlotFactor.Clone()
};
private static ReplacedEnemyDefinition Clone(ReplacedEnemyDefinition value) => new()
{
Id = value.Id, Name = value.Name, MaxHealth = value.MaxHealth,
NormalDeck = new List<string>(value.NormalDeck), EndDeck = new List<string>(value.EndDeck),
RewardIds = new List<string>(value.RewardIds)
};
}
public static class ReplacedBuiltInContent
{
public static ReplacedContentCatalog Create()
{
var result = new ReplacedContentCatalog();
AddCard(result, "attack.kill", "杀", ReplacedEffectType.Attack, 2, 2, 2, "直截了当的低费进攻。");
AddCard(result, "attack.elbow", "肘击", ReplacedEffectType.Attack, 3, 3, 2, "稳定进攻,适合试探对位。");
AddCard(result, "attack.break", "破局", ReplacedEffectType.Attack, 4, 3, 3, "高波动进攻。");
AddCard(result, "attack.iron", "铁山靠", ReplacedEffectType.Attack, 5, 5, 2, "高始数压制。");
AddCard(result, "attack.burst", "寸劲", ReplacedEffectType.Attack, 6, 4, 3, "高上限连击。");
AddCard(result, "attack.palm", "黄经极世掌", ReplacedEffectType.Attack, 7, 5, 3, "终盘重击。");
AddCard(result, "dodge.flash", "闪", ReplacedEffectType.Dodge, 2, 4, 2, "闪避成功后获得额外抽牌。");
AddCard(result, "dodge.phase", "虚化", ReplacedEffectType.Dodge, 3, 5, 3, "与反击合成可获得防御。");
AddCard(result, "counter.stance", "架势", ReplacedEffectType.Counter, 1, 2, 2, "低费反击,用来试探攻击并为高费反制保留 COST。");
AddCard(result, "counter.blood", "以血蒙眼", ReplacedEffectType.Counter, 3, 5, 2, "反制进攻并返还伤害。");
AddCard(result, "counter.instant", "一瞬千击", ReplacedEffectType.Counter, 5, 7, 3, "高费反击。");
AddCard(result, "greed.snob", "势利眼", ReplacedEffectType.Dodge, 3, 2, 2, "贪婪的闪避牌。", "greed.draw");
AddCard(result, "greed.turn-profit", "扭亏为盈", ReplacedEffectType.Counter, 4, 3, 3, "反击成功时恢复 COST。", "greed.refund");
AddCard(result, "greed.wage-cut", "降薪", ReplacedEffectType.Attack, 4, 2, 2, "命中后削减对方 COST。", "greed.wage-cut");
AddEnd(result, "end.inherit", "继承", 0, new[] { 0, 0, 0 }, new[] { 0, 0, 0 }, "将左侧普通牌效果继承到三个槽位。");
AddEnd(result, "end.ledger", "账本", 1, new[] { 1, 0, 1 }, new[] { 0, 1, 0 }, "稳定首尾槽。");
AddEnd(result, "end.severance", "断契", 1, new[] { 0, 2, 0 }, new[] { 1, 0, 1 }, "强化中槽始数与边槽因数。");
AddEnd(result, "end.black", "黑色遗嘱", 2, new[] { 1, 1, 1 }, new[] { 1, 0, 1 }, "全面强化,适合孤注一掷。");
var playerDeck = CreateStarterDeck();
result.Enemies.Add("greed.tutorial", new ReplacedEnemyDefinition
{
Id = "greed.tutorial", Name = "贪婪·影子", MaxHealth = 20,
NormalDeck = new List<string> { "attack.kill", "attack.kill", "attack.elbow", "attack.elbow", "dodge.flash", "dodge.flash", "counter.blood", "counter.blood", "greed.snob", "greed.snob", "greed.turn-profit", "greed.wage-cut" },
EndDeck = new List<string>(playerDeck.EndCards), RewardIds = new List<string> { "ornament.gold-eye" }
});
result.Enemies.Add("greed.full", new ReplacedEnemyDefinition
{
Id = "greed.full", Name = "贪婪·葛朗台", MaxHealth = 30,
NormalDeck = new List<string> { "attack.break", "attack.break", "attack.iron", "attack.iron", "dodge.phase", "dodge.phase", "greed.snob", "greed.snob", "greed.turn-profit", "greed.turn-profit", "greed.wage-cut", "greed.wage-cut" },
EndDeck = new List<string>(playerDeck.EndCards), RewardIds = new List<string> { "ornament.gold-eye", "ornament.gold-chain", "ornament.worn-suit", "greed.snob", "greed.turn-profit", "greed.wage-cut" }
});
return result;
}
public static ReplacedDeck CreateStarterDeck() => new()
{
NormalCards = new List<string>
{
"counter.stance", "counter.stance", "counter.blood", "counter.blood", "counter.instant", "counter.instant",
"dodge.flash", "dodge.flash", "attack.kill", "attack.kill", "attack.elbow", "attack.elbow"
},
EndCards = new List<string> { "end.inherit", "end.ledger", "end.severance", "end.black" }
};
private static void AddCard(ReplacedContentCatalog target, string id, string name, ReplacedEffectType type,
int cost, int baseValue, int factor, string description, string special = "")
{
target.Cards.Add(id, new ReplacedCardDefinition
{
Id = id, Name = name, EffectType = type, Cost = cost, BaseValue = baseValue,
Factor = factor, Description = description, SpecialEffectId = special
});
}
private static void AddEnd(ReplacedContentCatalog target, string id, string name, int cost,
int[] slotBase, int[] slotFactor, string description)
{
target.EndCards.Add(id, new ReplacedEndCardDefinition
{
Id = id, Name = name, Cost = cost, SlotBase = slotBase, SlotFactor = slotFactor, Description = description
});
}
}
public static class ReplacedDeckValidator
{
public static IReadOnlyList<string> Validate(ReplacedDeck deck, ReplacedContentCatalog catalog)
{
var errors = new List<string>();
if (deck == null) return new[] { "deck.null" };
if (deck.NormalCards.Count != 12) errors.Add("deck.normal.count");
if (deck.EndCards.Count != 4) errors.Add("deck.end.count");
foreach (var group in deck.NormalCards.GroupBy(value => value, StringComparer.Ordinal))
{
if (!catalog.Cards.ContainsKey(group.Key)) errors.Add("deck.normal.unknown:" + group.Key);
if (group.Count() > 2) errors.Add("deck.normal.duplicate:" + group.Key);
}
foreach (var group in deck.EndCards.GroupBy(value => value, StringComparer.Ordinal))
{
if (!catalog.EndCards.ContainsKey(group.Key)) errors.Add("deck.end.unknown:" + group.Key);
if (group.Count() > 1) errors.Add("deck.end.duplicate:" + group.Key);
}
return errors;
}
}
}