Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DualSwords v1.0.2
plugins/DualSwords/DualSwords.Core.dll
Decompiled 7 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("DualSwords.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+7b567f59d6531825de4c7a0c772c630fbd264809")] [assembly: AssemblyProduct("DualSwords.Core")] [assembly: AssemblyTitle("DualSwords.Core")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DualSwords.Core { public static class BalanceRules { private static readonly HashSet<string> NonScalable = new HashSet<string>(StringComparer.Ordinal) { "DyrnwynHiltFragment", "DyrnwynBladeFragment", "DyrnwynTipFragment", "Wishbone", "CryptKey", "DragonTear", "FaderDrop", "Upgrader1Weapon" }; public const float DualAttackForce = 20f; public const float DualBlockForce = 20f; public static int BlockPowerFor(int baseBlockPower) { if (baseBlockPower < 12) { return baseBlockPower + 8; } if (baseBlockPower < 21) { return baseBlockPower + 4; } return baseBlockPower; } public static int SecondaryStaminaFor(int baseSecondaryStamina) { return (int)Math.Round((double)baseSecondaryStamina * 0.8, MidpointRounding.AwayFromZero); } public static float WeightFor(float baseWeight) { return baseWeight * 2f; } public static bool IsScalable(string itemName) { if (string.IsNullOrWhiteSpace(itemName)) { return false; } if (NonScalable.Contains(itemName)) { return false; } if (itemName.StartsWith("Trophy", StringComparison.Ordinal)) { return false; } if (itemName.StartsWith("Upgrader", StringComparison.Ordinal) && itemName.EndsWith("Weapon", StringComparison.Ordinal)) { return false; } return true; } public static IReadOnlyList<RequirementSpec> DoubleRequirements(IReadOnlyList<RequirementSpec> requirements) { return RecipeScaling.Scale(requirements, 2); } } public static class BoneResolver { public const int NotFound = -1; public static int ResolveIndex(IReadOnlyList<string> boneNames, IReadOnlyList<string> jointChain) { if (boneNames == null) { throw new ArgumentNullException("boneNames"); } if (jointChain == null) { throw new ArgumentNullException("jointChain"); } foreach (string item in jointChain) { if (string.IsNullOrEmpty(item)) { continue; } for (int i = 0; i < boneNames.Count; i++) { if (string.Equals(boneNames[i], item, StringComparison.Ordinal)) { return i; } } } return -1; } } public static class BuildOrder { private enum State { Unvisited, InProgress, Done } public static bool Resolve(IReadOnlyList<DualSwordDeclaration> declarations, out IReadOnlyList<DualSwordDeclaration> ordered, out IReadOnlyList<string> errors) { Dictionary<string, DualSwordDeclaration> dictionary = new Dictionary<string, DualSwordDeclaration>(); foreach (DualSwordDeclaration declaration in declarations) { dictionary[declaration.Id] = declaration; } string[] catalogueIds = dictionary.Keys.ToArray(); Dictionary<string, State> state = new Dictionary<string, State>(); List<DualSwordDeclaration> list = new List<DualSwordDeclaration>(); List<string> list2 = new List<string>(); bool flag = false; foreach (DualSwordDeclaration declaration2 in declarations) { if (!Visit(declaration2, dictionary, (IReadOnlyCollection<string>)(object)catalogueIds, state, list, list2)) { flag = true; } } if (flag) { ordered = new DualSwordDeclaration[0]; errors = list2; return false; } ordered = list; errors = list2; return true; } private static bool Visit(DualSwordDeclaration declaration, IReadOnlyDictionary<string, DualSwordDeclaration> byId, IReadOnlyCollection<string> catalogueIds, IDictionary<string, State> state, List<DualSwordDeclaration> result, List<string> errors) { state.TryGetValue(declaration.Id, out var value); switch (value) { case State.Done: return true; case State.InProgress: errors.Add(declaration.Id + ": цикл зависимостей — предмет требует сам себя через цепочку рецептов"); return false; default: state[declaration.Id] = State.InProgress; foreach (string item in DependenciesOf(declaration, catalogueIds)) { if (byId.TryGetValue(item, out var value2) && !Visit(value2, byId, catalogueIds, state, result, errors)) { return false; } } state[declaration.Id] = State.Done; result.Add(declaration); return true; } } public static IReadOnlyList<string> DependenciesOf(DualSwordDeclaration declaration, IReadOnlyCollection<string> catalogueIds) { HashSet<string> hashSet = new HashSet<string>(catalogueIds); HashSet<string> hashSet2 = new HashSet<string>(); List<string> list = new List<string>(); foreach (RequirementSpec item in declaration.DualRecipe?.Requirements ?? new RequirementSpec[0]) { if (item != null && !string.IsNullOrEmpty(item.Item) && hashSet.Contains(item.Item) && hashSet2.Add(item.Item)) { list.Add(item.Item); } } return list; } public static IReadOnlyList<string> Dependents(string failedId, IReadOnlyList<DualSwordDeclaration> declarations) { string[] catalogueIds = declarations.Select((DualSwordDeclaration declaration) => declaration.Id).ToArray(); Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(); foreach (DualSwordDeclaration declaration in declarations) { foreach (string item in DependenciesOf(declaration, (IReadOnlyCollection<string>)(object)catalogueIds)) { if (!dictionary.TryGetValue(item, out var value)) { value = (dictionary[item] = new List<string>()); } value.Add(declaration.Id); } } List<string> list2 = new List<string>(); HashSet<string> hashSet = new HashSet<string> { failedId }; Queue<string> queue = new Queue<string>(); queue.Enqueue(failedId); while (queue.Count > 0) { string key = queue.Dequeue(); if (!dictionary.TryGetValue(key, out var value2)) { continue; } foreach (string item2 in value2) { if (hashSet.Add(item2)) { list2.Add(item2); queue.Enqueue(item2); } } } return list2; } } public static class ArmorySwords { public static IReadOnlyList<DualSwordDeclaration> All() { return new DualSwordDeclaration[8] { Flint(), Eikthyr(), Abyssal(), EldersBalance(), Bonemass(), ModersGrasp(), Queen(), Fader() }; } private static DualSwordDeclaration Flint() { return new DualSwordDeclaration("VAFlint_SwordDual", "VAFlint_Sword", optional: true, customRecipe: false, null, new StatSnapshot(Damage(15f), Damage(6f), 200, 50, 4, 0.8f, 6, 12, 0), new StatSnapshot(Damage(15f), Damage(6f), 200, 50, 12, 1.6f, 6, 10, 0), new RecipeSpec("piece_workbench", 1, new RequirementSpec[3] { new RequirementSpec("Wood", 2, 0), new RequirementSpec("Flint", 6, 3), new RequirementSpec("LeatherScraps", 0, 2) }), new RecipeSpec("piece_workbench", 1, new RequirementSpec[3] { new RequirementSpec("Wood", 4, 0), new RequirementSpec("Flint", 12, 6), new RequirementSpec("LeatherScraps", 0, 4) }), new LocalizedText("Flint dual swords", "Парные кремнёвые мечи"), new LocalizedText("A pair of flint swords, swung like the berzerkr's axes.", "Пара кремнёвых мечей, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Eikthyr() { return new DualSwordDeclaration("VAAntler_SwordDual", "VAAntler_Sword", optional: true, customRecipe: false, null, new StatSnapshot(Damage(16f, 8f, 0f, 0f, 0f, 6f), Damage(2f, 4f, 0f, 0f, 0f, 5f), 200, 50, 8, 0.8f, 8, 16, 0), new StatSnapshot(Damage(16f, 8f, 0f, 0f, 0f, 6f), Damage(2f, 4f, 0f, 0f, 0f, 5f), 200, 50, 16, 1.6f, 8, 13, 0), new RecipeSpec("piece_workbench", 1, new RequirementSpec[4] { new RequirementSpec("FineWood", 3, 1), new RequirementSpec("Resin", 16, 8), new RequirementSpec("HardAntler", 3, 3), new RequirementSpec("TrophyEikthyr", 1, 1) }), new RecipeSpec("piece_workbench", 1, new RequirementSpec[4] { new RequirementSpec("FineWood", 6, 2), new RequirementSpec("Resin", 32, 16), new RequirementSpec("HardAntler", 6, 6), new RequirementSpec("TrophyEikthyr", 1, 1) }), new LocalizedText("Eikthyr dual swords", "Парные мечи Эйктюра"), new LocalizedText("A pair of Eikthyr swords, swung like the berzerkr's axes.", "Пара мечей Эйктюра, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Abyssal() { return new DualSwordDeclaration("VASwordChitinDual", "VASwordChitin", optional: true, customRecipe: false, null, new StatSnapshot(Damage(25f, 20f), Damage(2f, 4f), 200, 50, 18, 0.8f, 10, 20, 2), new StatSnapshot(Damage(25f, 20f), Damage(2f, 4f), 200, 50, 22, 1.6f, 10, 16, 2), new RecipeSpec("piece_workbench", 1, new RequirementSpec[3] { new RequirementSpec("FineWood", 2, 1), new RequirementSpec("Chitin", 30, 15), new RequirementSpec("DeerHide", 2, 0) }), new RecipeSpec("piece_workbench", 1, new RequirementSpec[3] { new RequirementSpec("FineWood", 4, 2), new RequirementSpec("Chitin", 60, 30), new RequirementSpec("DeerHide", 4, 0) }), new LocalizedText("Abyssal dual swords", "Парные мечи Бездны"), new LocalizedText("A pair of Abyssal swords, swung like the berzerkr's axes.", "Пара мечей Бездны, что рубят как топоры берсерка.")); } private static DualSwordDeclaration EldersBalance() { return new DualSwordDeclaration("VAVine_SwordDual", "VAVine_Sword", optional: true, customRecipe: false, null, new StatSnapshot(Damage(40f, 0f, 0f, 0f, 0f, 0f, 0f, 10f), Damage(6f, 0f, 0f, 0f, 0f, 0f, 0f, 2f), 200, 50, 12, 0.8f, 8, 16, 1), new StatSnapshot(Damage(40f, 0f, 0f, 0f, 0f, 0f, 0f, 10f), Damage(6f, 0f, 0f, 0f, 0f, 0f, 0f, 2f), 200, 50, 16, 1.6f, 8, 13, 1), new RecipeSpec("forge", 1, new RequirementSpec[5] { new RequirementSpec("Bronze", 2, 1), new RequirementSpec("Stone", 16, 8), new RequirementSpec("CryptKey", 1, 0), new RequirementSpec("TrophyTheElder", 1, 0), new RequirementSpec("RoundLog", 0, 4) }), new RecipeSpec("forge", 1, new RequirementSpec[5] { new RequirementSpec("Bronze", 4, 2), new RequirementSpec("Stone", 32, 16), new RequirementSpec("CryptKey", 1, 0), new RequirementSpec("TrophyTheElder", 1, 0), new RequirementSpec("RoundLog", 0, 8) }), new LocalizedText("Elder's Balance dual swords", "Парные мечи Равновесия Старейшины"), new LocalizedText("A pair of Elder's Balance swords, swung like the berzerkr's axes.", "Пара мечей Равновесия Старейшины, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Bonemass() { return new DualSwordDeclaration("VABonemassSwordDual", "VABonemassSword", optional: true, customRecipe: false, null, new StatSnapshot(Damage(65f, 0f, 0f, 0f, 0f, 0f, 20f), Damage(6f, 0f, 0f, 0f, 0f, 0f, 5f), 200, 50, 30, 0.8f, 15, 30, 0), new StatSnapshot(Damage(65f, 0f, 0f, 0f, 0f, 0f, 20f), Damage(6f, 0f, 0f, 0f, 0f, 0f, 5f), 200, 50, 30, 1.6f, 15, 24, 0), new RecipeSpec("forge", 1, new RequirementSpec[6] { new RequirementSpec("WitheredBone", 10, 5), new RequirementSpec("Iron", 22, 15), new RequirementSpec("Wishbone", 1, 0), new RequirementSpec("TrophyBonemass", 1, 0), new RequirementSpec("ElderBark", 0, 2), new RequirementSpec("LeatherScraps", 0, 2) }), new RecipeSpec("forge", 1, new RequirementSpec[6] { new RequirementSpec("WitheredBone", 20, 10), new RequirementSpec("Iron", 44, 30), new RequirementSpec("Wishbone", 1, 0), new RequirementSpec("TrophyBonemass", 1, 0), new RequirementSpec("ElderBark", 0, 4), new RequirementSpec("LeatherScraps", 0, 4) }), new LocalizedText("Bonemass dual swords", "Парные мечи Бонемасса"), new LocalizedText("A pair of Bonemass swords, swung like the berzerkr's axes.", "Пара мечей Бонемасса, что рубят как топоры берсерка.")); } private static DualSwordDeclaration ModersGrasp() { return new DualSwordDeclaration("VASwordModerDual", "VASwordModer", optional: true, customRecipe: false, null, new StatSnapshot(Damage(35f, 0f, 30f, 0f, 25f), Damage(2f, 0f, 4f, 0f, 1f), 200, 50, 30, 0.8f, 12, 24, 3), new StatSnapshot(Damage(35f, 0f, 30f, 0f, 25f), Damage(2f, 0f, 4f, 0f, 1f), 200, 50, 30, 1.6f, 12, 19, 3), new RecipeSpec("forge", 1, new RequirementSpec[6] { new RequirementSpec("ElderBark", 4, 2), new RequirementSpec("Obsidian", 30, 15), new RequirementSpec("DragonTear", 10, 0), new RequirementSpec("TrophyDragonQueen", 1, 0), new RequirementSpec("Silver", 0, 2), new RequirementSpec("JuteRed", 0, 2) }), new RecipeSpec("forge", 1, new RequirementSpec[6] { new RequirementSpec("ElderBark", 8, 4), new RequirementSpec("Obsidian", 60, 30), new RequirementSpec("DragonTear", 10, 0), new RequirementSpec("TrophyDragonQueen", 1, 0), new RequirementSpec("Silver", 0, 4), new RequirementSpec("JuteRed", 0, 4) }), new LocalizedText("Moder's Grasp dual swords", "Парные мечи Хватки Модер"), new LocalizedText("A pair of Moder's Grasp swords, swung like the berzerkr's axes.", "Пара мечей Хватки Модер, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Queen() { return new DualSwordDeclaration("VASwordQueenDual", "VASwordQueen", optional: true, customRecipe: false, null, new StatSnapshot(Damage(95f, 0f, 0f, 0f, 0f, 30f, 25f), Damage(6f, 0f, 0f, 0f, 0f, 5f, 5f), 200, 50, 52, 0.8f, 16, 32, 5), new StatSnapshot(Damage(95f, 0f, 0f, 0f, 0f, 30f, 25f), Damage(6f, 0f, 0f, 0f, 0f, 5f, 5f), 200, 50, 52, 1.6f, 16, 26, 5), new RecipeSpec("blackforge", 1, new RequirementSpec[5] { new RequirementSpec("YggdrasilWood", 3, 1), new RequirementSpec("Eitr", 10, 5), new RequirementSpec("JuteBlue", 3, 1), new RequirementSpec("TrophySeekerQueen", 1, 0), new RequirementSpec("Carapace", 0, 6) }), new RecipeSpec("blackforge", 1, new RequirementSpec[5] { new RequirementSpec("YggdrasilWood", 6, 2), new RequirementSpec("Eitr", 20, 10), new RequirementSpec("JuteBlue", 6, 2), new RequirementSpec("TrophySeekerQueen", 1, 0), new RequirementSpec("Carapace", 0, 12) }), new LocalizedText("Queen dual swords", "Парные мечи Королевы"), new LocalizedText("A pair of Queen swords, swung like the berzerkr's axes.", "Пара мечей Королевы, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Fader() { return new DualSwordDeclaration("VASwordFaderDual", "VASwordFader", optional: true, customRecipe: false, null, new StatSnapshot(Damage(145f, 0f, 0f, 25f, 0f, 0f, 25f), Damage(3f, 0f, 0f, 1f, 0f, 0f, 1f), 200, 50, 60, 0.8f, 18, 30, 3), new StatSnapshot(Damage(145f, 0f, 0f, 25f, 0f, 0f, 25f), Damage(3f, 0f, 0f, 1f, 0f, 0f, 1f), 200, 50, 60, 1.6f, 18, 24, 3), new RecipeSpec("blackforge", 1, new RequirementSpec[4] { new RequirementSpec("FlametalNew", 30, 30), new RequirementSpec("CharredBone", 30, 30), new RequirementSpec("TrophyFader", 1, 0), new RequirementSpec("FaderDrop", 1, 0) }), new RecipeSpec("blackforge", 1, new RequirementSpec[4] { new RequirementSpec("FlametalNew", 60, 60), new RequirementSpec("CharredBone", 60, 60), new RequirementSpec("TrophyFader", 1, 0), new RequirementSpec("FaderDrop", 1, 0) }), new LocalizedText("Fader dual swords", "Парные мечи Фейдера"), new LocalizedText("A pair of Fader swords, swung like the berzerkr's axes.", "Пара мечей Фейдера, что рубят как топоры берсерка.")); } private static DamageProfile Damage(float slash = 0f, float blunt = 0f, float pierce = 0f, float fire = 0f, float frost = 0f, float lightning = 0f, float poison = 0f, float spirit = 0f) { return new DamageProfile { Slash = slash, Blunt = blunt, Pierce = pierce, Fire = fire, Frost = frost, Lightning = lightning, Poison = poison, Spirit = spirit }; } } public static class Catalogue { public static IReadOnlyList<DualSwordDeclaration> All() { List<DualSwordDeclaration> list = new List<DualSwordDeclaration>(); list.AddRange(VanillaSwords.All()); list.AddRange(NiedhoggSwords.All()); list.AddRange(GoldSwords.All()); list.AddRange(ArmorySwords.All()); return list; } } public static class GoldSwords { public static IReadOnlyList<DualSwordDeclaration> All() { return new DualSwordDeclaration[3] { Gold(), BloodLightning(), FrostFire() }; } private static DualSwordDeclaration Gold() { return new DualSwordDeclaration("SwordGoldDual", "SwordGold", optional: false, customRecipe: false, null, new StatSnapshot(Damage(170f), Damage(10f), 400, 50, 66, 0.8f, 16, 28, 6), new StatSnapshot(Damage(170f), Damage(10f), 400, 50, 66, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("Gold", 20, 10), new RequirementSpec("MoldSword", 1, 0), new RequirementSpec("Frostwood", 10, 5) }), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("Gold", 40, 20), new RequirementSpec("MoldSword", 2, 0), new RequirementSpec("Frostwood", 20, 10) }), new LocalizedText("Gold dual swords", "Парные золотые мечи"), new LocalizedText("A pair of gold swords, swung like the berzerkr's axes.", "Пара золотых мечей, что рубят как топоры берсерка.")); } private static DualSwordDeclaration BloodLightning() { return new DualSwordDeclaration("SwordGold_BloodLightningDual", "SwordGold_BloodLightning", optional: false, customRecipe: true, null, new StatSnapshot(Damage(180f, 0f, 0f, 0f, 0f, 45f), Damage(10f, 0f, 0f, 0f, 0f, 5f), 400, 50, 66, 0.8f, 16, 28, 6), new StatSnapshot(Damage(180f, 0f, 0f, 0f, 0f, 45f), Damage(10f, 0f, 0f, 0f, 0f, 5f), 400, 50, 66, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordGold", 1, 0), new RequirementSpec("Gold", 20, 10), new RequirementSpec("OrbThunderBlood", 1, 1) }), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordGoldDual", 1, 0), new RequirementSpec("Gold", 40, 20), new RequirementSpec("OrbThunderBlood", 2, 2) }), new LocalizedText("Gold Blood-Lightning dual swords", "Парные золотые мечи Крови и Молнии"), new LocalizedText("A pair of gold Blood-Lightning swords, swung like the berzerkr's axes.", "Пара золотых мечей Крови и Молнии, что рубят как топоры берсерка.")); } private static DualSwordDeclaration FrostFire() { return new DualSwordDeclaration("SwordGold_FrostFireDual", "SwordGold_FrostFire", optional: false, customRecipe: true, null, new StatSnapshot(Damage(138f, 0f, 0f, 12f, 88f), Damage(10f, 0f, 0f, 3f, 3f), 400, 50, 66, 0.8f, 16, 28, 6), new StatSnapshot(Damage(138f, 0f, 0f, 12f, 88f), Damage(10f, 0f, 0f, 3f, 3f), 400, 50, 66, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordGold", 1, 0), new RequirementSpec("Gold", 20, 10), new RequirementSpec("OrbFrostFire", 1, 1) }), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordGoldDual", 1, 0), new RequirementSpec("Gold", 40, 20), new RequirementSpec("OrbFrostFire", 2, 2) }), new LocalizedText("Gold Frost-Fire dual swords", "Парные золотые мечи Мороза и Огня"), new LocalizedText("A pair of gold Frost-Fire swords, swung like the berzerkr's axes.", "Пара золотых мечей Мороза и Огня, что рубят как топоры берсерка.")); } private static DamageProfile Damage(float slash = 0f, float blunt = 0f, float pierce = 0f, float fire = 0f, float frost = 0f, float lightning = 0f, float poison = 0f, float spirit = 0f) { return new DamageProfile { Slash = slash, Blunt = blunt, Pierce = pierce, Fire = fire, Frost = frost, Lightning = lightning, Poison = poison, Spirit = spirit }; } } public static class NiedhoggSwords { public static IReadOnlyList<DualSwordDeclaration> All() { return new DualSwordDeclaration[3] { Blood(), Lightning(), Nature() }; } private static DualSwordDeclaration Blood() { return new DualSwordDeclaration("SwordNiedhoggBloodDual", "SwordNiedhoggBlood", optional: false, customRecipe: true, null, new StatSnapshot(Damage(135f), Damage(6f), 300, 50, 57, 0.8f, 16, 28, 6), new StatSnapshot(Damage(135f), Damage(6f), 300, 50, 57, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordNiedhogg", 1, 0), new RequirementSpec("FlametalNew", 6, 6), new RequirementSpec("GemstoneRed", 1, 1) }), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordNiedhoggDual", 1, 0), new RequirementSpec("FlametalNew", 12, 12), new RequirementSpec("GemstoneRed", 2, 2) }), new LocalizedText("Niedhogg's Blood dual swords", "Парные мечи Нидхёгг Крови"), new LocalizedText("A pair of Niedhogg's Blood swords, swung like the berzerkr's axes.", "Пара кроволомных мечей Нидхёгг, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Lightning() { return new DualSwordDeclaration("SwordNiedhoggLightningDual", "SwordNiedhoggLightning", optional: false, customRecipe: true, null, new StatSnapshot(Damage(135f, 0f, 0f, 0f, 0f, 10f), Damage(6f), 300, 50, 57, 0.8f, 16, 28, 6), new StatSnapshot(Damage(135f, 0f, 0f, 0f, 0f, 10f), Damage(6f), 300, 50, 57, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordNiedhogg", 1, 0), new RequirementSpec("FlametalNew", 6, 6), new RequirementSpec("GemstoneBlue", 1, 1) }), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordNiedhoggDual", 1, 0), new RequirementSpec("FlametalNew", 12, 12), new RequirementSpec("GemstoneBlue", 2, 2) }), new LocalizedText("Niedhogg's Lightning dual swords", "Парные мечи Нидхёгг Молнии"), new LocalizedText("A pair of Niedhogg's Lightning swords, swung like the berzerkr's axes.", "Пара громовых мечей Нидхёгг, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Nature() { return new DualSwordDeclaration("SwordNiedhoggNatureDual", "SwordNiedhoggNature", optional: false, customRecipe: true, null, new StatSnapshot(Damage(135f, 0f, 0f, 0f, 0f, 0f, 10f), Damage(6f), 300, 50, 57, 0.8f, 16, 28, 6), new StatSnapshot(Damage(135f, 0f, 0f, 0f, 0f, 0f, 10f), Damage(6f), 300, 50, 57, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordNiedhogg", 1, 0), new RequirementSpec("FlametalNew", 6, 6), new RequirementSpec("GemstoneGreen", 1, 1) }), new RecipeSpec("blackforge", 4, new RequirementSpec[3] { new RequirementSpec("SwordNiedhoggDual", 1, 0), new RequirementSpec("FlametalNew", 12, 12), new RequirementSpec("GemstoneGreen", 2, 2) }), new LocalizedText("Niedhogg's Nature dual swords", "Парные мечи Нидхёгг Природы"), new LocalizedText("A pair of Niedhogg's Nature swords, swung like the berzerkr's axes.", "Пара ядовитых мечей Нидхёгг, что рубят как топоры берсерка.")); } private static DamageProfile Damage(float slash = 0f, float blunt = 0f, float pierce = 0f, float fire = 0f, float frost = 0f, float lightning = 0f, float poison = 0f, float spirit = 0f) { return new DamageProfile { Slash = slash, Blunt = blunt, Pierce = pierce, Fire = fire, Frost = frost, Lightning = lightning, Poison = poison, Spirit = spirit }; } } public static class VanillaSwords { public static IReadOnlyList<DualSwordDeclaration> All() { return new DualSwordDeclaration[8] { Wood(), Bronze(), Iron(), Silver(), Blackmetal(), Mistwalker(), Niedhogg(), Dyrnwyn() }; } private static DualSwordDeclaration Wood() { return new DualSwordDeclaration("SwordWoodDual", "SwordWood", optional: false, customRecipe: false, null, new StatSnapshot(Damage(1f), Damage(1f), 200, 50, 12, 0.8f, 4, 4, 1), new StatSnapshot(Damage(1f), Damage(1f), 200, 50, 16, 1.6f, 4, 3, 1), new RecipeSpec("piece_workbench", 1, new RequirementSpec[4] { new RequirementSpec("Wood", 5, 1), new RequirementSpec("FineWood", 3, 10), new RequirementSpec("RoundLog", 2, 2), new RequirementSpec("Upgrader1Weapon", 1, 0) }), new RecipeSpec("piece_workbench", 1, new RequirementSpec[4] { new RequirementSpec("Wood", 10, 2), new RequirementSpec("FineWood", 6, 20), new RequirementSpec("RoundLog", 4, 4), new RequirementSpec("Upgrader1Weapon", 1, 0) }), new LocalizedText("Wood dual swords", "Парные деревянные мечи"), new LocalizedText("A pair of wood swords, swung like the berzerkr's axes.", "Пара деревянных мечей, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Bronze() { return new DualSwordDeclaration("SwordBronzeDual", "SwordBronze", optional: false, customRecipe: false, null, new StatSnapshot(Damage(35f), Damage(6f), 200, 50, 12, 0.8f, 8, 16, 1), new StatSnapshot(Damage(35f), Damage(6f), 200, 50, 16, 1.6f, 8, 13, 1), new RecipeSpec("forge", 1, new RequirementSpec[3] { new RequirementSpec("Wood", 2, 1), new RequirementSpec("Bronze", 8, 4), new RequirementSpec("LeatherScraps", 2, 1) }), new RecipeSpec("forge", 1, new RequirementSpec[3] { new RequirementSpec("Wood", 4, 2), new RequirementSpec("Bronze", 16, 8), new RequirementSpec("LeatherScraps", 4, 2) }), new LocalizedText("Bronze dual swords", "Парные бронзовые мечи"), new LocalizedText("A pair of bronze swords, swung like the berzerkr's axes.", "Пара бронзовых мечей, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Iron() { return new DualSwordDeclaration("SwordIronDual", "SwordIron", optional: false, customRecipe: false, null, new StatSnapshot(Damage(55f), Damage(6f), 200, 50, 21, 0.8f, 10, 20, 2), new StatSnapshot(Damage(55f), Damage(6f), 200, 50, 21, 1.6f, 10, 16, 2), new RecipeSpec("forge", 2, new RequirementSpec[3] { new RequirementSpec("Wood", 2, 1), new RequirementSpec("Iron", 20, 10), new RequirementSpec("LeatherScraps", 3, 2) }), new RecipeSpec("forge", 2, new RequirementSpec[3] { new RequirementSpec("Wood", 4, 2), new RequirementSpec("Iron", 40, 20), new RequirementSpec("LeatherScraps", 6, 4) }), new LocalizedText("Iron dual swords", "Парные железные мечи"), new LocalizedText("A pair of iron swords, swung like the berzerkr's axes.", "Пара железных мечей, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Silver() { return new DualSwordDeclaration("SwordSilverDual", "SwordSilver", optional: false, customRecipe: false, null, new StatSnapshot(Damage(75f, 0f, 0f, 0f, 0f, 0f, 0f, 30f), Damage(6f, 0f, 0f, 0f, 0f, 0f, 0f, 5f), 200, 50, 30, 1f, 12, 24, 3), new StatSnapshot(Damage(75f, 0f, 0f, 0f, 0f, 0f, 0f, 30f), Damage(6f, 0f, 0f, 0f, 0f, 0f, 0f, 5f), 200, 50, 30, 2f, 12, 19, 3), new RecipeSpec("forge", 3, new RequirementSpec[4] { new RequirementSpec("Wood", 2, 1), new RequirementSpec("Silver", 40, 20), new RequirementSpec("LeatherScraps", 3, 1), new RequirementSpec("Iron", 5, 3) }), new RecipeSpec("forge", 3, new RequirementSpec[4] { new RequirementSpec("Wood", 4, 2), new RequirementSpec("Silver", 80, 40), new RequirementSpec("LeatherScraps", 6, 2), new RequirementSpec("Iron", 10, 6) }), new LocalizedText("Silver dual swords", "Парные серебряные мечи"), new LocalizedText("A pair of silver swords, swung like the berzerkr's axes.", "Пара серебряных мечей, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Blackmetal() { return new DualSwordDeclaration("SwordBlackmetalDual", "SwordBlackmetal", optional: false, customRecipe: false, null, new StatSnapshot(Damage(95f), Damage(6f), 200, 50, 39, 0.8f, 14, 28, 4), new StatSnapshot(Damage(95f), Damage(6f), 200, 50, 39, 1.6f, 14, 22, 4), new RecipeSpec("forge", 4, new RequirementSpec[3] { new RequirementSpec("FineWood", 2, 0), new RequirementSpec("BlackMetal", 20, 10), new RequirementSpec("LinenThread", 5, 5) }), new RecipeSpec("forge", 4, new RequirementSpec[3] { new RequirementSpec("FineWood", 4, 0), new RequirementSpec("BlackMetal", 40, 20), new RequirementSpec("LinenThread", 10, 10) }), new LocalizedText("Black metal dual swords", "Парные чернометаллические мечи"), new LocalizedText("A pair of black metal swords, swung like the berzerkr's axes.", "Пара чернометаллических мечей, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Mistwalker() { return new DualSwordDeclaration("SwordMistwalkerDual", "SwordMistwalker", optional: false, customRecipe: false, null, new StatSnapshot(Damage(75f, 0f, 0f, 0f, 40f), Damage(0f, 0f, 0f, 0f, 6f, 0f, 0f, 5f), 250, 50, 48, 0.8f, 16, 32, 5), new StatSnapshot(Damage(75f, 0f, 0f, 0f, 40f), Damage(0f, 0f, 0f, 0f, 6f, 0f, 0f, 5f), 250, 50, 48, 1.6f, 16, 26, 5), new RecipeSpec("blackforge", 1, new RequirementSpec[4] { new RequirementSpec("FineWood", 3, 0), new RequirementSpec("Iron", 15, 10), new RequirementSpec("Eitr", 10, 5), new RequirementSpec("Wisp", 3, 1) }), new RecipeSpec("blackforge", 1, new RequirementSpec[4] { new RequirementSpec("FineWood", 6, 0), new RequirementSpec("Iron", 30, 20), new RequirementSpec("Eitr", 20, 10), new RequirementSpec("Wisp", 6, 2) }), new LocalizedText("Mistwalker dual swords", "Парные мечи Мистуокер"), new LocalizedText("A pair of Mistwalker swords, swung like the berzerkr's axes.", "Пара мечей Мистуокер, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Niedhogg() { return new DualSwordDeclaration("SwordNiedhoggDual", "SwordNiedhogg", optional: false, customRecipe: false, null, new StatSnapshot(Damage(135f), Damage(6f), 300, 50, 57, 0.8f, 16, 28, 6), new StatSnapshot(Damage(135f), Damage(6f), 300, 50, 57, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 3, new RequirementSpec[3] { new RequirementSpec("CharredBone", 3, 0), new RequirementSpec("FlametalNew", 12, 10), new RequirementSpec("AskHide", 2, 2) }), new RecipeSpec("blackforge", 3, new RequirementSpec[3] { new RequirementSpec("CharredBone", 6, 0), new RequirementSpec("FlametalNew", 24, 20), new RequirementSpec("AskHide", 4, 4) }), new LocalizedText("Niedhogg dual swords", "Парные мечи Нидхёгг"), new LocalizedText("A pair of Niedhogg swords, swung like the berzerkr's axes.", "Пара мечей Нидхёгг, что рубят как топоры берсерка.")); } private static DualSwordDeclaration Dyrnwyn() { return new DualSwordDeclaration("SwordDyrnwynDual", "SwordDyrnwyn", optional: false, customRecipe: true, null, new StatSnapshot(Damage(145f, 0f, 0f, 10f), Damage(6f), 300, 50, 57, 0.8f, 16, 28, 6), new StatSnapshot(Damage(145f, 0f, 0f, 10f), Damage(6f), 300, 50, 57, 1.6f, 16, 22, 6), new RecipeSpec("blackforge", 4, new RequirementSpec[5] { new RequirementSpec("DyrnwynHiltFragment", 1, 0), new RequirementSpec("DyrnwynBladeFragment", 1, 0), new RequirementSpec("DyrnwynTipFragment", 1, 0), new RequirementSpec("FlametalNew", 20, 10), new RequirementSpec("GemstoneRed", 1, 1) }), new RecipeSpec("blackforge", 4, new RequirementSpec[4] { new RequirementSpec("SwordDyrnwyn", 1, 0), new RequirementSpec("FlametalNew", 20, 20), new RequirementSpec("Eitr", 20, 0), new RequirementSpec("GemstoneRed", 1, 2) }), new LocalizedText("Dyrnwyn dual swords", "Парные мечи Дирнвин"), new LocalizedText("A pair of Dyrnwyn swords, swung like the berzerkr's axes.", "Пара мечей Дирнвин, что рубят как топоры берсерка.")); } private static DamageProfile Damage(float slash = 0f, float blunt = 0f, float pierce = 0f, float fire = 0f, float frost = 0f, float lightning = 0f, float poison = 0f, float spirit = 0f) { return new DamageProfile { Slash = slash, Blunt = blunt, Pierce = pierce, Fire = fire, Frost = frost, Lightning = lightning, Poison = poison, Spirit = spirit }; } } public static class ConfigKeys { public const string BalanceSection = "Balance"; public const string ItemsSection = "Items"; public const string DescriptionsSection = "Descriptions"; public const string ItemStatsSection = "Item stats"; public const string DamageMultiplier = "DamageMultiplier"; public const string RecipeMultiplier = "RecipeMultiplier"; public const string BlockPowerMultiplier = "BlockPowerMultiplier"; public static string Enabled(string id) { return id + ".Enabled"; } public static string Description(string id) { return id + ".Description"; } public static string CustomStats(string id) { return id + ".CustomStats"; } public static string Damage(string id, DamageType type) { return id + "." + type; } public static string DamagePerLevel(string id, DamageType type) { return id + "." + type.ToString() + "PerLevel"; } public static string BlockPower(string id) { return id + ".BlockPower"; } public static string Durability(string id) { return id + ".Durability"; } public static string DurabilityPerLevel(string id) { return id + ".DurabilityPerLevel"; } public static string Weight(string id) { return id + ".Weight"; } public static string PrimaryAttackStamina(string id) { return id + ".PrimaryAttackStamina"; } public static string SecondaryAttackStamina(string id) { return id + ".SecondaryAttackStamina"; } public static string AttackForce(string id) { return id + ".AttackForce"; } public static string BlockForce(string id) { return id + ".BlockForce"; } public static string Recipe(string id) { return id + ".Recipe"; } } public static class ConfigRanges { public const float DamageMin = 0f; public const float DamageMax = 500f; public const float BlockPowerMin = 0f; public const float BlockPowerMax = 500f; public const int DurabilityMin = 1; public const int DurabilityMax = 5000; public const int DurabilityPerLevelMin = 0; public const int DurabilityPerLevelMax = 1000; public const float WeightMin = 0f; public const float WeightMax = 100f; public const int StaminaMin = 0; public const int StaminaMax = 200; public const float ForceMin = 0f; public const float ForceMax = 500f; } public enum ConfigValueKind { Float, Int, Bool } public sealed class ConfigMigrationEntry { public string OldSection { get; } public string OldKey { get; } public string NewSection { get; } public string NewKey { get; } public ConfigValueKind Kind { get; } public ConfigMigrationEntry(string oldSection, string oldKey, string newSection, string newKey, ConfigValueKind kind) { OldSection = oldSection; OldKey = oldKey; NewSection = newSection; NewKey = newKey; Kind = kind; } } public static class ConfigMigration { public const string OldBalanceSection = "Баланс"; public const string OldItemsSection = "Предметы"; public static bool IsNeeded(string configText) { if (configText == null) { return false; } string[] array = configText.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text == "[Баланс]" || text == "[Предметы]") { return true; } } return false; } public static IReadOnlyList<ConfigMigrationEntry> PresentIn(string configText, IEnumerable<DualSwordDeclaration> declarations) { List<ConfigMigrationEntry> list = new List<ConfigMigrationEntry>(); if (string.IsNullOrEmpty(configText)) { return list; } HashSet<string> hashSet = new HashSet<string>(); string text = null; string[] array = configText.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2); continue; } int num = text2.IndexOf('='); if (num >= 0 && text != null) { string text3 = text2.Substring(0, num).Trim(); hashSet.Add(text + "\u0001" + text3); } } foreach (ConfigMigrationEntry item in Entries(declarations)) { if (hashSet.Contains(item.OldSection + "\u0001" + item.OldKey)) { list.Add(item); } } return list; } public static IReadOnlyList<ConfigMigrationEntry> Entries(IEnumerable<DualSwordDeclaration> declarations) { List<ConfigMigrationEntry> list = new List<ConfigMigrationEntry> { new ConfigMigrationEntry("Баланс", "DamageMultiplier", "Balance", "DamageMultiplier", ConfigValueKind.Float), new ConfigMigrationEntry("Баланс", "RecipeMultiplier", "Balance", "RecipeMultiplier", ConfigValueKind.Int), new ConfigMigrationEntry("Баланс", "BlockPowerMultiplier", "Balance", "BlockPowerMultiplier", ConfigValueKind.Float) }; foreach (DualSwordDeclaration declaration in declarations) { list.Add(new ConfigMigrationEntry("Предметы", declaration.Id + ".Enabled", "Items", ConfigKeys.Enabled(declaration.Id), ConfigValueKind.Bool)); } return list; } } public enum DamageType { Damage, Blunt, Slash, Pierce, Chop, Pickaxe, Fire, Frost, Lightning, Poison, Spirit, NonPlayer } public static class DamageFields { public static readonly IReadOnlyList<DamageType> All = (DamageType[])Enum.GetValues(typeof(DamageType)); public static float Get(DamageProfile profile, DamageType type) { return type switch { DamageType.Damage => profile.Damage, DamageType.Blunt => profile.Blunt, DamageType.Slash => profile.Slash, DamageType.Pierce => profile.Pierce, DamageType.Chop => profile.Chop, DamageType.Pickaxe => profile.Pickaxe, DamageType.Fire => profile.Fire, DamageType.Frost => profile.Frost, DamageType.Lightning => profile.Lightning, DamageType.Poison => profile.Poison, DamageType.Spirit => profile.Spirit, DamageType.NonPlayer => profile.NonPlayer, _ => throw new ArgumentOutOfRangeException("type", type, null), }; } public static void Set(DamageProfile profile, DamageType type, float value) { switch (type) { case DamageType.Damage: profile.Damage = value; break; case DamageType.Blunt: profile.Blunt = value; break; case DamageType.Slash: profile.Slash = value; break; case DamageType.Pierce: profile.Pierce = value; break; case DamageType.Chop: profile.Chop = value; break; case DamageType.Pickaxe: profile.Pickaxe = value; break; case DamageType.Fire: profile.Fire = value; break; case DamageType.Frost: profile.Frost = value; break; case DamageType.Lightning: profile.Lightning = value; break; case DamageType.Poison: profile.Poison = value; break; case DamageType.Spirit: profile.Spirit = value; break; case DamageType.NonPlayer: profile.NonPlayer = value; break; default: throw new ArgumentOutOfRangeException("type", type, null); } } public static IReadOnlyList<DamageType> NonZero(StatSnapshot stats) { return All.Where((DamageType type) => Get(stats.Damage, type) != 0f || Get(stats.DamagePerLevel, type) != 0f).ToList(); } } public sealed class DamageProfile { public float Damage; public float Blunt; public float Slash; public float Pierce; public float Chop; public float Pickaxe; public float Fire; public float Frost; public float Lightning; public float Poison; public float Spirit; public float NonPlayer; public DamageProfile Scale(float multiplier) { if (multiplier <= 0f) { throw new ArgumentOutOfRangeException("multiplier", multiplier, "Множитель урона должен быть больше нуля"); } return new DamageProfile { Damage = Damage * multiplier, Blunt = Blunt * multiplier, Slash = Slash * multiplier, Pierce = Pierce * multiplier, Chop = Chop * multiplier, Pickaxe = Pickaxe * multiplier, Fire = Fire * multiplier, Frost = Frost * multiplier, Lightning = Lightning * multiplier, Poison = Poison * multiplier, Spirit = Spirit * multiplier, NonPlayer = NonPlayer * multiplier }; } } public sealed class DualSwordDeclaration { public string Id { get; } public string BaseSwordPrefab { get; } public bool Optional { get; } public bool CustomRecipe { get; } public EulerOffset? MeshRotation { get; } public StatSnapshot BaseStats { get; } public StatSnapshot DualStats { get; } public RecipeSpec BaseRecipe { get; } public RecipeSpec DualRecipe { get; } public LocalizedText Name { get; } public LocalizedText Description { get; } public string TokenName => "$item_" + (Id ?? string.Empty).ToLowerInvariant(); public string TokenDescription => TokenName + "_description"; public bool IsValid => Validate().Count == 0; public DualSwordDeclaration(string id, string baseSwordPrefab, bool optional, bool customRecipe, EulerOffset? meshRotation, StatSnapshot baseStats, StatSnapshot dualStats, RecipeSpec baseRecipe, RecipeSpec dualRecipe, LocalizedText name, LocalizedText description) { Id = id; BaseSwordPrefab = baseSwordPrefab; Optional = optional; CustomRecipe = customRecipe; MeshRotation = meshRotation; BaseStats = baseStats; DualStats = dualStats; BaseRecipe = baseRecipe; DualRecipe = dualRecipe; Name = name; Description = description; } public IReadOnlyList<string> Validate() { List<string> list = new List<string>(); RequireText(list, Id, "Id предмета"); RequireText(list, BaseSwordPrefab, "BaseSwordPrefab"); if (BaseStats == null) { list.Add(Id + ": нет снимка статов базового меча"); } if (DualStats == null) { list.Add(Id + ": нет статов парного предмета"); } ValidateRecipe(list, BaseRecipe, "рецепт базового меча"); ValidateRecipe(list, DualRecipe, "рецепт парного предмета"); ValidateText(list, Name, "название"); ValidateText(list, Description, "описание"); return list; } private void ValidateRecipe(List<string> errors, RecipeSpec recipe, string what) { if (recipe == null) { errors.Add(Id + ": не задан " + what); return; } if (string.IsNullOrWhiteSpace(recipe.Station)) { errors.Add(Id + ": не задан станок (" + what + ")"); } if (recipe.MinStationLevel < 1) { errors.Add(Id + ": уровень станка должен быть не меньше 1 (" + what + ")"); } if (recipe.Requirements.Count == 0) { errors.Add(Id + ": " + what + " пуст"); return; } foreach (RequirementSpec requirement in recipe.Requirements) { if (string.IsNullOrWhiteSpace(requirement.Item)) { errors.Add(Id + ": в требовании не указан префаб (" + what + ")"); } else if (requirement.Amount < 0 || requirement.AmountPerLevel < 0) { errors.Add(Id + ": отрицательное количество " + requirement.Item + " (" + what + ")"); } else if (requirement.Amount == 0 && requirement.AmountPerLevel == 0) { errors.Add(Id + ": требование " + requirement.Item + " не стоит ничего (" + what + ")"); } } } private void ValidateText(List<string> errors, LocalizedText text, string what) { if (text == null) { errors.Add(Id + ": не задано " + what); return; } RequireText(errors, text.English, what + " на английском"); RequireText(errors, text.Russian, what + " на русском"); } private void RequireText(List<string> errors, string value, string what) { if (string.IsNullOrWhiteSpace(value)) { errors.Add(Id + ": не заполнено " + what); } } } public static class DualSwordsInfo { public const string PluginGuid = "dev.alex.dualswords"; public const string PluginName = "DualSwords"; public const string PluginVersion = "1.0.2"; } public struct EulerOffset { public float X { get; } public float Y { get; } public float Z { get; } public static EulerOffset Zero => new EulerOffset(0f, 0f, 0f); public EulerOffset(float x, float y, float z) { X = x; Y = y; Z = z; } public override string ToString() { return $"({X}, {Y}, {Z})"; } } public sealed class BalanceMultipliers { public static readonly BalanceMultipliers Neutral = new BalanceMultipliers(1f, 1, 1f); public float Damage { get; } public int Recipe { get; } public float BlockPower { get; } public BalanceMultipliers(float damage, int recipe, float blockPower) { Damage = damage; Recipe = recipe; BlockPower = blockPower; } } public sealed class ResolvedItemStats { public DamageProfile Damage { get; internal set; } public DamageProfile DamagePerLevel { get; internal set; } public float BlockPower { get; internal set; } public int MaxDurability { get; internal set; } public int DurabilityPerLevel { get; internal set; } public float Weight { get; internal set; } public int PrimaryStamina { get; internal set; } public int SecondaryStamina { get; internal set; } public int ToolTier { get; internal set; } public float? AttackForce { get; internal set; } public float? BlockForce { get; internal set; } public string Station { get; internal set; } public int MinStationLevel { get; internal set; } public IReadOnlyList<RequirementSpec> Requirements { get; internal set; } public IReadOnlyList<string> Warnings { get; internal set; } } public static class ItemStatsResolver { public static ResolvedItemStats Resolve(DualSwordDeclaration declaration, ItemStatValues values, BalanceMultipliers multipliers, Func<string, bool> isItem) { if (declaration == null) { throw new ArgumentNullException("declaration"); } if (multipliers == null) { throw new ArgumentNullException("multipliers"); } if (isItem == null) { throw new ArgumentNullException("isItem"); } List<string> warnings = new List<string>(); ResolvedItemStats resolvedItemStats = new ResolvedItemStats { ToolTier = declaration.DualStats.ToolTier, Station = declaration.DualRecipe.Station, MinStationLevel = declaration.DualRecipe.MinStationLevel, Warnings = warnings }; if (values == null || !values.CustomStats) { FromCatalogue(declaration, multipliers, resolvedItemStats, warnings); } else { FromConfig(declaration, values, isItem, resolvedItemStats, warnings); } return resolvedItemStats; } private static void FromCatalogue(DualSwordDeclaration declaration, BalanceMultipliers multipliers, ResolvedItemStats resolved, List<string> warnings) { StatSnapshot dualStats = declaration.DualStats; float num = multipliers.Damage; if (num <= 0f) { warnings.Add($"{declaration.Id}: DamageMultiplier сконфигурирован как {num}, множитель урона должен быть больше нуля — беру 1.0"); num = 1f; } float num2 = multipliers.BlockPower; if (num2 <= 0f) { warnings.Add($"{declaration.Id}: BlockPowerMultiplier сконфигурирован как {num2}, множитель силы блока должен быть больше нуля — беру 1.0"); num2 = 1f; } int num3 = multipliers.Recipe; if (num3 < 1) { warnings.Add($"{declaration.Id}: RecipeMultiplier сконфигурирован как {num3}, множитель рецепта должен быть не меньше единицы — беру 1"); num3 = 1; } resolved.Damage = dualStats.Damage.Scale(num); resolved.DamagePerLevel = dualStats.DamagePerLevel.Scale(num); resolved.BlockPower = (float)dualStats.BlockPower * num2; resolved.MaxDurability = dualStats.MaxDurability; resolved.DurabilityPerLevel = dualStats.DurabilityPerLevel; resolved.Weight = dualStats.Weight; resolved.PrimaryStamina = dualStats.PrimaryStamina; resolved.SecondaryStamina = dualStats.SecondaryStamina; resolved.AttackForce = null; resolved.BlockForce = null; resolved.Requirements = RecipeScaling.Scale(declaration.DualRecipe.Requirements, num3); } private static void FromConfig(DualSwordDeclaration declaration, ItemStatValues values, Func<string, bool> isItem, ResolvedItemStats resolved, List<string> warnings) { StatSnapshot dualStats = declaration.DualStats; resolved.Damage = new DamageProfile(); resolved.DamagePerLevel = new DamageProfile(); foreach (DamageType item in DamageFields.NonZero(dualStats)) { DamageFields.Set(resolved.Damage, item, values.Damage.TryGetValue(item, out var value) ? value : DamageFields.Get(dualStats.Damage, item)); DamageFields.Set(resolved.DamagePerLevel, item, values.DamagePerLevel.TryGetValue(item, out var value2) ? value2 : DamageFields.Get(dualStats.DamagePerLevel, item)); } resolved.BlockPower = values.BlockPower; resolved.MaxDurability = values.Durability; resolved.DurabilityPerLevel = values.DurabilityPerLevel; resolved.Weight = values.Weight; resolved.PrimaryStamina = values.PrimaryAttackStamina; resolved.SecondaryStamina = values.SecondaryAttackStamina; resolved.AttackForce = values.AttackForce; resolved.BlockForce = values.BlockForce; if (!RecipeText.TryParse(values.Recipe, out var requirements, out var error)) { warnings.Add(declaration.Id + ": рецепт из конфига не принят (" + error + ") — беру рецепт каталога"); resolved.Requirements = declaration.DualRecipe.Requirements; return; } List<string> list = (from requirement in requirements select requirement.Item into item where !isItem(item) select item).ToList(); if (list.Count > 0) { warnings.Add(declaration.Id + ": в рецепте из конфига неизвестные префабы или не предметы: " + string.Join(", ", list) + " — беру рецепт каталога"); resolved.Requirements = declaration.DualRecipe.Requirements; } else { resolved.Requirements = requirements; } } } public sealed class ItemStatValues { public bool CustomStats { get; set; } public Dictionary<DamageType, float> Damage { get; } = new Dictionary<DamageType, float>(); public Dictionary<DamageType, float> DamagePerLevel { get; } = new Dictionary<DamageType, float>(); public float BlockPower { get; set; } public int Durability { get; set; } public int DurabilityPerLevel { get; set; } public float Weight { get; set; } public int PrimaryAttackStamina { get; set; } public int SecondaryAttackStamina { get; set; } public float AttackForce { get; set; } public float BlockForce { get; set; } public string Recipe { get; set; } public static ItemStatValues DefaultsFor(DualSwordDeclaration declaration) { StatSnapshot dualStats = declaration.DualStats; ItemStatValues itemStatValues = new ItemStatValues { CustomStats = false, BlockPower = dualStats.BlockPower, Durability = dualStats.MaxDurability, DurabilityPerLevel = dualStats.DurabilityPerLevel, Weight = dualStats.Weight, PrimaryAttackStamina = dualStats.PrimaryStamina, SecondaryAttackStamina = dualStats.SecondaryStamina, AttackForce = 20f, BlockForce = 20f, Recipe = RecipeText.Format(declaration.DualRecipe.Requirements) }; foreach (DamageType item in DamageFields.NonZero(dualStats)) { itemStatValues.Damage[item] = DamageFields.Get(dualStats.Damage, item); itemStatValues.DamagePerLevel[item] = DamageFields.Get(dualStats.DamagePerLevel, item); } return itemStatValues; } } public sealed class LocalizedText { public string English { get; } public string Russian { get; } public LocalizedText(string english, string russian) { English = english; Russian = russian; } } public static class RecipeScaling { public static IReadOnlyList<RequirementSpec> Scale(IReadOnlyList<RequirementSpec> source, int multiplier) { if (source == null) { throw new ArgumentNullException("source"); } if (multiplier < 1) { throw new ArgumentOutOfRangeException("multiplier", multiplier, "Множитель рецепта должен быть не меньше единицы"); } List<RequirementSpec> list = new List<RequirementSpec>(source.Count); foreach (RequirementSpec item in source) { if (item != null) { if (!BalanceRules.IsScalable(item.Item)) { list.Add(item); } else { list.Add(new RequirementSpec(item.Item, item.Amount * multiplier, item.AmountPerLevel * multiplier)); } } } return list; } } public sealed class RecipeSpec { public string Station { get; } public int MinStationLevel { get; } public IReadOnlyList<RequirementSpec> Requirements { get; } public RecipeSpec(string station, int minStationLevel, IReadOnlyList<RequirementSpec> requirements) { Station = station; MinStationLevel = minStationLevel; Requirements = requirements ?? new RequirementSpec[0]; } } public static class RecipeText { public static string Format(IReadOnlyList<RequirementSpec> requirements) { if (requirements == null) { throw new ArgumentNullException("requirements"); } return string.Join("|", requirements.Select((RequirementSpec requirement) => string.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", requirement.Item, requirement.Amount, requirement.AmountPerLevel))); } public static bool TryParse(string text, out IReadOnlyList<RequirementSpec> requirements, out string error) { requirements = null; if (string.IsNullOrWhiteSpace(text)) { error = "строка рецепта пустая"; return false; } List<RequirementSpec> list = new List<RequirementSpec>(); string[] array = text.Split(new char[1] { '|' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0) { error = "пустой ингредиент в «" + text + "»"; return false; } string[] array2 = text2.Split(new char[1] { ',' }); if (array2.Length != 3) { error = "«" + text2 + "»: нужно ровно три поля Prefab,Amount,AmountPerLevel"; return false; } string text3 = array2[0].Trim(); if (text3.Length == 0) { error = "«" + text2 + "»: не указан префаб"; return false; } if (!TryParseAmount(array2[1], out var value) || !TryParseAmount(array2[2], out var value2)) { error = "«" + text2 + "»: количество должно быть целым числом"; return false; } if (value < 0 || value2 < 0) { error = "«" + text2 + "»: отрицательное количество"; return false; } if (value == 0 && value2 == 0) { error = "«" + text2 + "»: ингредиент не стоит ничего"; return false; } list.Add(new RequirementSpec(text3, value, value2)); } requirements = list; error = null; return true; } private static bool TryParseAmount(string text, out int value) { return int.TryParse(text.Trim(), NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out value); } } public sealed class RequirementSpec { public string Item { get; } public int Amount { get; } public int AmountPerLevel { get; } public RequirementSpec(string item, int amount, int amountPerLevel) { Item = item; Amount = amount; AmountPerLevel = amountPerLevel; } } public sealed class StatSnapshot { public DamageProfile Damage { get; } public DamageProfile DamagePerLevel { get; } public int MaxDurability { get; } public int DurabilityPerLevel { get; } public int BlockPower { get; } public float Weight { get; } public int PrimaryStamina { get; } public int SecondaryStamina { get; } public int ToolTier { get; } public StatSnapshot(DamageProfile damage, DamageProfile damagePerLevel, int maxDurability, int durabilityPerLevel, int blockPower, float weight, int primaryStamina, int secondaryStamina, int toolTier) { Damage = damage; DamagePerLevel = damagePerLevel; MaxDurability = maxDurability; DurabilityPerLevel = durabilityPerLevel; BlockPower = blockPower; Weight = weight; PrimaryStamina = primaryStamina; SecondaryStamina = secondaryStamina; ToolTier = toolTier; } } }
plugins/DualSwords/DualSwords.dll
Decompiled 7 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using DualSwords.Core; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("DualSwords")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+7b567f59d6531825de4c7a0c772c630fbd264809")] [assembly: AssemblyProduct("DualSwords")] [assembly: AssemblyTitle("DualSwords")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DualSwords { internal static class AttachMeshCollector { public static bool TryCollect(Transform attachRoot, Matrix4x4 extraTransform, string meshName, out Mesh mesh, out Material[] materials, out string error) { //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) mesh = null; materials = null; error = null; if ((Object)(object)attachRoot == (Object)null) { error = "корень attach не задан"; return false; } try { List<CombineInstance> list = new List<CombineInstance>(); List<Material> list2 = new List<Material>(); MeshFilter[] componentsInChildren = ((Component)attachRoot).GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { Mesh sharedMesh = val.sharedMesh; if ((Object)(object)sharedMesh == (Object)null) { continue; } MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>(); if (!((Object)(object)component == (Object)null) && ((Renderer)component).sharedMaterials.Length != 0) { Matrix4x4 val2 = attachRoot.worldToLocalMatrix * ((Component)val).transform.localToWorldMatrix; Matrix4x4 transform = extraTransform * val2; for (int j = 0; j < sharedMesh.subMeshCount; j++) { CombineInstance item = default(CombineInstance); ((CombineInstance)(ref item)).mesh = sharedMesh; ((CombineInstance)(ref item)).subMeshIndex = j; ((CombineInstance)(ref item)).transform = transform; list.Add(item); int num = Mathf.Min(j, ((Renderer)component).sharedMaterials.Length - 1); list2.Add(((Renderer)component).sharedMaterials[num]); } } } if (list.Count == 0) { error = "под " + ((Object)attachRoot).name + " не нашлось ни одного меша с материалом"; return false; } Mesh val3 = new Mesh { name = meshName }; val3.indexFormat = (IndexFormat)1; val3.CombineMeshes(list.ToArray(), false, true); val3.RecalculateBounds(); mesh = val3; materials = list2.ToArray(); return true; } catch (Exception ex) { error = "меш " + meshName + ": " + ex.Message; return false; } } } internal static class DualSwordFactory { private const string BaseDualPrefab = "AxeBerzerkr"; private const string GroundModelName = "ground_model"; public static bool TryCreate(DualSwordDeclaration declaration, PlayerRigInfo rig, ResolvedItemStats stats, out GameObject prefab, out string error) { //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Expected O, but got Unknown //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_0330: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_0390: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_03fa: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0542: Unknown result type (might be due to invalid IL or missing references) //IL_05e1: Unknown result type (might be due to invalid IL or missing references) //IL_05e6: Unknown result type (might be due to invalid IL or missing references) //IL_064f: Unknown result type (might be due to invalid IL or missing references) //IL_0654: Unknown result type (might be due to invalid IL or missing references) //IL_0661: Unknown result type (might be due to invalid IL or missing references) //IL_0666: Unknown result type (might be due to invalid IL or missing references) prefab = null; error = null; GameObject val = null; try { IReadOnlyList<string> readOnlyList = declaration.Validate(); if (readOnlyList.Count > 0) { error = "декларация не прошла проверку: " + string.Join("; ", readOnlyList); return false; } GameObject prefab2 = PrefabManager.Instance.GetPrefab(declaration.BaseSwordPrefab); if ((Object)(object)prefab2 == (Object)null) { error = "ванильный префаб " + declaration.BaseSwordPrefab + " не найден"; return false; } Transform val2 = prefab2.transform.Find("attach"); if ((Object)(object)val2 == (Object)null) { error = "у " + declaration.BaseSwordPrefab + " нет узла attach"; return false; } List<MeshFilter> list = FindMeshes(val2); if (list.Count == 0) { error = "у " + declaration.BaseSwordPrefab + " в attach нет меша"; return false; } Bounds swordBounds = BoundsInAttachSpace(val2, list); ItemDrop component = prefab2.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { error = "у " + declaration.BaseSwordPrefab + " нет компонента ItemDrop"; return false; } SharedData shared = component.m_itemData.m_shared; StatDriftGuard.Report(declaration.BaseSwordPrefab, declaration.BaseStats, shared); val = PrefabManager.Instance.CreateClonedPrefab(declaration.Id, "AxeBerzerkr"); if ((Object)(object)val == (Object)null) { error = "не удалось клонировать AxeBerzerkr"; return false; } Transform val3 = val.transform.Find("attach"); if ((Object)(object)val3 == (Object)null) { error = declaration.Id + ": у AxeBerzerkr нет узла attach — скрещённую пару топоров собрать не из чего"; PrefabManager.Instance.DestroyPrefab(declaration.Id); return false; } List<MeshFilter> list2 = FindMeshes(val3); if (list2.Count < 2) { error = string.Format("{0}: у {1} в attach {2} меш(ей) топоров, нужно минимум два — скрещённую пару собрать не из чего", declaration.Id, "AxeBerzerkr", list2.Count); PrefabManager.Instance.DestroyPrefab(declaration.Id); return false; } Vector3[] array = (Vector3[])(object)new Vector3[2] { ((Component)list2[0]).transform.localPosition, ((Component)list2[1]).transform.localPosition }; Quaternion[] array2 = (Quaternion[])(object)new Quaternion[2] { ((Component)list2[0]).transform.localRotation, ((Component)list2[1]).transform.localRotation }; Bounds bounds = list2[0].sharedMesh.bounds; Quaternion val4; if (declaration.MeshRotation.HasValue) { EulerOffset value = declaration.MeshRotation.Value; val4 = Quaternion.Euler(((EulerOffset)(ref value)).X, ((EulerOffset)(ref value)).Y, ((EulerOffset)(ref value)).Z); DualSwordsPlugin.Log.LogInfo((object)($"{declaration.Id}: поворот меша взят из декларации (MeshRotation={value}) — " + "вычисление по геометрии пропущено")); } else { val4 = MeshOrientation.For(swordBounds, bounds); } GameObject val5 = new GameObject("attach_back"); val5.transform.SetParent(val.transform, false); CloneSwordInto(val5.transform, val2, array[0], array2[0] * val4); CloneSwordInto(val5.transform, val2, array[1], array2[1] * val4); val5.SetActive(false); GameObject obj = Object.Instantiate<GameObject>(((Component)val2).gameObject, val.transform, false); ((Object)obj).name = "attach"; obj.transform.localPosition = Vector3.zero; obj.transform.localRotation = Quaternion.identity; obj.SetActive(false); GameObject val6 = new GameObject("ground_model"); val6.transform.SetParent(val.transform, false); CloneSwordInto(val6.transform, val2, array[0], array2[0] * val4); CloneSwordInto(val6.transform, val2, array[1], array2[1] * val4); val6.SetActive(true); Transform val7 = val.transform.Find("attach_skin"); if ((Object)(object)val7 != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)val7).gameObject); } Object.DestroyImmediate((Object)(object)((Component)val3).gameObject); val5.transform.SetSiblingIndex(0); obj.transform.SetSiblingIndex(1); if (((Object)val.transform.GetChild(0)).name != "attach_back" || ((Object)val.transform.GetChild(1)).name != "attach") { error = declaration.Id + ": порядок узлов визуала нарушен (0=" + ((Object)val.transform.GetChild(0)).name + ", 1=" + ((Object)val.transform.GetChild(1)).name + "), ожидались attach_back и attach — AttachItem подберёт не тот узел"; PrefabManager.Instance.DestroyPrefab(declaration.Id); return false; } DualSwordsPlugin.Log.LogInfo((object)($"{declaration.Id}: визуал — меч bounds.extents={((Bounds)(ref swordBounds)).extents}, топор bounds.extents={((Bounds)(ref bounds)).extents}, " + string.Format("поправка ориентации euler={0}, узлы: attach_back, attach, {1}", ((Quaternion)(ref val4)).eulerAngles, "ground_model"))); ItemDrop component2 = val.GetComponent<ItemDrop>(); if ((Object)(object)component2 == (Object)null) { error = "у клона " + declaration.Id + " нет компонента ItemDrop"; PrefabManager.Instance.DestroyPrefab(declaration.Id); return false; } SharedData shared2 = component2.m_itemData.m_shared; shared2.m_name = declaration.TokenName; shared2.m_description = declaration.TokenDescription; shared2.m_icons = shared.m_icons.ToArray(); shared2.m_skillType = shared.m_skillType; if (IconRenderer.TryMirror((shared.m_icons.Length != 0) ? shared.m_icons[0] : null, declaration.Id, out var icon, out var error2)) { shared2.m_icons = (Sprite[])(object)new Sprite[1] { icon }; } else { DualSwordsPlugin.Log.LogWarning((object)(declaration.Id + ": не удалось получить иконку скрещённых мечей режимом Mirror — " + error2 + ", оставляю иконку базового меча")); } shared2.m_damages = VanillaBridge.WriteDamage(stats.Damage); shared2.m_damagesPerLevel = VanillaBridge.WriteDamage(stats.DamagePerLevel); shared2.m_maxDurability = stats.MaxDurability; shared2.m_durabilityPerLevel = stats.DurabilityPerLevel; shared2.m_blockPower = stats.BlockPower; shared2.m_weight = stats.Weight; shared2.m_toolTier = stats.ToolTier; shared2.m_attack.m_attackStamina = stats.PrimaryStamina; shared2.m_secondaryAttack.m_attackStamina = stats.SecondaryStamina; if (stats.AttackForce.HasValue) { shared2.m_attackForce = stats.AttackForce.Value; } if (stats.BlockForce.HasValue) { shared2.m_deflectionForce = stats.BlockForce.Value; } prefab = val; return true; } catch (Exception ex) { error = declaration.Id + ": непредвиденное исключение при сборке — " + ex.Message; if ((Object)(object)val != (Object)null) { PrefabManager.Instance.DestroyPrefab(declaration.Id); } prefab = null; return false; } } public static ItemConfig BuildItemConfig(ResolvedItemStats stats) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown ItemConfig val = new ItemConfig { Amount = 1, CraftingStation = stats.Station, MinStationLevel = stats.MinStationLevel, Enabled = true }; foreach (RequirementSpec requirement in stats.Requirements) { val.AddRequirement(new RequirementConfig { Item = requirement.Item, Amount = requirement.Amount, AmountPerLevel = requirement.AmountPerLevel }); } return val; } private static void CloneSwordInto(Transform parent, Transform swordAttach, Vector3 localPosition, Quaternion localRotation) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) GameObject obj = Object.Instantiate<GameObject>(((Component)swordAttach).gameObject, parent, false); obj.transform.localPosition = localPosition; obj.transform.localRotation = localRotation; } private static Bounds BoundsInAttachSpace(Transform root, IEnumerable<MeshFilter> filters) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) Bounds? val = null; Vector3 val3 = default(Vector3); foreach (MeshFilter filter in filters) { Bounds bounds = filter.sharedMesh.bounds; Vector3 min = ((Bounds)(ref bounds)).min; Vector3 max = ((Bounds)(ref bounds)).max; Matrix4x4 val2 = root.worldToLocalMatrix * ((Component)filter).transform.localToWorldMatrix; for (int i = 0; i < 8; i++) { ((Vector3)(ref val3))..ctor(((i & 1) == 0) ? min.x : max.x, ((i & 2) == 0) ? min.y : max.y, ((i & 4) == 0) ? min.z : max.z); Vector3 val4 = ((Matrix4x4)(ref val2)).MultiplyPoint3x4(val3); if (!val.HasValue) { val = new Bounds(val4, Vector3.zero); continue; } Bounds value = val.Value; ((Bounds)(ref value)).Encapsulate(val4); val = value; } } return val.GetValueOrDefault(); } private static List<MeshFilter> FindMeshes(Transform root) { List<MeshFilter> list = new List<MeshFilter>(); MeshFilter[] componentsInChildren = ((Component)root).GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { if ((Object)(object)((Component)val).GetComponent<MeshRenderer>() != (Object)null && (Object)(object)val.sharedMesh != (Object)null) { list.Add(val); } } return list; } } internal sealed class DualSwordsConfig { private const string BakedWarning = " Read once at game start and baked into the items: changing it on a server does not change items already built, neither for a client that has just connected nor for the others."; private readonly ConfigEntry<float> _damageMultiplier; private readonly ConfigEntry<int> _recipeMultiplier; private readonly ConfigEntry<float> _blockPowerMultiplier; private readonly Dictionary<string, ConfigEntry<bool>> _itemEnabled = new Dictionary<string, ConfigEntry<bool>>(); private readonly Dictionary<string, ConfigEntry<string>> _descriptions = new Dictionary<string, ConfigEntry<string>>(); private readonly Dictionary<string, ItemStatEntries> _itemStats = new Dictionary<string, ItemStatEntries>(); public BalanceMultipliers Multipliers => new BalanceMultipliers(_damageMultiplier.Value, _recipeMultiplier.Value, _blockPowerMultiplier.Value); private DualSwordsConfig(ConfigFile file) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected O, but got Unknown bool saveOnConfigSet = file.SaveOnConfigSet; file.SaveOnConfigSet = false; try { _damageMultiplier = file.Bind<float>("Balance", "DamageMultiplier", 1f, new ConfigDescription("Damage multiplier on top of the mod's own, already balanced numbers (not vanilla ones). 1.0 means \"as designed\", not \"off\". Does not affect items with CustomStats = true. Read once at game start and baked into the items: changing it on a server does not change items already built, neither for a client that has just connected nor for the others.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); _recipeMultiplier = file.Bind<int>("Balance", "RecipeMultiplier", 1, new ConfigDescription("Recipe cost multiplier on top of the mod's own, already balanced recipes (not vanilla ones). 1 means \"as designed\", not \"off\". Unique ingredients (trophies, fragments, keys) are never multiplied. Does not affect items with CustomStats = true. Read once at game start and baked into the items: changing it on a server does not change items already built, neither for a client that has just connected nor for the others.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); _blockPowerMultiplier = file.Bind<float>("Balance", "BlockPowerMultiplier", 1f, new ConfigDescription("Block power multiplier on top of the mod's own, already balanced numbers (not vanilla ones). 1.0 means \"as designed\", not \"off\". Does not affect items with CustomStats = true. Read once at game start and baked into the items: changing it on a server does not change items already built, neither for a client that has just connected nor for the others.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); IReadOnlyList<DualSwordDeclaration> readOnlyList = Catalogue.All(); foreach (DualSwordDeclaration item in readOnlyList) { string id = item.Id; string english = item.Name.English; _itemEnabled[id] = file.Bind<bool>("Items", ConfigKeys.Enabled(id), true, new ConfigDescription("Build " + english + " (" + id + "). false = the item is not added to the game", (AcceptableValueBase)null, Array.Empty<object>())); _descriptions[id] = file.Bind<string>("Descriptions", ConfigKeys.Description(id), string.Empty, new ConfigDescription("Description of " + english + " (" + id + "). Empty = the built-in description in the player's language; any text replaces it in every language", (AcceptableValueBase)null, Array.Empty<object>())); _itemStats[id] = ItemStatEntries.Bind(file, item); } try { MigrateFromRussianSections(file, readOnlyList); } catch (Exception ex) { DualSwordsPlugin.Log.LogError((object)("конфиг: перенос секций 1.0.x не выполнен — " + ex.Message)); } } finally { try { file.Save(); } catch (Exception ex2) { DualSwordsPlugin.Log.LogError((object)("конфиг: файл не сохранён — " + ex2.Message)); } file.SaveOnConfigSet = saveOnConfigSet; } } public bool IsEnabled(string declarationId) { if (_itemEnabled.TryGetValue(declarationId, out var value)) { return value.Value; } return true; } public string DescriptionFor(string declarationId) { if (!_descriptions.TryGetValue(declarationId, out var value) || string.IsNullOrWhiteSpace(value.Value)) { return null; } return value.Value.Trim(); } public ItemStatValues StatValuesFor(string declarationId) { if (!_itemStats.TryGetValue(declarationId, out var value)) { return null; } return value.ToValues(); } public static DualSwordsConfig Bind(ConfigFile file) { return new DualSwordsConfig(file); } private static void MigrateFromRussianSections(ConfigFile file, IReadOnlyList<DualSwordDeclaration> declarations) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected I4, but got Unknown string configFilePath = file.ConfigFilePath; if (!File.Exists(configFilePath)) { return; } string text = File.ReadAllText(configFilePath); if (!ConfigMigration.IsNeeded(text)) { return; } IReadOnlyList<ConfigMigrationEntry> readOnlyList = ConfigMigration.PresentIn(text, (IEnumerable<DualSwordDeclaration>)declarations); foreach (ConfigMigrationEntry item in readOnlyList) { ConfigDefinition oldDefinition = new ConfigDefinition(item.OldSection, item.OldKey); ConfigDefinition newDefinition = new ConfigDefinition(item.NewSection, item.NewKey); ConfigValueKind kind = item.Kind; switch ((int)kind) { case 0: Move(file, oldDefinition, newDefinition, 1f); break; case 1: Move(file, oldDefinition, newDefinition, 1); break; case 2: Move(file, oldDefinition, newDefinition, oldDefault: true); break; } } DualSwordsPlugin.Log.LogInfo((object)$"конфиг: значения секций [Баланс] и [Предметы] версии 1.0.x перенесены в английские секции ({readOnlyList.Count} записей)"); } private static void Move<T>(ConfigFile file, ConfigDefinition oldDefinition, ConfigDefinition newDefinition, T oldDefault) { ConfigEntry<T> val = file.Bind<T>(oldDefinition, oldDefault, (ConfigDescription)null); ConfigEntry<T> val2 = default(ConfigEntry<T>); if (file.TryGetEntry<T>(newDefinition, ref val2)) { val2.Value = val.Value; } file.Remove(oldDefinition); } } [BepInPlugin("dev.alex.dualswords", "DualSwords", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class DualSwordsPlugin : BaseUnityPlugin { internal static ManualLogSource Log; internal static DualSwordsConfig Settings; private void Awake() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Settings = DualSwordsConfig.Bind(((BaseUnityPlugin)this).Config); Harmony val = new Harmony("dev.alex.dualswords"); val.PatchAll(typeof(LeftHandPatch)); val.PatchAll(typeof(ItemStandPatch)); CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); Dictionary<string, string> dictionary = new Dictionary<string, string>(); Dictionary<string, string> dictionary2 = new Dictionary<string, string>(); foreach (DualSwordDeclaration item in Catalogue.All()) { string key = item.TokenName.TrimStart(new char[1] { '$' }); string key2 = item.TokenDescription.TrimStart(new char[1] { '$' }); dictionary[key] = item.Name.English; dictionary2[key] = item.Name.Russian; string text = Settings.DescriptionFor(item.Id); dictionary[key2] = text ?? item.Description.English; dictionary2[key2] = text ?? item.Description.Russian; } string text2 = "English"; localization.AddTranslation(ref text2, dictionary); text2 = "Russian"; localization.AddTranslation(ref text2, dictionary2); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DumpCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new StatsCommand()); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ProbeCommand()); PrefabManager.OnVanillaPrefabsAvailable += CreateItems; Log.LogInfo((object)"DualSwords 1.0.2 загружен"); } private static void CreateItems() { //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Expected O, but got Unknown PrefabManager.OnVanillaPrefabsAvailable -= CreateItems; try { string error; PlayerRigInfo playerRigInfo = PlayerRigInfo.TryBuild(PrefabManager.Instance.GetPrefab("Player"), out error); if (playerRigInfo == null) { Log.LogError((object)("скелет игрока не прочитан, предметы не созданы: " + error)); return; } IReadOnlyList<DualSwordDeclaration> readOnlyList = default(IReadOnlyList<DualSwordDeclaration>); IReadOnlyList<string> readOnlyList2 = default(IReadOnlyList<string>); if (!BuildOrder.Resolve((IReadOnlyList<DualSwordDeclaration>)(from declaration in Catalogue.All() where Settings.IsEnabled(declaration.Id) select declaration).ToList(), ref readOnlyList, ref readOnlyList2)) { foreach (string item in readOnlyList2) { Log.LogError((object)("каталог не собран: " + item)); } return; } Stopwatch stopwatch = Stopwatch.StartNew(); int num = 0; int num2 = 0; int num3 = 0; Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (DualSwordDeclaration item2 in readOnlyList) { GameObject prefab = null; try { if (dictionary.TryGetValue(item2.Id, out var value)) { Log.LogWarning((object)(item2.Id + " пропущен: не собран " + value)); num3++; continue; } if ((Object)(object)PrefabManager.Instance.GetPrefab(item2.BaseSwordPrefab) == (Object)null) { if (item2.Optional) { Log.LogInfo((object)(item2.Id + " пропущен: базовый меч " + item2.BaseSwordPrefab + " не найден, мод Valheim Armory не установлен")); num2++; } else { Log.LogError((object)(item2.Id + " не собран: базовый меч " + item2.BaseSwordPrefab + " не найден")); num3++; } MarkDependents(item2.Id, readOnlyList, dictionary); continue; } ResolvedItemStats val = ItemStatsResolver.Resolve(item2, Settings.StatValuesFor(item2.Id), Settings.Multipliers, (Func<string, bool>)delegate(string name) { GameObject prefab2 = PrefabManager.Instance.GetPrefab(name); return (Object)(object)prefab2 != (Object)null && (Object)(object)prefab2.GetComponent<ItemDrop>() != (Object)null; }); foreach (string warning in val.Warnings) { Log.LogWarning((object)warning); } if (!DualSwordFactory.TryCreate(item2, playerRigInfo, val, out prefab, out var error2)) { Log.LogError((object)(item2.Id + " не собран: " + error2)); num3++; MarkDependents(item2.Id, readOnlyList, dictionary); continue; } ItemConfig val2 = DualSwordFactory.BuildItemConfig(val); if (!ItemManager.Instance.AddItem(new CustomItem(prefab, true, val2))) { Log.LogError((object)(item2.Id + " не собран: ItemManager отказался зарегистрировать предмет")); PrefabManager.Instance.DestroyPrefab(item2.Id); num3++; MarkDependents(item2.Id, readOnlyList, dictionary); } else { Log.LogInfo((object)(item2.Id + " собран")); num++; } } catch (Exception ex) { Log.LogError((object)(item2.Id + " не собран: непредвиденное исключение — " + ex.Message)); if ((Object)(object)prefab != (Object)null) { PrefabManager.Instance.DestroyPrefab(item2.Id); } num3++; MarkDependents(item2.Id, readOnlyList, dictionary); } } stopwatch.Stop(); Log.LogInfo((object)$"итог сборки: собрано {num}, пропущено штатно {num2}, пропущено с ошибкой {num3}"); Log.LogInfo((object)$"каталог собран за {stopwatch.ElapsedMilliseconds} мс"); } catch (Exception ex2) { Log.LogError((object)("сборка предметов прервана непредвиденным исключением: " + ex2.Message)); } } private static void MarkDependents(string failedId, IReadOnlyList<DualSwordDeclaration> declarations, IDictionary<string, string> cascadeReason) { foreach (string item in BuildOrder.Dependents(failedId, declarations)) { if (!cascadeReason.ContainsKey(item)) { cascadeReason[item] = failedId; } } } } internal sealed class DumpCommand : ConsoleCommand { public override string Name => "dualswords.dump"; public override string Help => "Вывести в лог скелет игрока и структуру базовых префабов"; public override void Run(string[] args) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = DualSwordsPlugin.Log; if (args.Length == 0) { string error; PlayerRigInfo playerRigInfo = PlayerRigInfo.TryBuild(PrefabManager.Instance.GetPrefab("Player"), out error); if (playerRigInfo == null) { log.LogError((object)("скелет не прочитан: " + error)); } else { log.LogInfo((object)$"костей в теле: {playerRigInfo.BoneCount}"); log.LogInfo((object)$"правая кисть: индекс {playerRigInfo.RightHandIndex}, кость {((Object)playerRigInfo.Bones[playerRigInfo.RightHandIndex]).name}"); log.LogInfo((object)$"левая кисть: индекс {playerRigInfo.LeftHandIndex}, кость {((Object)playerRigInfo.Bones[playerRigInfo.LeftHandIndex]).name}"); log.LogInfo((object)$"смещение правого сустава: {playerRigInfo.RightJointToBone}"); log.LogInfo((object)$"смещение левого сустава: {playerRigInfo.LeftJointToBone}"); } DumpPrefab("AxeBerzerkr"); DumpPrefab("SwordBronze"); GameObject prefab = PrefabManager.Instance.GetPrefab("SwordBronze"); if (AttachMeshCollector.TryCollect(((Object)(object)prefab == (Object)null) ? null : prefab.transform.Find("attach"), Matrix4x4.identity, "dump_probe", out var mesh, out var materials, out var error2)) { log.LogInfo((object)$"меч собран: вершин {mesh.vertexCount}, сабмешей {mesh.subMeshCount}, материалов {materials.Length}, границы {mesh.bounds}"); } else { log.LogError((object)("меч не собран: " + error2)); } DumpAttachChildTransforms("AxeBerzerkr", onlyWithMesh: false); DumpAttachChildTransforms("SwordBronze", onlyWithMesh: true); return; } foreach (string text in args) { try { DumpPrefabWithStats(text); } catch (Exception arg) { log.LogError((object)$"ошибка при обработке префаба {text}: {arg}"); } } } private static void DumpPrefabWithStats(string prefabName) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = DualSwordsPlugin.Log; GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { log.LogWarning((object)("префаб " + prefabName + " не найден в игре")); return; } ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { SharedData shared = component.m_itemData.m_shared; log.LogInfo((object)$"{prefabName}: itemType={shared.m_itemType}, animationState={shared.m_animationState}, skillType={shared.m_skillType}"); log.LogInfo((object)(prefabName + ": attack=" + shared.m_attack.m_attackAnimation + ", secondary=" + shared.m_secondaryAttack.m_attackAnimation)); } StringBuilder stringBuilder = new StringBuilder(); AppendHierarchyWithStats(prefab.transform, prefabName, string.Empty, 0, stringBuilder); log.LogInfo((object)$"иерархия {prefabName}:\n{stringBuilder}"); } private static void DumpAttachChildTransforms(string prefabName, bool onlyWithMesh) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = DualSwordsPlugin.Log; GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); Transform val = (((Object)(object)prefab == (Object)null) ? null : prefab.transform.Find("attach")); if ((Object)(object)val == (Object)null) { log.LogError((object)(prefabName + ": узел attach не найден, диагностика трансформов пропущена")); return; } for (int i = 0; i < val.childCount; i++) { Transform child = val.GetChild(i); MeshFilter component = ((Component)child).GetComponent<MeshFilter>(); bool flag = (Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null; if (!onlyWithMesh || flag) { object[] obj = new object[5] { prefabName, ((Object)child).name, child.localPosition, null, null }; Quaternion localRotation = child.localRotation; obj[3] = ((Quaternion)(ref localRotation)).eulerAngles; obj[4] = child.localScale; log.LogInfo((object)string.Format("{0}/attach/{1}: localPosition={2}, localRotation.eulerAngles={3}, localScale={4}", obj)); if (flag) { Bounds bounds = component.sharedMesh.bounds; log.LogInfo((object)$"{prefabName}/attach/{((Object)child).name}: bounds.center={((Bounds)(ref bounds)).center}, bounds.extents={((Bounds)(ref bounds)).extents}"); } else { log.LogInfo((object)(prefabName + "/attach/" + ((Object)child).name + ": MeshFilter отсутствует, bounds недоступны")); } } } } private static void DumpPrefab(string prefabName) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = DualSwordsPlugin.Log; GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { log.LogError((object)("префаб " + prefabName + " не найден")); return; } ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { SharedData shared = component.m_itemData.m_shared; log.LogInfo((object)$"{prefabName}: itemType={shared.m_itemType}, animationState={shared.m_animationState}, skillType={shared.m_skillType}"); log.LogInfo((object)(prefabName + ": attack=" + shared.m_attack.m_attackAnimation + ", secondary=" + shared.m_secondaryAttack.m_attackAnimation)); } StringBuilder stringBuilder = new StringBuilder(); AppendHierarchy(prefab.transform, 0, stringBuilder); log.LogInfo((object)$"иерархия {prefabName}:\n{stringBuilder}"); } private static void AppendHierarchy(Transform node, int depth, StringBuilder text) { text.Append(' ', depth * 2).Append(((Object)node).name); if ((Object)(object)((Component)node).GetComponent<MeshFilter>() != (Object)null) { text.Append(" [MeshFilter]"); } if ((Object)(object)((Component)node).GetComponent<SkinnedMeshRenderer>() != (Object)null) { text.Append(" [SkinnedMeshRenderer]"); } text.AppendLine(); for (int i = 0; i < node.childCount; i++) { AppendHierarchy(node.GetChild(i), depth + 1, text); } } private static void AppendHierarchyWithStats(Transform node, string prefabName, string currentPath, int depth, StringBuilder text) { string text2 = (string.IsNullOrEmpty(currentPath) ? ((Object)node).name : (currentPath + "/" + ((Object)node).name)); text.Append(' ', depth * 2).Append(prefabName).Append('/') .Append(text2); MeshFilter component = ((Component)node).GetComponent<MeshFilter>(); SkinnedMeshRenderer component2 = ((Component)node).GetComponent<SkinnedMeshRenderer>(); MeshRenderer component3 = ((Component)node).GetComponent<MeshRenderer>(); if ((Object)(object)component != (Object)null) { text.Append(" [MeshFilter]"); if ((Object)(object)component.sharedMesh != (Object)null) { text.Append($" submeshes={component.sharedMesh.subMeshCount}"); text.Append($" vertices={component.sharedMesh.vertexCount}"); if ((Object)(object)component3 != (Object)null) { text.Append($" materials={((Renderer)component3).sharedMaterials.Length}"); } } } if ((Object)(object)component2 != (Object)null) { text.Append(" [SkinnedMeshRenderer]"); if ((Object)(object)component2.sharedMesh != (Object)null) { text.Append($" submeshes={component2.sharedMesh.subMeshCount}"); text.Append($" vertices={component2.sharedMesh.vertexCount}"); text.Append($" materials={((Renderer)component2).sharedMaterials.Length}"); } } if ((Object)(object)component3 != (Object)null && (Object)(object)component == (Object)null && (Object)(object)component2 == (Object)null) { text.Append(" [MeshRenderer]"); text.Append($" materials={((Renderer)component3).sharedMaterials.Length}"); } text.AppendLine(); for (int i = 0; i < node.childCount; i++) { AppendHierarchyWithStats(node.GetChild(i), prefabName, text2, depth + 1, text); } } } internal static class IconRenderer { public static bool TryMirror(Sprite baseIcon, string name, out Sprite icon, out string error) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) icon = null; error = null; RenderTexture val = null; RenderTexture active = RenderTexture.active; try { if ((Object)(object)baseIcon == (Object)null || (Object)(object)baseIcon.texture == (Object)null) { error = name + ": иконка базового меча не передана (null) — зеркальному режиму нечего отражать"; return false; } Texture2D texture = baseIcon.texture; Rect textureRect = baseIcon.textureRect; int num = Mathf.RoundToInt(((Rect)(ref textureRect)).width); int num2 = Mathf.RoundToInt(((Rect)(ref textureRect)).height); if (num <= 0 || num2 <= 0) { error = $"{name}: у иконки базового меча нулевая область в атласе ({num}x{num2})"; return false; } Rect normalizedSource = default(Rect); ((Rect)(ref normalizedSource))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height); val = (RenderTexture.active = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0)); GL.Clear(true, true, Color.clear); GL.PushMatrix(); try { GL.LoadPixelMatrix(0f, (float)num, (float)num2, 0f); Vector3 pivot = default(Vector3); ((Vector3)(ref pivot))..ctor((float)num / 2f, (float)num2 / 2f, 0f); Rect destRect = default(Rect); ((Rect)(ref destRect))..ctor((float)(-num) / 2f, (float)(-num2) / 2f, (float)num, (float)num2); DrawMirrored((Texture)(object)texture, normalizedSource, destRect, pivot, flip: false); DrawMirrored((Texture)(object)texture, normalizedSource, destRect, pivot, flip: true); } finally { GL.PopMatrix(); } Texture2D val2 = new Texture2D(num, num2, (TextureFormat)4, false); val2.ReadPixels(new Rect(0f, 0f, (float)num, (float)num2), 0, 0); val2.Apply(); icon = Sprite.Create(val2, new Rect(0f, 0f, (float)num, (float)num2), new Vector2(0.5f, 0.5f)); DualSwordsPlugin.Log.LogInfo((object)$"{name}: иконка собрана режимом Mirror — размер={num}x{num2}"); return true; } catch (Exception ex) { error = name + ": непредвиденное исключение при сборке зеркальной иконки — " + ex.Message; icon = null; return false; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { RenderTexture.ReleaseTemporary(val); } } } private static void DrawMirrored(Texture source, Rect normalizedSource, Rect destRect, Vector3 pivot, bool flip) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) GL.PushMatrix(); GL.MultMatrix(Matrix4x4.TRS(pivot, Quaternion.identity, new Vector3(flip ? (-1f) : 1f, 1f, 1f))); Graphics.DrawTexture(destRect, source, normalizedSource, 0, 0, 0, 0); GL.PopMatrix(); } } [HarmonyPatch(typeof(ItemStand), "SetVisualItem")] internal static class ItemStandPatch { private static void Postfix(int itemHash, int variant, int quality, ref GameObject ___m_visualItem) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = ___m_visualItem; if (itemHash == 0 || (Object)(object)val == (Object)null || val.activeSelf) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemHash); if ((Object)(object)itemPrefab == (Object)null || !LeftHandPatch.CatalogueIds.Contains(((Object)itemPrefab).name)) { return; } Transform val2 = itemPrefab.transform.Find("attach_back"); if ((Object)(object)val2 == (Object)null) { val.SetActive(true); DualSwordsPlugin.Log.LogError((object)("ItemStandPatch: у " + ((Object)itemPrefab).name + " нет узла attach_back — на подставке показан один меч")); return; } GameObject val3 = Object.Instantiate<GameObject>(((Component)val2).gameObject, val.transform.parent, false); val3.transform.localPosition = val.transform.localPosition; val3.transform.localRotation = val.transform.localRotation; val3.transform.localScale = val.transform.localScale; val3.SetActive(true); ParticleIntensityScaler[] componentsInChildren = val3.GetComponentsInChildren<ParticleIntensityScaler>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].SetQuality(quality); } IEquipmentVisual componentInChildren = val3.GetComponentInChildren<IEquipmentVisual>(); if (componentInChildren != null) { componentInChildren.Setup(variant); } Object.Destroy((Object)(object)val); ___m_visualItem = val3; } catch (Exception ex) { DualSwordsPlugin.Log.LogError((object)$"ItemStandPatch: не удалось показать пару мечей на подставке (itemHash={itemHash}) — {ex.Message}"); } } } internal sealed class ItemStatEntries { private readonly Dictionary<DamageType, ConfigEntry<float>> _damage = new Dictionary<DamageType, ConfigEntry<float>>(); private readonly Dictionary<DamageType, ConfigEntry<float>> _damagePerLevel = new Dictionary<DamageType, ConfigEntry<float>>(); private ConfigEntry<bool> _customStats; private ConfigEntry<float> _blockPower; private ConfigEntry<int> _durability; private ConfigEntry<int> _durabilityPerLevel; private ConfigEntry<float> _weight; private ConfigEntry<int> _primaryStamina; private ConfigEntry<int> _secondaryStamina; private ConfigEntry<float> _attackForce; private ConfigEntry<float> _blockForce; private ConfigEntry<string> _recipe; public static ItemStatEntries Bind(ConfigFile file, DualSwordDeclaration declaration) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Expected O, but got Unknown string id = declaration.Id; string english = declaration.Name.English; ItemStatValues val = ItemStatValues.DefaultsFor(declaration); ItemStatEntries itemStatEntries = new ItemStatEntries(); itemStatEntries._customStats = file.Bind<bool>("Item stats", ConfigKeys.CustomStats(id), false, new ConfigDescription("true = " + english + " (" + id + ") uses the other " + id + ".* values of this section exactly as entered, and the [Balance] multipliers do not affect it. false = those values are ignored and the built-in numbers are used. Read once at game start; not synchronized with a server — every player uses their own file", (AcceptableValueBase)null, Array.Empty<object>())); foreach (DamageType item in DamageFields.NonZero(declaration.DualStats)) { itemStatEntries._damage[item] = file.Bind<float>("Item stats", ConfigKeys.Damage(id, item), val.Damage[item], Range(0f, 500f)); itemStatEntries._damagePerLevel[item] = file.Bind<float>("Item stats", ConfigKeys.DamagePerLevel(id, item), val.DamagePerLevel[item], Range(0f, 500f)); } itemStatEntries._blockPower = file.Bind<float>("Item stats", ConfigKeys.BlockPower(id), val.BlockPower, Range(0f, 500f)); itemStatEntries._durability = file.Bind<int>("Item stats", ConfigKeys.Durability(id), val.Durability, Range(1, 5000)); itemStatEntries._durabilityPerLevel = file.Bind<int>("Item stats", ConfigKeys.DurabilityPerLevel(id), val.DurabilityPerLevel, Range(0, 1000)); itemStatEntries._weight = file.Bind<float>("Item stats", ConfigKeys.Weight(id), val.Weight, Range(0f, 100f)); itemStatEntries._primaryStamina = file.Bind<int>("Item stats", ConfigKeys.PrimaryAttackStamina(id), val.PrimaryAttackStamina, Range(0, 200)); itemStatEntries._secondaryStamina = file.Bind<int>("Item stats", ConfigKeys.SecondaryAttackStamina(id), val.SecondaryAttackStamina, Range(0, 200)); itemStatEntries._attackForce = file.Bind<float>("Item stats", ConfigKeys.AttackForce(id), val.AttackForce, Range(0f, 500f)); itemStatEntries._blockForce = file.Bind<float>("Item stats", ConfigKeys.BlockForce(id), val.BlockForce, Range(0f, 500f)); itemStatEntries._recipe = file.Bind<string>("Item stats", ConfigKeys.Recipe(id), val.Recipe, new ConfigDescription("Recipe of " + english + ". Format: Prefab,Amount,AmountPerLevel|Prefab,Amount,AmountPerLevel. " + $"Crafted at {declaration.DualRecipe.Station}, level {declaration.DualRecipe.MinStationLevel} — not configurable. " + "An invalid recipe or an unknown prefab falls back to the built-in recipe", (AcceptableValueBase)null, Array.Empty<object>())); return itemStatEntries; } public ItemStatValues ToValues() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) ItemStatValues val = new ItemStatValues { CustomStats = _customStats.Value, BlockPower = _blockPower.Value, Durability = _durability.Value, DurabilityPerLevel = _durabilityPerLevel.Value, Weight = _weight.Value, PrimaryAttackStamina = _primaryStamina.Value, SecondaryAttackStamina = _secondaryStamina.Value, AttackForce = _attackForce.Value, BlockForce = _blockForce.Value, Recipe = _recipe.Value }; foreach (KeyValuePair<DamageType, ConfigEntry<float>> item in _damage) { val.Damage[item.Key] = item.Value.Value; } foreach (KeyValuePair<DamageType, ConfigEntry<float>> item2 in _damagePerLevel) { val.DamagePerLevel[item2.Key] = item2.Value.Value; } return val; } private static ConfigDescription Range(float min, float max) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown return new ConfigDescription(string.Empty, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()); } private static ConfigDescription Range(int min, int max) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown return new ConfigDescription(string.Empty, (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max), Array.Empty<object>()); } } internal sealed class LeftHandCleanup : MonoBehaviour { public GameObject LeftHandInstance; private void OnDestroy() { if ((Object)(object)LeftHandInstance != (Object)null) { Object.Destroy((Object)(object)LeftHandInstance); } } } [HarmonyPatch(typeof(VisEquipment), "AttachItem")] internal static class LeftHandPatch { private static HashSet<string> _catalogueIds; internal static HashSet<string> CatalogueIds => _catalogueIds ?? (_catalogueIds = new HashSet<string>(from declaration in Catalogue.All() select declaration.Id)); private static void Postfix(VisEquipment __instance, GameObject __result, int itemHash, Transform joint) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__result == (Object)null || (Object)(object)joint != (Object)(object)__instance.m_rightHand) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemHash); if (!((Object)(object)itemPrefab == (Object)null) && CatalogueIds.Contains(((Object)itemPrefab).name)) { Transform leftHand = __instance.m_leftHand; if (!((Object)(object)leftHand == (Object)null)) { GameObject val = Object.Instantiate<GameObject>(__result); val.transform.SetParent(leftHand, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; __result.AddComponent<LeftHandCleanup>().LeftHandInstance = val; } } } catch (Exception ex) { DualSwordsPlugin.Log.LogError((object)$"LeftHandPatch: не удалось повесить второй меч на левую руку (itemHash={itemHash}) — {ex.Message}"); } } } internal static class MeshOrientation { public static Quaternion For(Bounds swordBounds, Bounds targetBounds) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) int num = LongestAxis(((Bounds)(ref swordBounds)).extents); int num2 = LongestAxis(((Bounds)(ref targetBounds)).extents); if (num == num2) { return Quaternion.identity; } return Quaternion.FromToRotation(AxisVector(num), AxisVector(num2)); } private static int LongestAxis(Vector3 extents) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (extents.x >= extents.y && extents.x >= extents.z) { return 0; } if (!(extents.y >= extents.z)) { return 2; } return 1; } private static Vector3 AxisVector(int axis) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) return (Vector3)(axis switch { 0 => Vector3.right, 1 => Vector3.up, _ => Vector3.forward, }); } } internal sealed class PlayerRigInfo { public Transform[] Bones { get; } public int BoneCount => Bones.Length; public int RightHandIndex { get; } public int LeftHandIndex { get; } public Matrix4x4 RightJointToBone { get; } public Matrix4x4 LeftJointToBone { get; } private PlayerRigInfo(Transform[] bones, int rightHandIndex, int leftHandIndex, Matrix4x4 rightJointToBone, Matrix4x4 leftJointToBone) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Bones = bones; RightHandIndex = rightHandIndex; LeftHandIndex = leftHandIndex; RightJointToBone = rightJointToBone; LeftJointToBone = leftJointToBone; } public static PlayerRigInfo TryBuild(GameObject playerPrefab, out string error) { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) error = null; if ((Object)(object)playerPrefab == (Object)null) { error = "префаб Player не найден"; return null; } VisEquipment component = playerPrefab.GetComponent<VisEquipment>(); if ((Object)(object)component == (Object)null) { error = "у префаба Player нет компонента VisEquipment"; return null; } SkinnedMeshRenderer bodyModel = component.m_bodyModel; if ((Object)(object)bodyModel == (Object)null || bodyModel.bones == null || bodyModel.bones.Length == 0) { error = "у VisEquipment.m_bodyModel нет костей"; return null; } Transform[] bones = bodyModel.bones; string[] array = new string[bones.Length]; for (int i = 0; i < bones.Length; i++) { array[i] = (((Object)(object)bones[i] == (Object)null) ? null : ((Object)bones[i]).name); } Matrix4x4 jointToBone; int num = ResolveJoint(component.m_rightHand, array, bones, out jointToBone); if (num == -1) { error = "кость правой кисти не найдена в массиве костей тела"; return null; } Matrix4x4 jointToBone2; int num2 = ResolveJoint(component.m_leftHand, array, bones, out jointToBone2); if (num2 == -1) { error = "кость левой кисти не найдена в массиве костей тела"; return null; } return new PlayerRigInfo(bones, num, num2, jointToBone, jointToBone2); } private static int ResolveJoint(Transform joint, string[] boneNames, Transform[] bones, out Matrix4x4 jointToBone) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) jointToBone = Matrix4x4.identity; if ((Object)(object)joint == (Object)null) { return -1; } List<string> list = new List<string>(); Transform val = joint; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); val = val.parent; } int num = BoneResolver.ResolveIndex((IReadOnlyList<string>)boneNames, (IReadOnlyList<string>)list); if (num == -1) { return -1; } jointToBone = bones[num].worldToLocalMatrix * joint.localToWorldMatrix; return num; } } internal sealed class ProbeCommand : ConsoleCommand { public override string Name => "dualswords.probe"; public override string Help => "Вывести в лог трансформы и эффекты предмета в руках и во всех суставах за спиной локального игрока — для сравнения одноручного меча с парным"; public override void Run(string[] args) { ManualLogSource log = DualSwordsPlugin.Log; try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { log.LogInfo((object)"dualswords.probe: локальный игрок не найден — выполните команду в мире"); return; } VisEquipment component = ((Component)localPlayer).GetComponent<VisEquipment>(); if ((Object)(object)component == (Object)null) { log.LogInfo((object)"dualswords.probe: у игрока нет компонента VisEquipment"); return; } Transform rightHand = component.m_rightHand; Transform leftHand = component.m_leftHand; bool num = (Object)(object)rightHand == (Object)null || rightHand.childCount == 0; bool flag = (Object)(object)leftHand == (Object)null || leftHand.childCount == 0; if (num && flag) { log.LogInfo((object)"dualswords.probe: в руках пусто"); return; } ProbeJoint("правая рука", rightHand, log); ProbeJoint("левая рука", leftHand, log); ProbeJoint("за спиной: щит (m_backShield)", component.m_backShield, log); ProbeJoint("за спиной: одноручное оружие (m_backMelee)", component.m_backMelee, log); ProbeJoint("за спиной: двуручное оружие (m_backTwohandedMelee)", component.m_backTwohandedMelee, log); ProbeJoint("за спиной: лук (m_backBow)", component.m_backBow, log); ProbeJoint("за спиной: инструмент (m_backTool)", component.m_backTool, log); ProbeJoint("за спиной: атгейр (m_backAtgeir)", component.m_backAtgeir, log); } catch (Exception arg) { log.LogError((object)$"dualswords.probe: непредвиденное исключение — {arg}"); } } private static void ProbeJoint(string sectionLabel, Transform joint, ManualLogSource log) { if ((Object)(object)joint == (Object)null) { log.LogInfo((object)("dualswords.probe: " + sectionLabel + " — сустав не найден в VisEquipment")); } else if (joint.childCount == 0) { log.LogInfo((object)("dualswords.probe: " + sectionLabel + " (" + ((Object)joint).name + ") — пусто")); } else { StringBuilder stringBuilder = new StringBuilder(); int particleSystemCount = 0; AppendNode(joint, 0, stringBuilder, ref particleSystemCount); stringBuilder.Append("систем частиц в поддереве: ").Append(particleSystemCount).AppendLine(); log.LogInfo((object)$"dualswords.probe: {sectionLabel} ({((Object)joint).name}):\n{stringBuilder}"); } } private static void AppendNode(Transform node, int depth, StringBuilder text, ref int particleSystemCount) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) string value = new string(' ', depth * 2); StringBuilder stringBuilder = text.Append(value).Append(((Object)node).name).Append(" localScale=") .Append(FormatVector3(node.localScale)) .Append(" lossyScale=") .Append(FormatVector3(node.lossyScale)) .Append(" localPosition=") .Append(FormatVector3(node.localPosition)) .Append(" localRotation.euler="); Quaternion localRotation = node.localRotation; stringBuilder.Append(FormatVector3(((Quaternion)(ref localRotation)).eulerAngles)).Append(" activeSelf=").Append(((Component)node).gameObject.activeSelf) .Append(" activeInHierarchy=") .Append(((Component)node).gameObject.activeInHierarchy) .AppendLine(); ParticleSystem component = ((Component)node).GetComponent<ParticleSystem>(); if ((Object)(object)component != (Object)null) { particleSystemCount++; MainModule main = component.main; EmissionModule emission = component.emission; text.Append(value).Append(" [ParticleSystem]").Append(" simulationSpace=") .Append(((MainModule)(ref main)).simulationSpace) .Append(" scalingMode=") .Append(((MainModule)(ref main)).scalingMode) .Append(" startSize=") .Append(FormatCurve(((MainModule)(ref main)).startSize)) .Append(" startLifetime=") .Append(FormatCurve(((MainModule)(ref main)).startLifetime)) .Append(" rateOverTime=") .Append(FormatCurve(((EmissionModule)(ref emission)).rateOverTime)) .AppendLine(); ParticleSystemRenderer component2 = ((Component)node).GetComponent<ParticleSystemRenderer>(); if ((Object)(object)component2 != (Object)null) { Material sharedMaterial = ((Renderer)component2).sharedMaterial; string value2 = (((Object)(object)sharedMaterial != (Object)null) ? ((Object)sharedMaterial).name : "НЕТ"); string value3 = (((Object)(object)sharedMaterial != (Object)null && (Object)(object)sharedMaterial.shader != (Object)null) ? ((Object)sharedMaterial.shader).name : "НЕТ"); string value4 = (((Object)(object)sharedMaterial != (Object)null && (Object)(object)sharedMaterial.mainTexture != (Object)null) ? ((Object)sharedMaterial.mainTexture).name : "<ТЕКСТУРА НЕ НАЙДЕНА>"); text.Append(value).Append(" [ParticleSystemRenderer]").Append(" renderMode=") .Append(component2.renderMode) .Append(" material=") .Append(value2) .Append(" shader=") .Append(value3) .Append(" mainTexture=") .Append(value4) .AppendLine(); } } Light component3 = ((Component)node).GetComponent<Light>(); if ((Object)(object)component3 != (Object)null) { text.Append(value).Append(" [Light]").Append(" type=") .Append(component3.type) .Append(" range=") .Append(component3.range.ToString("F3")) .Append(" intensity=") .Append(component3.intensity.ToString("F3")) .Append(" color=") .Append(FormatColor(component3.color)) .AppendLine(); } ParticleIntensityScaler component4 = ((Component)node).GetComponent<ParticleIntensityScaler>(); if ((Object)(object)component4 != (Object)null) { text.Append(value).Append(" [ParticleIntensityScaler]").Append(" intensity=") .Append(component4.intensity.ToString("F3")) .Append(" maxQuality=") .Append(component4.maxQuality) .Append(" itemDropLevelMultiplier=") .Append(component4.itemDropLevelMultiplier.ToString("F3")) .AppendLine(); } for (int i = 0; i < node.childCount; i++) { AppendNode(node.GetChild(i), depth + 1, text, ref particleSystemCount); } } private static string FormatCurve(MinMaxCurve curve) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) ParticleSystemCurveMode mode = ((MinMaxCurve)(ref curve)).mode; return (int)mode switch { 0 => "Constant(" + ((MinMaxCurve)(ref curve)).constant.ToString("F3") + ")", 3 => "TwoConstants(" + ((MinMaxCurve)(ref curve)).constantMin.ToString("F3") + ".." + ((MinMaxCurve)(ref curve)).constantMax.ToString("F3") + ")", 1 => "Curve(multiplier=" + ((MinMaxCurve)(ref curve)).curveMultiplier.ToString("F3") + ")", 2 => "TwoCurves(multiplier=" + ((MinMaxCurve)(ref curve)).curveMultiplier.ToString("F3") + ")", _ => ((object)((MinMaxCurve)(ref curve)).mode/*cast due to .constrained prefix*/).ToString(), }; } private static string FormatVector3(Vector3 v) { return "(" + v.x.ToString("F3") + ", " + v.y.ToString("F3") + ", " + v.z.ToString("F3") + ")"; } private static string FormatColor(Color c) { return "(" + c.r.ToString("F3") + ", " + c.g.ToString("F3") + ", " + c.b.ToString("F3") + ", " + c.a.ToString("F3") + ")"; } } internal static class StatDriftGuard { private const float Tolerance = 0.01f; private static readonly string[] DamageFieldNames = new string[12] { "damage", "blunt", "slash", "pierce", "chop", "pickaxe", "fire", "frost", "lightning", "poison", "spirit", "nonPlayer" }; private static readonly Func<DamageProfile, float>[] DamageFieldSelectors = new Func<DamageProfile, float>[12] { (DamageProfile p) => p.Damage, (DamageProfile p) => p.Blunt, (DamageProfile p) => p.Slash, (DamageProfile p) => p.Pierce, (DamageProfile p) => p.Chop, (DamageProfile p) => p.Pickaxe, (DamageProfile p) => p.Fire, (DamageProfile p) => p.Frost, (DamageProfile p) => p.Lightning, (DamageProfile p) => p.Poison, (DamageProfile p) => p.Spirit, (DamageProfile p) => p.NonPlayer }; public static void Report(string baseSwordPrefabName, StatSnapshot declared, SharedData live) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (declared == null || live == null) { return; } DamageProfile arg = VanillaBridge.ReadDamage(live.m_damages); DamageProfile arg2 = VanillaBridge.ReadDamage(live.m_damagesPerLevel); for (int i = 0; i < DamageFieldNames.Length; i++) { string text = DamageFieldNames[i]; Func<DamageProfile, float> func = DamageFieldSelectors[i]; float num = func(declared.Damage); if (num != 0f) { CompareFloat(baseSwordPrefabName, "урон " + text, num, func(arg)); } float num2 = func(declared.DamagePerLevel); if (num2 != 0f) { CompareFloat(baseSwordPrefabName, "прирост урона " + text + " за уровень", num2, func(arg2)); } } CompareFloat(baseSwordPrefabName, "максимальная прочность", declared.MaxDurability, live.m_maxDurability); CompareFloat(baseSwordPrefabName, "прирост прочности за уровень", declared.DurabilityPerLevel, live.m_durabilityPerLevel); CompareFloat(baseSwordPrefabName, "блок", declared.BlockPower, live.m_blockPower); CompareFloat(baseSwordPrefabName, "вес", declared.Weight, live.m_weight); if (live.m_attack != null) { CompareFloat(baseSwordPrefabName, "стамина основной атаки", declared.PrimaryStamina, live.m_attack.m_attackStamina); } if (live.m_secondaryAttack != null) { CompareFloat(baseSwordPrefabName, "стамина вторичной атаки", declared.SecondaryStamina, live.m_secondaryAttack.m_attackStamina); } if (declared.ToolTier != live.m_toolTier) { DualSwordsPlugin.Log.LogWarning((object)$"{baseSwordPrefabName}: tool tier в декларации {declared.ToolTier}, в игре {live.m_toolTier}"); } } private static void CompareFloat(string prefabName, string label, float declaredValue, float liveValue) { if (Math.Abs(declaredValue - liveValue) > 0.01f) { DualSwordsPlugin.Log.LogWarning((object)$"{prefabName}: {label} в декларации {declaredValue}, в игре {liveValue}"); } } } internal sealed class StatsCommand : ConsoleCommand { private static readonly string[] DefaultPrefabs = new string[12] { "SwordBronze", "SwordIron", "SwordSilver", "SwordBlackmetal", "SwordIronFire", "SwordCheat", "AxeFlint", "AxeBronze", "AxeIron", "AxeBlackMetal", "AxeJotunBane", "AxeBerzerkr" }; private const int FindResultLimit = 60; public override string Name => "dualswords.stats"; public override string Help => "Вывести в лог статы и рецепт префабов (без аргументов — список одноручных мечей и топоров по умолчанию, с аргументами — перечисленные имена префабов; 'find <строка...>' — поиск предметов ObjectDB по вхождению подстроки в имени префаба, без учёта регистра, отдельный список на каждую строку поиска)"; public override void Run(string[] args) { if (args != null && args.Length != 0 && string.Equals(args[0], "find", StringComparison.OrdinalIgnoreCase)) { RunFind(args); return; } string[] array = ((args != null && args.Length != 0) ? args : DefaultPrefabs); foreach (string text in array) { try { DumpStats(text); } catch (Exception ex) { DualSwordsPlugin.Log.LogError((object)(text + ": непредвиденное исключение при выводе статов — " + ex.Message)); } } } private static void RunFind(string[] args) { ManualLogSource log = DualSwordsPlugin.Log; string text = "dualswords.stats"; string[] array = new string[args.Length - 1]; Array.Copy(args, 1, array, 0, array.Length); if (array.Length == 0) { log.LogInfo((object)(text + ": режим find требует хотя бы одной строки поиска, например 'dualswords.stats find Sword'")); return; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || instance.m_items == null) { log.LogInfo((object)(text + ": ObjectDB недоступен — выполните команду находясь в мире, а не в главном меню")); return; } List<GameObject> items = instance.m_items; string[] array2 = array; foreach (string term in array2) { RunFindForTerm(text, term, items); } } private static void RunFindForTerm(string commandName, string term, List<GameObject> items) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = DualSwordsPlugin.Log; List<string> list = new List<string>(); int num = 0; foreach (GameObject item in items) { try { if ((Object)(object)item == (Object)null) { continue; } string name = ((Object)item).name; if (string.IsNullOrEmpty(name) || name.IndexOf(term, StringComparison.OrdinalIgnoreCase) < 0) { continue; } ItemDrop component = item.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null) { num++; if (list.Count < 60) { SharedData shared = component.m_itemData.m_shared; list.Add($"{name}: itemType={shared.m_itemType}, skillType={shared.m_skillType}, " + "damages " + FormatDamage(shared.m_damages)); } } } catch (Exception ex) { log.LogError((object)(commandName + ": непредвиденное исключение при поиске '" + term + "' — " + ex.Message)); } } foreach (string item2 in list) { log.LogInfo((object)item2); } if (num > 60) { log.LogInfo((object)$"{commandName}: find '{term}' — показаны первые {60} из {num} совпадений, вывод усечён"); } log.LogInfo((object)$"{commandName}: find '{term}' — найдено {num}"); } private static void DumpStats(string prefabName) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = DualSwordsPlugin.Log; GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { log.LogInfo((object)(prefabName + ": префаб не найден в этой сборке игры, пропущен")); return; } ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { log.LogInfo((object)(prefabName + ": нет компонента ItemDrop, пропущен")); return; } SharedData shared = component.m_itemData.m_shared; log.LogInfo((object)($"{prefabName}: itemType={shared.m_itemType}, animationState={shared.m_animationState}, " + $"skillType={shared.m_skillType}, toolTier={shared.m_toolTier}, maxQuality={shared.m_maxQuality}, weight={shared.m_weight}")); log.LogInfo((object)(prefabName + ": damages " + FormatDamage(shared.m_damages))); log.LogInfo((object)(prefabName + ": damagesPerLevel " + FormatDamage(shared.m_damagesPerLevel))); log.LogInfo((object)($"{prefabName}: maxDurability={shared.m_maxDurability}, durabilityPerLevel={shared.m_durabilityPerLevel}, " + $"useDurabilityDrain={shared.m_useDurabilityDrain}")); log.LogInfo((object)($"{prefabName}: blockPower={shared.m_blockPower}, blockPowerPerLevel={shared.m_blockPowerPerLevel}, " + $"deflectionForce={shared.m_deflectionForce}, attackForce={shared.m_attackForce}, timedBlockBonus={shared.m_timedBlockBonus}")); log.LogInfo((object)($"{prefabName}: attack={shared.m_attack.m_attackAnimation} (stamina={shared.m_attack.m_attackStamina}), " + $"secondary={shared.m_secondaryAttack.m_attackAnimation} (stamina={shared.m_secondaryAttack.m_attackStamina})")); log.LogInfo((object)$"{prefabName}: movementModifier={shared.m_movementModifier}"); DumpRecipe(prefabName, component); } private static void DumpRecipe(string prefabName, ItemDrop drop) { ManualLogSource log = DualSwordsPlugin.Log; Recipe val = (((Object)(object)ObjectDB.instance == (Object)null) ? null : ObjectDB.instance.GetRecipe(drop.m_itemData)); if ((Object)(object)val == (Object)null) { log.LogInfo((object)(prefabName + ": рецепт не найден")); return; } string text = (((Object)(object)val.m_craftingStation != (Object)null) ? ((Object)val.m_craftingStation).name : "нет"); log.LogInfo((object)$"{prefabName}: рецепт станок={text}, minStationLevel={val.m_minStationLevel}, amount={val.m_amount}"); if (val.m_resources == null || val.m_resources.Length == 0) { log.LogInfo((object)(prefabName + ": рецепт без требований")); return; } Requirement[] resources = val.m_resources; foreach (Requirement val2 in resources) { if (val2 != null) { string text2 = (((Object)(object)val2.m_resItem != (Object)null) ? ((Object)((Component)val2.m_resItem).gameObject).name : "?"); log.LogInfo((object)$"{prefabName}: требование {text2} amount={val2.m_amount} amountPerLevel={val2.m_amountPerLevel}"); } } } private static string FormatDamage(DamageTypes damage) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); AppendIfNonZero(stringBuilder, "damage", damage.m_damage); AppendIfNonZero(stringBuilder, "blunt", damage.m_blunt); AppendIfNonZero(stringBuilder, "slash", damage.m_slash); AppendIfNonZero(stringBuilder, "pierce", damage.m_pierce); AppendIfNonZero(stringBuilder, "chop", damage.m_chop); AppendIfNonZero(stringBuilder, "pickaxe", damage.m_pickaxe); AppendIfNonZero(stringBuilder, "fire", damage.m_fire); AppendIfNonZero(stringBuilder, "frost", damage.m_frost); AppendIfNonZero(stringBuilder, "lightning", damage.m_lightning); AppendIfNonZero(stringBuilder, "poison", damage.m_poison); AppendIfNonZero(stringBuilder, "spirit", damage.m_spirit); AppendIfNonZero(stringBuilder, "nonPlayer", damage.m_nonPlayer); if (stringBuilder.Length != 0) { return stringBuilder.ToString().TrimEnd(Array.Empty<char>()); } return "(все нули)"; } private static void AppendIfNonZero(StringBuilder text, string name, float value) { if (value != 0f) { text.Append(name).Append('=').Append(value) .Append(' '); } } } internal static class VanillaBridge { public static DamageProfile ReadDamage(DamageTypes source) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown return new DamageProfile { Damage = source.m_damage, Blunt = source.m_blunt, Slash = source.m_slash, Pierce = source.m_pierce, Chop = source.m_chop, Pickaxe = source.m_pickaxe, Fire = source.m_fire, Frost = source.m_frost, Lightning = source.m_lightning, Poison = source.m_poison, Spirit = source.m_spirit, NonPlayer = source.m_nonPlayer }; } public static DamageTypes WriteDamage(DamageProfile source) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) return new DamageTypes { m_damage = source.Damage, m_blunt = source.Blunt, m_slash = source.Slash, m_pierce = source.Pierce, m_chop = source.Chop, m_pickaxe = source.Pickaxe, m_fire = source.Fire, m_frost = source.Frost, m_lightning = source.Lightning, m_poison = source.Poison, m_spirit = source.Spirit, m_nonPlayer = source.NonPlayer }; } } }