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 Seidr Enchanting v0.5.0
BepInEx/plugins/SeidrEnchanting/SeidrEnchanting.dll
Decompiled 14 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SeidrEnchanting")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.5.0.0")] [assembly: AssemblyInformationalVersion("0.5.0")] [assembly: AssemblyProduct("SeidrEnchanting")] [assembly: AssemblyTitle("SeidrEnchanting")] [assembly: AssemblyVersion("0.5.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 SeidrEnchanting { public enum AffixStat { None, DamagePct, ArmorPct, Health, Stamina, Eitr, MoveSpeedPct, BlockPct, DurabilityPct, Fire, Frost, Lightning, Poison, Spirit, Regen, StaminaRegenPct, EitrRegenPct, HealthRegenPct, StealthPct, DamageReductionPct, PoisonResistPct, FrostResistPct, FireResistPct, LightningResistPct, StaggerResistPct, KnockbackResistPct, FallDamageReductionPct, JumpHeightPct, SwimSpeedPct, DodgeStaminaPct, BlockStaminaPct, ParryBonusPct, BackstabPct, BossDamagePct, AnimalDamagePct, UndeadDamagePct, PickaxeDamagePct, ChopDamagePct, BowDamagePct, SpellDamagePct, ArmorPenPct, LowHealthDamagePct, FoodDurationPct, FoodRegenPct, RestedBonusPct, ExplosionResistPct, CraftingBonusPct, SneakDamagePct, ReflectDamagePct, ProjectileDeflectPct } [Flags] public enum AffixPool { None = 0, Weapon = 1, Armor = 2, Shield = 4 } public enum AffixProc { None, Stagger, Lifesteal, Frostbite, Ignite, ChainLightning, Bleed, Knockback, Root, ArmorBreak, ExtraHit, EarthSlam, PullLightning, SoulDrain, SummonVermin, Echo, ParrySlow, PhaseShift, Combust, Execute, OnKillBuff, Decay, Weakness, Riposte, GiantSlayer, ThornReflect, BlockCharge, LastStand, Vengeance, AbsorbImpact, Phoenix, FrostAura, EitrShield, Deflect, SprintTrail, MagmaAura, SecondWind, Invisibility, WaterBreath, RainProof, NightBuff, DayBuff, HomeRest } public sealed class StatWeight { public readonly AffixStat Stat; public readonly float Weight; public StatWeight(AffixStat stat, float weight) { Stat = stat; Weight = weight; } } public sealed class AffixDef { public readonly string Id; public readonly AffixPool Pool; public readonly StatWeight[] Stats; public readonly AffixProc Proc; public readonly float ProcChancePerTier; public readonly float ProcCooldown; public readonly float ProcWeight; public readonly bool ProcPercent; public readonly string Ru; public readonly string En; public readonly string HintRu; public readonly string HintEn; public bool HasProc => Proc != AffixProc.None; public float Cooldown { get { float num = ((Settings.AffixCooldown != null) ? Settings.AffixCooldown.Value : 1f); return Math.Max(0f, ProcCooldown * num); } } public AffixDef(string id, AffixPool pool, StatWeight[] stats, AffixProc proc, float procChancePerTier, float procCooldown, float procWeight, bool procPercent, string ru, string en, string hintRu = null, string hintEn = null) { Id = id; Pool = pool; Stats = stats ?? Array.Empty<StatWeight>(); Proc = proc; ProcChancePerTier = procChancePerTier; ProcCooldown = procCooldown; ProcWeight = procWeight; ProcPercent = procPercent; Ru = ru; En = en; HintRu = hintRu; HintEn = hintEn; } public float Chance(int tier) { if (!HasProc) { return 0f; } float num = ((Settings.AffixChance != null) ? Settings.AffixChance.Value : 1f); return Math.Min(0.75f, ProcChancePerTier * (float)Math.Max(1, tier) * num); } } public sealed class Affix { public string Id; public int Tier; public float Value; public Affix() { } public Affix(string id, int tier, float value) { Id = id; Tier = Math.Max(1, Math.Min(5, tier)); Value = value; } public Affix Clone() { return new Affix(Id, Tier, Value); } public override string ToString() { return string.Format("{0}:{1}:{2}", Id, Tier, Value.ToString("0.##", CultureInfo.InvariantCulture)); } } public struct Bonuses { public float DamagePct; public float ArmorPct; public float Health; public float Stamina; public float Eitr; public float MoveSpeedPct; public float BlockPct; public float DurabilityPct; public float Fire; public float Frost; public float Lightning; public float Poison; public float Spirit; public float Regen; public float StaminaRegenPct; public float EitrRegenPct; public float HealthRegenPct; public float StealthPct; public float DamageReductionPct; public float PoisonResistPct; public float FrostResistPct; public float FireResistPct; public float LightningResistPct; public float StaggerResistPct; public float KnockbackResistPct; public float FallDamageReductionPct; public float JumpHeightPct; public float SwimSpeedPct; public float DodgeStaminaPct; public float BlockStaminaPct; public float ParryBonusPct; public float BackstabPct; public float BossDamagePct; public float AnimalDamagePct; public float UndeadDamagePct; public float PickaxeDamagePct; public float ChopDamagePct; public float BowDamagePct; public float SpellDamagePct; public float ArmorPenPct; public float LowHealthDamagePct; public float FoodDurationPct; public float FoodRegenPct; public float RestedBonusPct; public float ExplosionResistPct; public float CraftingBonusPct; public float SneakDamagePct; public float ReflectDamagePct; public float ProjectileDeflectPct; public void Add(Bonuses other) { DamagePct += other.DamagePct; ArmorPct += other.ArmorPct; Health += other.Health; Stamina += other.Stamina; Eitr += other.Eitr; MoveSpeedPct += other.MoveSpeedPct; BlockPct += other.BlockPct; DurabilityPct += other.DurabilityPct; Fire += other.Fire; Frost += other.Frost; Lightning += other.Lightning; Poison += other.Poison; Spirit += other.Spirit; Regen += other.Regen; StaminaRegenPct += other.StaminaRegenPct; EitrRegenPct += other.EitrRegenPct; HealthRegenPct += other.HealthRegenPct; StealthPct += other.StealthPct; DamageReductionPct += other.DamageReductionPct; PoisonResistPct += other.PoisonResistPct; FrostResistPct += other.FrostResistPct; FireResistPct += other.FireResistPct; LightningResistPct += other.LightningResistPct; StaggerResistPct += other.StaggerResistPct; KnockbackResistPct += other.KnockbackResistPct; FallDamageReductionPct += other.FallDamageReductionPct; JumpHeightPct += other.JumpHeightPct; SwimSpeedPct += other.SwimSpeedPct; DodgeStaminaPct += other.DodgeStaminaPct; BlockStaminaPct += other.BlockStaminaPct; ParryBonusPct += other.ParryBonusPct; BackstabPct += other.BackstabPct; BossDamagePct += other.BossDamagePct; AnimalDamagePct += other.AnimalDamagePct; UndeadDamagePct += other.UndeadDamagePct; PickaxeDamagePct += other.PickaxeDamagePct; ChopDamagePct += other.ChopDamagePct; BowDamagePct += other.BowDamagePct; SpellDamagePct += other.SpellDamagePct; ArmorPenPct += other.ArmorPenPct; LowHealthDamagePct += other.LowHealthDamagePct; FoodDurationPct += other.FoodDurationPct; FoodRegenPct += other.FoodRegenPct; RestedBonusPct += other.RestedBonusPct; ExplosionResistPct += other.ExplosionResistPct; CraftingBonusPct += other.CraftingBonusPct; SneakDamagePct += other.SneakDamagePct; ReflectDamagePct += other.ReflectDamagePct; ProjectileDeflectPct += other.ProjectileDeflectPct; } public void AddStat(AffixStat stat, float value) { switch (stat) { case AffixStat.DamagePct: DamagePct += value; break; case AffixStat.ArmorPct: ArmorPct += value; break; case AffixStat.Health: Health += value; break; case AffixStat.Stamina: Stamina += value; break; case AffixStat.Eitr: Eitr += value; break; case AffixStat.MoveSpeedPct: MoveSpeedPct += value; break; case AffixStat.BlockPct: BlockPct += value; break; case AffixStat.DurabilityPct: DurabilityPct += value; break; case AffixStat.Fire: Fire += value; break; case AffixStat.Frost: Frost += value; break; case AffixStat.Lightning: Lightning += value; break; case AffixStat.Poison: Poison += value; break; case AffixStat.Spirit: Spirit += value; break; case AffixStat.Regen: Regen += value; break; case AffixStat.StaminaRegenPct: StaminaRegenPct += value; break; case AffixStat.EitrRegenPct: EitrRegenPct += value; break; case AffixStat.HealthRegenPct: HealthRegenPct += value; break; case AffixStat.StealthPct: StealthPct += value; break; case AffixStat.DamageReductionPct: DamageReductionPct += value; break; case AffixStat.PoisonResistPct: PoisonResistPct += value; break; case AffixStat.FrostResistPct: FrostResistPct += value; break; case AffixStat.FireResistPct: FireResistPct += value; break; case AffixStat.LightningResistPct: LightningResistPct += value; break; case AffixStat.StaggerResistPct: StaggerResistPct += value; break; case AffixStat.KnockbackResistPct: KnockbackResistPct += value; break; case AffixStat.FallDamageReductionPct: FallDamageReductionPct += value; break; case AffixStat.JumpHeightPct: JumpHeightPct += value; break; case AffixStat.SwimSpeedPct: SwimSpeedPct += value; break; case AffixStat.DodgeStaminaPct: DodgeStaminaPct += value; break; case AffixStat.BlockStaminaPct: BlockStaminaPct += value; break; case AffixStat.ParryBonusPct: ParryBonusPct += value; break; case AffixStat.BackstabPct: BackstabPct += value; break; case AffixStat.BossDamagePct: BossDamagePct += value; break; case AffixStat.AnimalDamagePct: AnimalDamagePct += value; break; case AffixStat.UndeadDamagePct: UndeadDamagePct += value; break; case AffixStat.PickaxeDamagePct: PickaxeDamagePct += value; break; case AffixStat.ChopDamagePct: ChopDamagePct += value; break; case AffixStat.BowDamagePct: BowDamagePct += value; break; case AffixStat.SpellDamagePct: SpellDamagePct += value; break; case AffixStat.ArmorPenPct: ArmorPenPct += value; break; case AffixStat.LowHealthDamagePct: LowHealthDamagePct += value; break; case AffixStat.FoodDurationPct: FoodDurationPct += value; break; case AffixStat.FoodRegenPct: FoodRegenPct += value; break; case AffixStat.RestedBonusPct: RestedBonusPct += value; break; case AffixStat.ExplosionResistPct: ExplosionResistPct += value; break; case AffixStat.CraftingBonusPct: CraftingBonusPct += value; break; case AffixStat.SneakDamagePct: SneakDamagePct += value; break; case AffixStat.ReflectDamagePct: ReflectDamagePct += value; break; case AffixStat.ProjectileDeflectPct: ProjectileDeflectPct += value; break; } } } public static class Affixes { public const int MaxSlots = 6; public const int MaxTier = 5; private static readonly Dictionary<string, AffixDef> ById = new Dictionary<string, AffixDef>(StringComparer.OrdinalIgnoreCase); public static readonly AffixDef[] All = BuildCatalog(); private static float[] TierWeights = new float[5] { 50f, 28f, 14f, 6f, 2f }; public static AffixDef Def(string id) { if (string.IsNullOrEmpty(id)) { return null; } if (!ById.TryGetValue(id, out var value)) { return null; } return value; } public static string Name(Affix affix) { AffixDef affixDef = Def(affix?.Id); if (affixDef == null) { return "?"; } if (!Settings.Russian.Value) { return affixDef.En; } return affixDef.Ru; } public static string Describe(Affix affix) { AffixDef affixDef = Def(affix?.Id); if (affixDef == null) { return ""; } bool value = Settings.Russian.Value; float num = affix.Value * ((Settings.AffixPower != null) ? Settings.AffixPower.Value : 1f); List<string> list = new List<string>(); for (int i = 0; i < affixDef.Stats.Length; i++) { StatWeight statWeight = affixDef.Stats[i]; if (statWeight.Stat != AffixStat.None) { list.Add(FormatStat(statWeight.Stat, num * statWeight.Weight, IsPercent(statWeight.Stat), value)); } } if (affixDef.HasProc) { string text = (value ? affixDef.HintRu : affixDef.HintEn); if (!string.IsNullOrEmpty(text)) { bool flag = text.IndexOf("{1", StringComparison.Ordinal) >= 0; try { text = string.Format(CultureInfo.InvariantCulture, text, num * affixDef.ProcWeight, affixDef.Chance(affix.Tier) * 100f); } catch (FormatException) { } list.Add(text); if (flag && affixDef.Cooldown > 0.3f) { list.Add(value ? ("КД " + affixDef.Cooldown.ToString("0.#", CultureInfo.InvariantCulture) + " с") : ("CD " + affixDef.Cooldown.ToString("0.#", CultureInfo.InvariantCulture) + " s")); } } } return string.Join(value ? " · " : " · ", list); } public static bool IsPercent(AffixStat stat) { switch (stat) { case AffixStat.DamagePct: case AffixStat.ArmorPct: case AffixStat.MoveSpeedPct: case AffixStat.BlockPct: case AffixStat.DurabilityPct: case AffixStat.StaminaRegenPct: case AffixStat.EitrRegenPct: case AffixStat.HealthRegenPct: case AffixStat.StealthPct: case AffixStat.DamageReductionPct: case AffixStat.PoisonResistPct: case AffixStat.FrostResistPct: case AffixStat.FireResistPct: case AffixStat.LightningResistPct: case AffixStat.StaggerResistPct: case AffixStat.KnockbackResistPct: case AffixStat.FallDamageReductionPct: case AffixStat.JumpHeightPct: case AffixStat.SwimSpeedPct: case AffixStat.DodgeStaminaPct: case AffixStat.BlockStaminaPct: case AffixStat.ParryBonusPct: case AffixStat.BackstabPct: case AffixStat.BossDamagePct: case AffixStat.AnimalDamagePct: case AffixStat.UndeadDamagePct: case AffixStat.PickaxeDamagePct: case AffixStat.ChopDamagePct: case AffixStat.BowDamagePct: case AffixStat.SpellDamagePct: case AffixStat.ArmorPenPct: case AffixStat.LowHealthDamagePct: case AffixStat.FoodDurationPct: case AffixStat.FoodRegenPct: case AffixStat.RestedBonusPct: case AffixStat.ExplosionResistPct: case AffixStat.CraftingBonusPct: case AffixStat.SneakDamagePct: case AffixStat.ReflectDamagePct: case AffixStat.ProjectileDeflectPct: return true; default: return false; } } public static string StatName(AffixStat stat, bool ru) { switch (stat) { case AffixStat.DamagePct: if (!ru) { return "damage"; } return "урона"; case AffixStat.ArmorPct: if (!ru) { return "armor"; } return "брони"; case AffixStat.Health: if (!ru) { return "health"; } return "здоровья"; case AffixStat.Stamina: if (!ru) { return "stamina"; } return "выносливости"; case AffixStat.Eitr: if (!ru) { return "eitr"; } return "эйтра"; case AffixStat.MoveSpeedPct: if (!ru) { return "movement speed"; } return "скорости"; case AffixStat.BlockPct: if (!ru) { return "block"; } return "блока"; case AffixStat.DurabilityPct: if (!ru) { return "durability"; } return "прочности"; case AffixStat.Fire: if (!ru) { return "fire"; } return "огня"; case AffixStat.Frost: if (!ru) { return "frost"; } return "мороза"; case AffixStat.Lightning: if (!ru) { return "lightning"; } return "молнии"; case AffixStat.Poison: if (!ru) { return "poison"; } return "яда"; case AffixStat.Spirit: if (!ru) { return "spirit"; } return "духа"; case AffixStat.Regen: if (!ru) { return "HP/s"; } return "HP/с"; case AffixStat.StaminaRegenPct: if (!ru) { return "stamina regen"; } return "регенерации выносливости"; case AffixStat.EitrRegenPct: if (!ru) { return "eitr regen"; } return "регенерации эйтра"; case AffixStat.HealthRegenPct: if (!ru) { return "health regen"; } return "регенерации здоровья"; case AffixStat.StealthPct: if (!ru) { return "stealth"; } return "скрытности"; case AffixStat.DamageReductionPct: if (!ru) { return "damage reduction"; } return "снижения урона"; case AffixStat.PoisonResistPct: if (!ru) { return "poison resistance"; } return "сопр. яду"; case AffixStat.FrostResistPct: if (!ru) { return "frost resistance"; } return "сопр. морозу"; case AffixStat.FireResistPct: if (!ru) { return "fire resistance"; } return "сопр. огню"; case AffixStat.LightningResistPct: if (!ru) { return "lightning resistance"; } return "сопр. молнии"; case AffixStat.StaggerResistPct: if (!ru) { return "stagger resistance"; } return "сопр. оглушению"; case AffixStat.KnockbackResistPct: if (!ru) { return "knockback resistance"; } return "сопр. отбрасыванию"; case AffixStat.FallDamageReductionPct: if (!ru) { return "fall protection"; } return "защиты от падения"; case AffixStat.JumpHeightPct: if (!ru) { return "jump height"; } return "высоты прыжка"; case AffixStat.SwimSpeedPct: if (!ru) { return "swim speed"; } return "скорости плавания"; case AffixStat.DodgeStaminaPct: if (!ru) { return "dodge stamina"; } return "экономии рывка"; case AffixStat.BlockStaminaPct: if (!ru) { return "block stamina"; } return "экономии блока"; case AffixStat.ParryBonusPct: if (!ru) { return "parry power"; } return "силы парирования"; case AffixStat.BackstabPct: if (!ru) { return "backstab damage"; } return "урона в спину"; case AffixStat.BossDamagePct: if (!ru) { return "boss damage"; } return "урона по боссам"; case AffixStat.AnimalDamagePct: if (!ru) { return "beast damage"; } return "урона по зверям"; case AffixStat.UndeadDamagePct: if (!ru) { return "undead damage"; } return "урона по нежити"; case AffixStat.PickaxeDamagePct: if (!ru) { return "pickaxe damage"; } return "урона киркой"; case AffixStat.ChopDamagePct: if (!ru) { return "chopping damage"; } return "урона рубке"; case AffixStat.BowDamagePct: if (!ru) { return "bow damage"; } return "урона из лука"; case AffixStat.SpellDamagePct: if (!ru) { return "spell damage"; } return "урона заклинаний"; case AffixStat.ArmorPenPct: if (!ru) { return "armor penetration"; } return "пробития брони"; case AffixStat.LowHealthDamagePct: if (!ru) { return "low-health damage"; } return "урона при низком HP"; case AffixStat.FoodDurationPct: if (!ru) { return "food duration"; } return "длительности еды"; case AffixStat.FoodRegenPct: if (!ru) { return "food value"; } return "пользы от еды"; case AffixStat.RestedBonusPct: if (!ru) { return "comfort"; } return "комфорта"; case AffixStat.ExplosionResistPct: if (!ru) { return "blast resistance"; } return "сопр. взрывам"; case AffixStat.CraftingBonusPct: if (!ru) { return "crafting quality"; } return "качества ремесла"; case AffixStat.SneakDamagePct: if (!ru) { return "sneak damage"; } return "урона из скрытности"; case AffixStat.ReflectDamagePct: if (!ru) { return "damage reflect"; } return "отражения урона"; case AffixStat.ProjectileDeflectPct: if (!ru) { return "projectile deflect"; } return "отражения снарядов"; default: return ""; } } public static string FormatStat(AffixStat stat, float value, bool percent, bool ru) { string text = (percent ? (value.ToString("0.#", CultureInfo.InvariantCulture) + "%") : value.ToString("0.#", CultureInfo.InvariantCulture)); string text2 = StatName(stat, ru); return "+" + text + ((text2.Length > 0) ? (" " + text2) : ""); } public static Affix Roll(int tier, AffixPool pool) { tier = Math.Max(1, Math.Min(5, tier)); AffixDef[] array = ForPool(pool); AffixDef affixDef = array[Random.Range(0, array.Length)]; float value = ((affixDef.Stats.Length != 0) ? RollStatValue(affixDef, tier) : RollProcValue(tier)); return new Affix(affixDef.Id, tier, value); } public static float RollValue(AffixDef def, int tier) { tier = Math.Max(1, Math.Min(5, tier)); if (def == null) { return 0f; } if (def.Stats.Length == 0) { return RollProcValue(tier); } return RollStatValue(def, tier); } private static float RollStatValue(AffixDef def, int tier) { return 4f * (float)tier + Random.Range(0f, 2f * (float)tier); } private static float RollProcValue(int tier) { return 6f * (float)tier + Random.Range(0f, 3f * (float)tier); } public static Affix RollForLevel(int level, AffixPool pool, int minTier = 1) { int num = TierForLevel(Math.Max(1, level)); int num2 = Math.Max(1, Math.Min(num, minTier)); if (num2 >= num) { return Roll(num, pool); } float num3 = 0f; for (int i = num2 - 1; i < num; i++) { num3 += TierWeights[i]; } float num4 = Random.Range(0f, num3); for (int j = num2 - 1; j < num; j++) { num4 -= TierWeights[j]; if (num4 <= 0f) { return Roll(j + 1, pool); } } return Roll(num, pool); } public static Affix RollForLevel(int level, AffixPool pool, IList<Affix> existing, int minTier = 1) { Affix affix = RollForLevel(level, pool, minTier); if (existing == null || existing.Count == 0) { return affix; } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < existing.Count; i++) { if (existing[i] != null) { hashSet.Add(existing[i].Id); } } if (!hashSet.Contains(affix.Id)) { return affix; } AffixDef[] array = ForPool(pool); List<AffixDef> list = new List<AffixDef>(); for (int j = 0; j < array.Length; j++) { if (!hashSet.Contains(array[j].Id)) { list.Add(array[j]); } } if (list.Count == 0) { return affix; } AffixDef affixDef = list[Random.Range(0, list.Count)]; float value = ((affixDef.Stats.Length != 0) ? RollStatValue(affixDef, affix.Tier) : RollProcValue(affix.Tier)); return new Affix(affixDef.Id, affix.Tier, value); } public static AffixPool PoolFor(ItemData item) { return ItemCatalog.PoolFor(item); } public static AffixDef[] ForPool(AffixPool pool) { List<AffixDef> list = new List<AffixDef>(); for (int i = 0; i < All.Length; i++) { if ((All[i].Pool & pool) != AffixPool.None) { list.Add(All[i]); } } if (list.Count <= 0) { return All; } return list.ToArray(); } public static string PoolName(AffixPool pool) { bool value = Settings.Russian.Value; switch (pool) { case AffixPool.Armor: if (!value) { return "ARMOR"; } return "БРОНЯ"; case AffixPool.Shield: if (!value) { return "SHIELD"; } return "ЩИТ"; default: if (!value) { return "WEAPON"; } return "ОРУЖИЕ"; } } public static int TierForLevel(int level) { if (level >= 17) { return 5; } if (level >= 13) { return 4; } if (level >= 9) { return 3; } if (level >= 5) { return 2; } return 1; } public static int SlotsForLevel(int level) { if (level >= 18) { return 6; } if (level >= 15) { return 5; } if (level >= 12) { return 4; } if (level >= 9) { return 3; } if (level >= 6) { return 2; } if (level >= 3) { return 1; } return 0; } public static string Serialize(Affix[] slots) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 6; i++) { if (i > 0) { stringBuilder.Append(';'); } Affix affix = ((slots != null && i < slots.Length) ? slots[i] : null); if (affix != null) { stringBuilder.Append(affix); } } return stringBuilder.ToString(); } public static Affix[] Deserialize(string raw) { Affix[] array = new Affix[6]; if (string.IsNullOrEmpty(raw)) { return array; } string[] array2 = raw.Split(';'); for (int i = 0; i < 6 && i < array2.Length; i++) { string text = array2[i]; if (!string.IsNullOrEmpty(text)) { string[] array3 = text.Split(':'); if (array3.Length >= 3 && int.TryParse(array3[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && float.TryParse(array3[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && Def(array3[0]) != null) { array[i] = new Affix(array3[0], result, result2); } } } return array; } public static void AddBonuses(IEnumerable<Affix> affixes, ref Bonuses bonuses) { if (affixes == null) { return; } float num = ((Settings.AffixPower != null) ? Settings.AffixPower.Value : 1f); foreach (Affix affix in affixes) { AffixDef affixDef = Def(affix?.Id); if (affixDef != null) { float num2 = affix.Value * num; for (int i = 0; i < affixDef.Stats.Length; i++) { StatWeight statWeight = affixDef.Stats[i]; bonuses.AddStat(statWeight.Stat, num2 * statWeight.Weight); } } } } private static StatWeight[] S(params StatWeight[] weights) { return weights; } private static StatWeight W(AffixStat stat, float weight = 1f) { return new StatWeight(stat, weight); } private static AffixDef Stat(string id, AffixPool pool, string ru, string en, params StatWeight[] stats) { return new AffixDef(id, pool, S(stats), AffixProc.None, 0f, 0f, 0f, procPercent: false, ru, en); } private static AffixDef Proc(string id, AffixPool pool, AffixProc proc, float chancePerTier, float cooldown, float weight, bool percent, string ru, string en, string hintRu, string hintEn) { return new AffixDef(id, pool, S(), proc, chancePerTier, cooldown, weight, percent, ru, en, hintRu, hintEn); } private static AffixDef Mix(string id, AffixPool pool, AffixProc proc, float chancePerTier, float cooldown, float weight, bool percent, string ru, string en, string hintRu, string hintEn, params StatWeight[] stats) { return new AffixDef(id, pool, S(stats), proc, chancePerTier, cooldown, weight, percent, ru, en, hintRu, hintEn); } private static AffixDef[] BuildCatalog() { List<AffixDef> list = new List<AffixDef>(); list.Add(Stat("hunters_mark", AffixPool.Weapon, "Метка охотника", "Hunter's Mark", W(AffixStat.AnimalDamagePct))); list.Add(Stat("miners_joy", AffixPool.Weapon, "Радость шахтёра", "Miner's Joy", W(AffixStat.PickaxeDamagePct, 0.8f), W(AffixStat.Stamina, 0.25f))); list.Add(Stat("lumberjacks_edge", AffixPool.Weapon, "Грань лесоруба", "Lumberjack's Edge", W(AffixStat.ChopDamagePct, 0.8f), W(AffixStat.Stamina, 0.25f))); list.Add(Mix("precise_draw", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Точный натяг", "Precise Draw", null, null, W(AffixStat.BowDamagePct, 0.8f), W(AffixStat.StaminaRegenPct, 0.2f))); list.Add(Proc("heavy_stagger", AffixPool.Weapon, AffixProc.Stagger, 0.05f, 7f, 1f, percent: false, "Тяжёлый удар", "Heavy Stagger", "шанс {1:0.#}%: оглушает цель", "chance {1:0.#}%: staggers the target")); list.Add(Proc("bloodlust", AffixPool.Weapon, AffixProc.Lifesteal, 1f, 0f, 0.4f, percent: true, "Жажда крови", "Bloodlust", "возвращает {0:0.#}% урона здоровьем", "returns {0:0.#}% damage as health")); list.Add(Mix("piercing_strike", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Пронзающий удар", "Piercing Strike", null, null, W(AffixStat.ArmorPenPct, 0.7f), W(AffixStat.DamagePct, 0.2f))); list.Add(Mix("hallowed_spirit", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Священный дух", "Hallowed Spirit", null, null, W(AffixStat.UndeadDamagePct, 0.7f), W(AffixStat.Spirit, 0.5f))); list.Add(Proc("frostbite_strike", AffixPool.Weapon, AffixProc.Frostbite, 0.35f, 0f, 0.7f, percent: false, "Ледяной укус", "Frostbite Strike", "наносит +{0:0.#} мороза и замедляет", "deals +{0:0.#} frost and slows")); list.Add(Proc("hellfire_burn", AffixPool.Weapon, AffixProc.Ignite, 0.35f, 0f, 0.7f, percent: false, "Адское пламя", "Hellfire Burn", "наносит +{0:0.#} огня и поджигает", "deals +{0:0.#} fire and ignites")); list.Add(Proc("thors_voice", AffixPool.Weapon, AffixProc.ChainLightning, 0.2f, 5f, 0.8f, percent: false, "Голос Тора", "Thor's Voice", "шанс {1:0.#}%: цепная молния на {0:0.#} урона", "chance {1:0.#}%: chain lightning for {0:0.#}")); list.Add(Mix("channeler_eitr", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Проводник эйтра", "Channeler", null, null, W(AffixStat.EitrRegenPct, 0.7f), W(AffixStat.SpellDamagePct, 0.5f))); list.Add(Proc("serrated_edge", AffixPool.Weapon, AffixProc.Bleed, 0.3f, 0f, 0.5f, percent: false, "Зазубренное лезвие", "Serrated Edge", "шанс {1:0.#}%: кровотечение на {0:0.#} урона", "chance {1:0.#}%: bleed for {0:0.#}")); list.Add(Mix("duelist_focus", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Стойка дуэлянта", "Duelist Focus", null, null, W(AffixStat.DamagePct, 0.5f), W(AffixStat.ParryBonusPct, 0.5f))); list.Add(Proc("pushback_wave", AffixPool.Weapon, AffixProc.Knockback, 0.2f, 8f, 1f, percent: false, "Волна отбрасывания", "Pushback Wave", "шанс {1:0.#}%: отбрасывает врагов", "chance {1:0.#}%: knocks enemies back")); list.Add(Mix("gale_force", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Сила ветра", "Gale Force", null, null, W(AffixStat.StaminaRegenPct, 0.7f), W(AffixStat.MoveSpeedPct, 0.15f))); list.Add(Proc("rooting_strike", AffixPool.Weapon, AffixProc.Root, 0.15f, 9f, 1f, percent: false, "Сковывающий удар", "Rooting Strike", "шанс {1:0.#}%: обездвиживает цель", "chance {1:0.#}%: roots the target")); list.Add(Stat("backstabber_pro", AffixPool.Weapon, "Мастер спины", "Backstabber", W(AffixStat.BackstabPct), W(AffixStat.SneakDamagePct, 0.4f))); list.Add(Stat("boss_bane", AffixPool.Weapon, "Погибель боссов", "Boss Bane", W(AffixStat.BossDamagePct))); list.Add(Proc("shattering_blow", AffixPool.Weapon, AffixProc.ArmorBreak, 0.2f, 10f, 1f, percent: true, "Дробящий удар", "Shattering Blow", "шанс {1:0.#}%: снижает броню на {0:0.#}%", "chance {1:0.#}%: reduces armor by {0:0.#}%")); list.Add(Proc("phantom_strike", AffixPool.Weapon, AffixProc.ExtraHit, 0.12f, 4f, 0.6f, percent: true, "Призрачный удар", "Phantom Strike", "шанс {1:0.#}%: повторный удар на {0:0.#}% урона", "chance {1:0.#}%: extra hit for {0:0.#}% damage")); list.Add(Proc("earth_shaker", AffixPool.Weapon, AffixProc.EarthSlam, 0.1f, 12f, 0.8f, percent: false, "Землетряс", "Earth Shaker", "шанс {1:0.#}%: удар по земле на {0:0.#} урона", "chance {1:0.#}%: ground slam for {0:0.#}")); list.Add(Proc("harpooner_static", AffixPool.Weapon, AffixProc.PullLightning, 0.18f, 7f, 0.7f, percent: false, "Гарпун-разряд", "Harpooner Static", "шанс {1:0.#}%: притягивает и бьёт молнией на {0:0.#}", "chance {1:0.#}%: pulls and zaps for {0:0.#}")); list.Add(Mix("slinger_speed", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Скорость пращника", "Slinger Speed", null, null, W(AffixStat.StaminaRegenPct, 0.6f), W(AffixStat.MoveSpeedPct, 0.2f))); list.Add(Mix("winded_strike", AffixPool.Weapon, AffixProc.None, 0f, 0f, 0f, false, "Дыхание в ударе", "Winded Strike", null, null, W(AffixStat.Stamina, 0.5f), W(AffixStat.StaminaRegenPct, 0.5f))); list.Add(Proc("soul_reaper", AffixPool.Weapon, AffixProc.SoulDrain, 0.15f, 4f, 0.6f, percent: false, "Жнец душ", "Soul Reaper", "шанс {1:0.#}%: добивает слабую цель и лечит на {0:0.#}", "chance {1:0.#}%: executes and heals {0:0.#}")); list.Add(Proc("vermin_host", AffixPool.Weapon, AffixProc.SummonVermin, 0.08f, 18f, 1f, percent: false, "Хозяин паразитов", "Vermin Host", "шанс {1:0.#}%: призывает паразитов", "chance {1:0.#}%: summons vermin")); list.Add(Proc("echoing_strike", AffixPool.Weapon, AffixProc.Echo, 0.12f, 6f, 0.5f, percent: true, "Эхо удара", "Echoing Strike", "шанс {1:0.#}%: эхо на {0:0.#}% урона", "chance {1:0.#}%: echo for {0:0.#}% damage")); list.Add(Mix("chrono_parry", AffixPool.Weapon, AffixProc.ParrySlow, 0.25f, 8f, 0.4f, true, "Хроно-парирование", "Chrono Parry", "идеальный блок замедляет врагов на {0:0.#}%", "perfect block slows enemies by {0:0.#}%", W(AffixStat.ParryBonusPct, 0.6f))); list.Add(Proc("phase_shift", AffixPool.Weapon, AffixProc.PhaseShift, 0.08f, 12f, 1f, percent: false, "Фазовый сдвиг", "Phase Shift", "шанс {1:0.#}%: уклонение от удара", "chance {1:0.#}%: dodge the hit")); list.Add(Proc("catalyst_wet", AffixPool.Weapon, AffixProc.Combust, 0.2f, 6f, 0.8f, percent: false, "Мокрый катализатор", "Catalyst Wet", "шанс {1:0.#}%: взрыв пара на {0:0.#} урона", "chance {1:0.#}%: steam burst for {0:0.#}")); list.Add(Proc("executioner_stagger", AffixPool.Weapon, AffixProc.Execute, 0.5f, 2f, 0.5f, percent: false, "Казнь", "Executioner", "добивает цель ниже {0:0.#}% HP и оглушает", "executes below {0:0.#}% HP and staggers")); list.Add(Proc("triumphant_strike", AffixPool.Weapon, AffixProc.OnKillBuff, 0.15f, 10f, 0.5f, percent: true, "Триумф", "Triumphant Strike", "шанс {1:0.#}%: +{0:0.#}% урона на 10 с", "chance {1:0.#}%: +{0:0.#}% damage for 10 s")); list.Add(Proc("chain_combustion", AffixPool.Weapon, AffixProc.Combust, 0.15f, 7f, 1f, percent: false, "Цепное возгорание", "Chain Combustion", "шанс {1:0.#}%: огненный взрыв на {0:0.#}", "chance {1:0.#}%: fire burst for {0:0.#}")); list.Add(Proc("seismic_slam", AffixPool.Weapon, AffixProc.EarthSlam, 0.08f, 15f, 1.2f, percent: false, "Сейсмический удар", "Seismic Slam", "шанс {1:0.#}%: сейсмический удар на {0:0.#}", "chance {1:0.#}%: seismic slam for {0:0.#}")); list.Add(Proc("decay_strike", AffixPool.Weapon, AffixProc.Decay, 0.3f, 0f, 0.6f, percent: false, "Удар тлена", "Decay Strike", "шанс {1:0.#}%: яд на {0:0.#} урона", "chance {1:0.#}%: poison for {0:0.#}")); list.Add(Proc("weakness_found", AffixPool.Weapon, AffixProc.Weakness, 0.25f, 6f, 0.3f, percent: true, "Найденная слабость", "Weakness Found", "шанс {1:0.#}%: +{0:0.#}% урона по оглушённым", "chance {1:0.#}%: +{0:0.#}% vs staggered")); list.Add(Proc("riposte_dodge", AffixPool.Weapon, AffixProc.Riposte, 0.2f, 8f, 0.6f, percent: true, "Ответный рывок", "Riposte", "шанс {1:0.#}%: контратака после уклонения на {0:0.#}%", "chance {1:0.#}%: counter after dodge for {0:0.#}%")); list.Add(Mix("giant_slayer", AffixPool.Weapon, AffixProc.GiantSlayer, 0.1f, 8f, 0.8f, true, "Убийца великанов", "Giant Slayer", "шанс {1:0.#}%: +{0:0.#}% урона по крупным врагам", "chance {1:0.#}%: +{0:0.#}% vs large foes", W(AffixStat.DamagePct, 0.3f))); list.Add(Stat("lightfoot", AffixPool.Armor | AffixPool.Shield, "Лёгкая поступь", "Lightfoot", W(AffixStat.MoveSpeedPct, 0.6f), W(AffixStat.FallDamageReductionPct, 0.4f))); list.Add(Stat("refreshing_stamina", AffixPool.Armor | AffixPool.Shield, "Бодрость", "Refreshing Stamina", W(AffixStat.StaminaRegenPct))); list.Add(Mix("shaded_stealth", AffixPool.Armor, AffixProc.None, 0f, 0f, 0f, false, "Тень", "Shaded", null, null, W(AffixStat.StealthPct, 0.8f), W(AffixStat.SneakDamagePct, 0.2f))); list.Add(Stat("thick_skinned", AffixPool.Armor, "Толстая шкура", "Thick Skinned", W(AffixStat.ArmorPct, 0.6f), W(AffixStat.DamageReductionPct, 0.4f))); list.Add(Stat("antivenom_proof", AffixPool.Armor | AffixPool.Shield, "Противоядие", "Antivenom Proof", W(AffixStat.PoisonResistPct))); list.Add(Proc("leech_touch", AffixPool.Armor | AffixPool.Shield, AffixProc.Lifesteal, 1f, 0f, 0.3f, percent: true, "Прикосновение пиявки", "Leech Touch", "возвращает {0:0.#}% урона здоровьем", "returns {0:0.#}% damage as health")); list.Add(Mix("ymirs_warmth", AffixPool.Armor, AffixProc.None, 0f, 0f, 0f, false, "Тепло Имира", "Ymir's Warmth", null, null, W(AffixStat.FrostResistPct, 0.8f), W(AffixStat.HealthRegenPct, 0.2f))); list.Add(Stat("mountaineer_climb", AffixPool.Armor, "Скалолаз", "Mountaineer", W(AffixStat.JumpHeightPct, 0.7f), W(AffixStat.FallDamageReductionPct, 0.5f))); list.Add(Stat("odins_fury", AffixPool.Armor | AffixPool.Shield, "Ярость Одина", "Odin's Fury", W(AffixStat.LowHealthDamagePct))); list.Add(Stat("windrider_mobility", AffixPool.Armor, "Ветрокрылый", "Windrider", W(AffixStat.MoveSpeedPct, 0.8f), W(AffixStat.StaminaRegenPct, 0.2f))); list.Add(Mix("yggdrasils_core", AffixPool.Armor, AffixProc.None, 0f, 0f, 0f, false, "Сердце Иггдрасиля", "Yggdrasil's Core", null, null, W(AffixStat.Health, 0.7f), W(AffixStat.HealthRegenPct, 0.3f))); list.Add(Mix("deceiver_mist", AffixPool.Armor, AffixProc.None, 0f, 0f, 0f, false, "Обман тумана", "Deceiver Mist", null, null, W(AffixStat.StealthPct, 0.7f), W(AffixStat.MoveSpeedPct, 0.2f))); list.Add(Mix("magma_core", AffixPool.Armor | AffixPool.Shield, AffixProc.MagmaAura, 0.5f, 2f, 0.5f, false, "Магмовое ядро", "Magma Core", "жжёт врагов рядом на {0:0.#} урона", "burns nearby enemies for {0:0.#}", W(AffixStat.FireResistPct, 0.6f))); list.Add(Proc("phoenix_rise", AffixPool.Armor | AffixPool.Shield, AffixProc.Phoenix, 0.5f, 90f, 2f, percent: true, "Восход Феникса", "Phoenix Rise", "при падении ниже 25% HP восстанавливает {0:0.#}% здоровья", "below 25% HP restores {0:0.#}% health")); list.Add(Mix("absolute_zero_aura", AffixPool.Armor | AffixPool.Shield, AffixProc.FrostAura, 0.5f, 2f, 0.5f, false, "Аура абсолютного нуля", "Absolute Zero Aura", "замораживает врагов рядом на {0:0.#} урона", "freezes nearby enemies for {0:0.#}", W(AffixStat.FrostResistPct, 0.4f))); list.Add(Stat("heavyweight_bulk", AffixPool.Armor, "Тяжёлая масса", "Heavyweight", W(AffixStat.ArmorPct, 0.7f), W(AffixStat.KnockbackResistPct, 0.4f))); list.Add(Stat("shield_wall_block", AffixPool.Shield, "Стена щитов", "Shield Wall", W(AffixStat.BlockPct), W(AffixStat.BlockStaminaPct, 0.2f))); list.Add(Mix("spike_armor_return", AffixPool.Armor | AffixPool.Shield, AffixProc.ThornReflect, 0.5f, 0f, 0.6f, true, "Шипастая броня", "Spike Armor", "возвращает {0:0.#}% полученного урона", "returns {0:0.#}% damage taken", W(AffixStat.ArmorPct, 0.3f))); list.Add(Mix("kinetic_charge_shield", AffixPool.Shield, AffixProc.BlockCharge, 0.4f, 8f, 0.6f, true, "Кинетический заряд", "Kinetic Charge", "блок заряжает удар на {0:0.#}% урона", "block charges a strike for {0:0.#}% damage", W(AffixStat.BlockPct, 0.3f))); list.Add(Mix("last_stand_shield", AffixPool.Shield, AffixProc.LastStand, 0.5f, 45f, 1.2f, true, "Последний рубеж", "Last Stand", "при HP ниже 30% даёт {0:0.#}% брони на 10 с", "below 30% HP grants {0:0.#}% armor for 10 s", W(AffixStat.BlockPct, 0.3f), W(AffixStat.ArmorPct, 0.3f))); list.Add(Proc("einherjars_will_buff", AffixPool.Armor | AffixPool.Shield, AffixProc.Vengeance, 0.3f, 8f, 0.5f, percent: true, "Воля эйнхерия", "Einherjar's Will", "блок даёт +{0:0.#}% урона на 8 с", "block grants +{0:0.#}% damage for 8 s")); list.Add(Stat("stonewall_resistance", AffixPool.Armor | AffixPool.Shield, "Каменная стена", "Stonewall", W(AffixStat.StaggerResistPct, 0.8f), W(AffixStat.KnockbackResistPct, 0.2f))); list.Add(Mix("impact_absorb_buff", AffixPool.Armor | AffixPool.Shield, AffixProc.AbsorbImpact, 0.25f, 6f, 0.5f, true, "Поглощение удара", "Impact Absorb", "шанс {1:0.#}%: поглощает удар и даёт {0:0.#}% брони", "chance {1:0.#}%: absorbs a hit and grants {0:0.#}% armor", W(AffixStat.DamageReductionPct, 0.4f))); list.Add(Mix("second_wind_stamina", AffixPool.Armor, AffixProc.SecondWind, 0.35f, 20f, 1f, true, "Второе дыхание", "Second Wind", "при нехватке выносливости возвращает {0:0.#}% запаса", "when exhausted restores {0:0.#}% stamina", W(AffixStat.StaminaRegenPct, 0.4f))); list.Add(Stat("acrobatic_dodge", AffixPool.Armor, "Акробатика", "Acrobatic Dodge", W(AffixStat.DodgeStaminaPct), W(AffixStat.MoveSpeedPct, 0.1f))); list.Add(Stat("wave_runner_swim", AffixPool.Armor, "Бегун волн", "Wave Runner", W(AffixStat.SwimSpeedPct, 0.8f), W(AffixStat.StaminaRegenPct, 0.2f))); list.Add(Mix("spook_invisibility", AffixPool.Armor, AffixProc.Invisibility, 0.5f, 2f, 1f, false, "Призрачная тень", "Spook", "в приседе маскирует вас от врагов", "crouching hides you from enemies", W(AffixStat.StealthPct, 0.6f))); list.Add(Mix("pathfinder_map", AffixPool.Armor, AffixProc.None, 0f, 0f, 0f, false, "Путеводная карта", "Pathfinder", null, null, W(AffixStat.MoveSpeedPct, 0.4f), W(AffixStat.StealthPct, 0.3f))); list.Add(Stat("artisan_bench", AffixPool.Armor, "Верстак мастера", "Artisan Bench", W(AffixStat.DurabilityPct, 0.7f), W(AffixStat.RestedBonusPct, 0.3f))); list.Add(Mix("deep_breath_water", AffixPool.Armor, AffixProc.WaterBreath, 0.5f, 2f, 1f, false, "Глубокий вдох", "Deep Breath", "позволяет дышать под водой", "lets you breathe underwater", W(AffixStat.SwimSpeedPct, 0.3f))); list.Add(Stat("well_fed_buff", AffixPool.Armor, "Сытость", "Well Fed", W(AffixStat.FoodDurationPct, 0.8f), W(AffixStat.Health, 0.2f))); list.Add(Mix("metabolism_boost", AffixPool.Armor, AffixProc.None, 0f, 0f, 0f, false, "Ускоренный обмен", "Metabolism", null, null, W(AffixStat.FoodRegenPct, 0.5f), W(AffixStat.HealthRegenPct, 0.4f))); list.Add(Mix("waterproof_rain", AffixPool.Armor, AffixProc.RainProof, 0.5f, 2f, 1f, false, "Дождевик", "Waterproof", "дождь и сырость не мешают", "rain and wet do not hinder you", W(AffixStat.PoisonResistPct, 0.2f))); list.Add(Mix("night_stalker_buff", AffixPool.Armor, AffixProc.NightBuff, 0.5f, 2f, 0.35f, true, "Ночной охотник", "Night Stalker", "ночью +{0:0.#}% урона и скрытности", "at night +{0:0.#}% damage and stealth", W(AffixStat.StealthPct, 0.3f))); list.Add(Mix("sun_blessed_buff", AffixPool.Armor, AffixProc.DayBuff, 0.5f, 2f, 0.35f, true, "Благословение солнца", "Sun Blessed", "днём +{0:0.#}% здоровья и регенерации", "by day +{0:0.#}% health and regen", W(AffixStat.HealthRegenPct, 0.2f))); list.Add(Stat("storm_shield_buff", AffixPool.Armor | AffixPool.Shield, "Штормовой щит", "Storm Shield", W(AffixStat.LightningResistPct))); list.Add(Stat("homebound_rest", AffixPool.Armor, "Домашний уют", "Homebound Rest", W(AffixStat.RestedBonusPct, 0.8f), W(AffixStat.StaminaRegenPct, 0.2f))); list.Add(Mix("mana_shield_eitr", AffixPool.Armor | AffixPool.Shield, AffixProc.EitrShield, 0.5f, 0f, 0.8f, true, "Эйтровый щит", "Mana Shield", "{0:0.#}% урона оплачивается эйтром", "{0:0.#}% of damage is paid with eitr", W(AffixStat.Eitr, 0.4f))); list.Add(Mix("warp_field_deflect", AffixPool.Armor | AffixPool.Shield, AffixProc.Deflect, 0.15f, 0f, 1f, false, "Поле искажения", "Warp Field", "шанс {1:0.#}%: отражает снаряд", "chance {1:0.#}%: deflects a projectile", W(AffixStat.DamageReductionPct, 0.2f))); list.Add(Mix("eitr_trail_sprint", AffixPool.Armor, AffixProc.SprintTrail, 0.5f, 3f, 0.5f, true, "Эйтровый след", "Eitr Trail", "бег оставляет эйтровый след ({0:0.#} урона)", "sprinting leaves an eitr trail ({0:0.#} damage)", W(AffixStat.StaminaRegenPct, 0.3f))); list.Add(Stat("blast_proof_self", AffixPool.Armor | AffixPool.Shield, "Взрывостойкость", "Blast Proof", W(AffixStat.ExplosionResistPct, 0.8f), W(AffixStat.DamageReductionPct, 0.2f))); List<AffixDef> list2 = list; ById.Clear(); for (int i = 0; i < list2.Count; i++) { ById[list2[i].Id] = list2[i]; } return list2.ToArray(); } } public enum EnchantOutcome { Success, SafeFail, Downgrade, Destroyed, NoItem, NoScroll, MaxLevel, TierTooLow } public static class EnchantService { public const int RerollCost = 25; public const int LockCost = 40; public const int TransmuteCount = 3; private static readonly MethodInfo InventoryChanged = AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); private static readonly float[] Chances = new float[21] { 0f, 100f, 95f, 90f, 82f, 76f, 70f, 62f, 55f, 48f, 42f, 36f, 31f, 26f, 22f, 18f, 15f, 12f, 9f, 7f, 5f }; public static float SuccessChance(int targetLevel, int failStreak) { if (targetLevel <= 1) { return 100f; } int num = Mathf.Clamp(targetLevel, 1, Chances.Length - 1); float num2 = Chances[num]; if (Settings.PityEnabled.Value) { num2 += (float)failStreak * Settings.PityStep.Value; } return Mathf.Clamp(num2, 1f, 99f); } public static string FormatChance(float chance) { return chance.ToString("0.#", CultureInfo.InvariantCulture) + "%"; } public static void Refresh(Inventory inventory) { try { InventoryChanged?.Invoke(inventory, new object[2] { false, false }); } catch (Exception ex) { Plugin.Log.LogDebug((object)("Inventory refresh: " + ex.Message)); } } public static List<ItemData> FindScrolls(Inventory inventory) { List<ItemData> list = new List<ItemData>(); foreach (ItemData allItem in inventory.GetAllItems()) { if (Scrolls.TryRead(allItem, out var _)) { list.Add(allItem); } } return list; } public static bool FindBestScroll(Inventory inventory, ItemData target, ScrollTier? preferred, bool blessed, out ScrollInfo found) { found = default(ScrollInfo); if (target == null) { return false; } if (!ItemTiers.TryGet(target, out var tier)) { return false; } if (preferred.HasValue && preferred.Value != tier) { return false; } foreach (ItemData allItem in inventory.GetAllItems()) { if (Scrolls.TryRead(allItem, out var info) && info.Tier == tier && info.Blessed == blessed) { found = info; return true; } } return false; } public static int CountAvailableScrolls(Inventory inventory, ItemData target, bool blessed) { if (target == null) { return 0; } if (!ItemTiers.TryGet(target, out var tier)) { return 0; } int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (Scrolls.TryRead(allItem, out var info) && info.Tier == tier && info.Blessed == blessed) { num += allItem.m_stack; } } return num; } public static EnchantOutcome TryEnchant(Player player, ItemData item, ScrollTier? preferred, bool blessed, out string message) { message = ""; if ((Object)(object)player == (Object)null || item == null) { return EnchantOutcome.NoItem; } if (!SeidrData.IsEligible(item)) { message = T("Этот предмет нельзя зачаровать.", "This item cannot be enchanted."); return EnchantOutcome.NoItem; } if (!ItemTiers.TryGet(item, out var tier)) { message = T("Предмет не привязан к тиру. Добавьте его в ItemTiers.txt.", "This item has no tier. Add it to ItemTiers.txt."); return EnchantOutcome.NoItem; } Inventory inventory = ((Humanoid)player).GetInventory(); SeidrData seidrData = SeidrData.Load(item); int num = Math.Min(Settings.MaxLevel.Value, Scrolls.Cap(tier)); if (seidrData.Level >= num) { message = T("Предмет уже достиг максимума.", "The item is already at maximum level."); return EnchantOutcome.MaxLevel; } if (!FindBestScroll(inventory, item, preferred, blessed, out var found)) { string text = (blessed ? T("благословлённый свиток", "blessed scroll") : T("свиток", "scroll")); message = T("Нужен " + text + " " + Scrolls.Letter(tier) + " (" + Scrolls.BiomeName(tier) + ").", "A " + text + " " + Scrolls.Letter(tier) + " (" + Scrolls.BiomeName(tier) + ") is required."); return EnchantOutcome.NoScroll; } int num2 = seidrData.Level + 1; if (found.Tier != tier) { message = T("Свиток " + Scrolls.Letter(found.Tier) + " не подходит предмету тира " + Scrolls.Letter(tier) + ".", "A " + Scrolls.Letter(found.Tier) + " scroll does not fit a tier " + Scrolls.Letter(tier) + " item."); return EnchantOutcome.TierTooLow; } if (!ItemFactory.TakeScroll(inventory, found)) { message = T("Не удалось взять свиток.", "Could not take the scroll."); return EnchantOutcome.NoScroll; } float num3 = (blessed ? 100f : SuccessChance(num2, seidrData.FailStreak)); bool num4 = blessed || Random.Range(0f, 100f) < num3; string text2 = Scrolls.Color(found.Tier); if (num4) { seidrData.Level = num2; seidrData.FailStreak = 0; bool num5 = seidrData.AddAffixIfNeeded(item); if (!num5 && Random.Range(0f, 100f) < 40f) { seidrData.TryUpgradeRandomAffix(); } seidrData.Save(item); Refresh(inventory); string text3 = (num5 ? T("\nОткрыт новый аффикс.", "\nA new affix unlocked.") : ""); string text4 = (blessed ? T("Благословение!", "Blessing!") : T("Успех!", "Success!")); message = T($"<color={text2}>{text4}</color> {UiKit.Loc(item.m_shared.m_name)} теперь +{seidrData.Level}.{text3}", $"<color={text2}>{text4}</color> {UiKit.Loc(item.m_shared.m_name)} is now +{seidrData.Level}.{text3}"); return EnchantOutcome.Success; } if (num2 <= Settings.SafetyLevel.Value) { seidrData.Save(item); Refresh(inventory); message = T("<color=#B9C0C7>Безопасный уровень.</color> Свиток сгорел, предмет цел.", "<color=#B9C0C7>Safe level.</color> The scroll is gone, the item is intact."); return EnchantOutcome.SafeFail; } if (Settings.DestroyOnFailure.Value) { inventory.RemoveItem(item); Refresh(inventory); message = T("<color=#FF6B5A>Провал!</color> " + UiKit.Loc(item.m_shared.m_name) + " уничтожен.", "<color=#FF6B5A>Failure!</color> " + UiKit.Loc(item.m_shared.m_name) + " was destroyed."); return EnchantOutcome.Destroyed; } seidrData.Level = Math.Max(0, seidrData.Level - 1); seidrData.FailStreak++; seidrData.Save(item); Refresh(inventory); message = T($"<color=#FF8A6B>Провал.</color> Уровень упал до +{seidrData.Level}. Шанс следующей попытки немного выше.", $"<color=#FF8A6B>Failure.</color> Level dropped to +{seidrData.Level}. The next attempt is a bit more likely."); return EnchantOutcome.Downgrade; } public static bool TryReroll(Player player, ItemData item, int slot, bool all, out string message) { message = ""; if ((Object)(object)player == (Object)null || item == null) { return false; } SeidrData seidrData = SeidrData.Load(item); List<int> list = new List<int>(); if (all) { for (int i = 0; i < seidrData.Slots.Length; i++) { if (seidrData.Slots[i] != null && !seidrData.Locks[i]) { list.Add(i); } } } else if (slot >= 0 && slot < seidrData.Slots.Length && seidrData.Slots[slot] != null && !seidrData.Locks[slot]) { list.Add(slot); } if (list.Count == 0) { message = T("Нет аффиксов для перековки (или все закрыты).", "No affixes to reroll (or all locked)."); return false; } int num = 25 * list.Count; if (ItemFactory.CountDust(((Humanoid)player).GetInventory()) < num) { message = T($"Нужно {num} рунной пыли.", $"{num} runic dust required."); return false; } ItemFactory.RemoveDust(((Humanoid)player).GetInventory(), num); AffixPool pool = Affixes.PoolFor(item); int level = Math.Max(1, seidrData.Level); List<Affix> list2 = new List<Affix>(); for (int j = 0; j < seidrData.Slots.Length; j++) { if (seidrData.Slots[j] != null && !list.Contains(j)) { list2.Add(seidrData.Slots[j]); } } foreach (int item2 in list) { Affix affix = Affixes.RollForLevel(level, pool, list2); seidrData.Slots[item2] = affix; list2.Add(affix); } seidrData.Save(item); Refresh(((Humanoid)player).GetInventory()); message = T($"Перековано аффиксов: {list.Count}.", $"Rerolled affixes: {list.Count}."); return true; } public static bool TryToggleLock(Player player, ItemData item, int slot, out string message) { message = ""; if ((Object)(object)player == (Object)null || item == null) { return false; } SeidrData seidrData = SeidrData.Load(item); if (slot < 0 || slot >= seidrData.Locks.Length || seidrData.Slots[slot] == null) { return false; } if (!seidrData.Locks[slot]) { if (ItemFactory.CountDust(((Humanoid)player).GetInventory()) < 40) { message = T($"Нужно {40} рунной пыли для замка.", $"{40} runic dust required to lock."); return false; } ItemFactory.RemoveDust(((Humanoid)player).GetInventory(), 40); seidrData.Locks[slot] = true; message = T("Аффикс закреплён.", "Affix locked."); } else { seidrData.Locks[slot] = false; message = T("Замок снят.", "Affix unlocked."); } seidrData.Save(item); Refresh(((Humanoid)player).GetInventory()); return true; } public static bool TrySalvage(Player player, ItemData item, out int dust, out string message) { dust = 0; message = ""; if ((Object)(object)player == (Object)null || item == null) { return false; } SeidrData seidrData = SeidrData.Load(item, allowImport: false); int num = seidrData.Slots.Count((Affix a) => a != null); dust = 5 + seidrData.Level * 4 + num * 3; ((Humanoid)player).GetInventory().RemoveItem(item); ItemFactory.GiveDust(player, dust); Refresh(((Humanoid)player).GetInventory()); message = T($"Предмет разобран в {dust} рунной пыли.", $"Salvaged into {dust} runic dust."); return true; } public static bool TryTransmute(Player player, ScrollInfo info, out string message) { message = ""; if (info.Tier >= ScrollTier.SPlus) { message = T("Свитки тира S+ уже высшие.", "S+ scrolls are the highest."); return false; } ScrollInfo info2 = new ScrollInfo(info.Tier + 1, info.Blessed); string text = (info.Blessed ? T("благословлённых свитков", "blessed scrolls") : T("свитков", "scrolls")); if (ItemFactory.CountScrolls(((Humanoid)player).GetInventory(), info) < 3) { message = T($"Нужно {3} {text} {Scrolls.Letter(info.Tier)}.", $"{3} {Scrolls.Letter(info.Tier)} {text} required."); return false; } for (int i = 0; i < 3; i++) { ItemFactory.TakeScroll(((Humanoid)player).GetInventory(), info); } ItemFactory.GiveScroll(player, info2); Refresh(((Humanoid)player).GetInventory()); message = T("Свитки объединены.", "Scrolls merged."); return true; } public static int UpgradeScrollTier(int level) { return level; } private static string T(string ru, string en) { if (!Settings.Russian.Value) { return en; } return ru; } } public enum ItemCategory { Auto, Weapon, Armor, Shield, Skip } internal sealed class ItemOverride { internal bool HasTier; internal ScrollTier Tier; internal bool HasCategory; internal ItemCategory Category; } public static class ItemCatalog { private static readonly Dictionary<string, ItemOverride> BuiltIn = new Dictionary<string, ItemOverride>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, ItemOverride> User = new Dictionary<string, ItemOverride>(StringComparer.OrdinalIgnoreCase); private static bool _loaded; internal static int BuiltInCount => BuiltIn.Count; internal static IEnumerable<string> BuiltInNames => BuiltIn.Keys; public static string ConfigPath => Path.Combine(Path.Combine(Paths.ConfigPath, "SeidrEnchanting"), "AdditionalItems.txt"); internal static bool IsBuiltIn(string prefab) { if (!string.IsNullOrEmpty(prefab)) { return BuiltIn.ContainsKey(prefab); } return false; } internal static void Initialize() { Load(); } internal static void RegisterBuiltIn(string prefab, ItemCategory category, ScrollTier tier) { if (!string.IsNullOrEmpty(prefab)) { BuiltIn[prefab] = new ItemOverride { HasTier = true, Tier = tier, HasCategory = true, Category = category }; } } internal static ItemOverride Resolve(string prefab) { if (string.IsNullOrEmpty(prefab)) { return null; } Load(); User.TryGetValue(prefab, out var value); BuiltIn.TryGetValue(prefab, out var value2); if (value == null && value2 == null) { return null; } ItemOverride itemOverride = new ItemOverride(); if (value != null) { itemOverride.HasTier = value.HasTier; itemOverride.Tier = value.Tier; itemOverride.HasCategory = value.HasCategory; itemOverride.Category = value.Category; } if (!itemOverride.HasTier && value2 != null && value2.HasTier) { itemOverride.HasTier = true; itemOverride.Tier = value2.Tier; } if (!itemOverride.HasCategory && value2 != null && value2.HasCategory) { itemOverride.HasCategory = true; itemOverride.Category = value2.Category; } return itemOverride; } public static bool TryGetTier(string prefab, out ScrollTier tier) { ItemOverride itemOverride = Resolve(prefab); if (itemOverride != null && itemOverride.HasTier) { tier = itemOverride.Tier; return true; } tier = ScrollTier.E; return false; } public static ItemCategory CategoryOf(ItemData item) { string prefab = ItemTiers.PrefabName(item); ItemOverride itemOverride = Resolve(prefab); if (itemOverride != null && itemOverride.HasCategory) { return itemOverride.Category; } return ModSupport.GuessCategory(prefab); } public static bool IsEligible(ItemData item) { //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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected I4, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 if (item == null || item.m_shared == null) { return false; } switch (CategoryOf(item)) { case ItemCategory.Skip: return false; default: return true; case ItemCategory.Auto: { ItemType itemType = item.m_shared.m_itemType; switch (itemType - 3) { default: if ((int)itemType != 22) { break; } goto case 0; case 0: case 1: case 2: case 3: case 4: case 8: case 11: case 14: return true; case 5: case 6: case 7: case 9: case 10: case 12: case 13: break; } return false; } } } public static bool IsArmor(ItemData item) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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) //IL_0038: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 if (item == null || item.m_shared == null) { return false; } switch (CategoryOf(item)) { case ItemCategory.Armor: return true; case ItemCategory.Weapon: case ItemCategory.Shield: case ItemCategory.Skip: return false; default: { ItemType itemType = item.m_shared.m_itemType; if (itemType - 6 <= 1 || (int)itemType == 11 || (int)itemType == 17) { return true; } return false; } } } public static AffixPool PoolFor(ItemData item) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected I4, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 if (item == null || item.m_shared == null) { return AffixPool.Weapon; } switch (CategoryOf(item)) { case ItemCategory.Armor: return AffixPool.Armor; case ItemCategory.Shield: return AffixPool.Shield; case ItemCategory.Weapon: return AffixPool.Weapon; default: { ItemType itemType = item.m_shared.m_itemType; switch (itemType - 5) { default: if ((int)itemType != 17) { break; } goto case 1; case 1: case 2: case 6: return AffixPool.Armor; case 0: return AffixPool.Shield; case 3: case 4: case 5: break; } return AffixPool.Weapon; } } } private static void Load() { if (_loaded) { return; } _loaded = true; try { string configPath = ConfigPath; string directoryName = Path.GetDirectoryName(configPath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(configPath)) { File.WriteAllText(configPath, BuildDefaultFile(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); Plugin.Log.LogInfo((object)("Список дополнительных предметов создан: " + configPath)); } ReadFile(configPath); Plugin.Log.LogInfo((object)$"Дополнительные предметы: загружено {User.Count} записей."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Не удалось прочитать AdditionalItems.txt: " + ex.Message)); } } private static void ReadFile(string path) { string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text.StartsWith("#") || text.StartsWith("//") || text.StartsWith(";")) { continue; } int num = text.IndexOfAny(new char[2] { '=', ':' }); if (num <= 0) { Warn(i + 1, "нет разделителя '=' или ':'"); continue; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); if (text2.Length == 0 || text3.Length == 0) { Warn(i + 1, "пустое имя предмета или значение"); continue; } ItemOverride itemOverride = new ItemOverride(); bool flag = false; string[] array2 = text3.Split(new char[3] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < array2.Length; j++) { string text4 = array2[j].Trim(); if (text4.Length != 0) { ItemCategory category; if (TryParseTier(text4, out var tier)) { itemOverride.HasTier = true; itemOverride.Tier = tier; flag = true; } else if (TryParseCategory(text4, out category)) { itemOverride.HasCategory = true; itemOverride.Category = category; flag = true; } else { Warn(i + 1, "неизвестное значение '" + text4 + "'"); } } } if (!flag) { Warn(i + 1, "не указан ни тир, ни категория"); } else { User[text2] = itemOverride; } } } private static bool TryParseTier(string value, out ScrollTier tier) { tier = ScrollTier.E; if (ItemTiers.IsTierToken(value)) { tier = Scrolls.TierFrom(value); return true; } for (int i = 0; i < Scrolls.BiomeRu.Length; i++) { if (string.Equals(value, Scrolls.BiomeRu[i], StringComparison.OrdinalIgnoreCase) || string.Equals(value, Scrolls.BiomeEn[i], StringComparison.OrdinalIgnoreCase)) { tier = (ScrollTier)i; return true; } } return false; } private static bool TryParseCategory(string value, out ItemCategory category) { category = ItemCategory.Auto; switch (value.Trim().ToLowerInvariant()) { case "weapon": case "оружие": category = ItemCategory.Weapon; return true; case "armour": case "armor": case "броня": category = ItemCategory.Armor; return true; case "shield": case "щит": category = ItemCategory.Shield; return true; case "auto": case "авто": category = ItemCategory.Auto; return true; case "нет": case "skip": case "exclude": case "исключить": category = ItemCategory.Skip; return true; default: return false; } } private static void Warn(int line, string message) { Plugin.Log.LogWarning((object)$"AdditionalItems.txt, строка {line}: {message}. Строка пропущена."); } private static string BuildDefaultFile() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Seidr Enchanting — дополнительные предметы из других модов."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Формат:"); stringBuilder.AppendLine("# <PrefabName> = <тир>[, <категория>]"); stringBuilder.AppendLine("# <PrefabName> = <категория>"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Тиры: E, D, C, B, A, S, S+"); stringBuilder.AppendLine("# E Луга и Чёрный лес"); stringBuilder.AppendLine("# D Болота и Океан"); stringBuilder.AppendLine("# C Горы"); stringBuilder.AppendLine("# B Равнины"); stringBuilder.AppendLine("# A Туманные земли"); stringBuilder.AppendLine("# S Пепельные земли"); stringBuilder.AppendLine("# S+ Северные земли"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Категории (необязательно):"); stringBuilder.AppendLine("# weapon — пул аффиксов оружия;"); stringBuilder.AppendLine("# armor — пул аффиксов брони;"); stringBuilder.AppendLine("# shield — пул аффиксов щитов;"); stringBuilder.AppendLine("# auto — определить по типу предмета (по умолчанию);"); stringBuilder.AppendLine("# skip — исключить предмет из зачарования."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Если категория не указана, предмет должен иметь обычный"); stringBuilder.AppendLine("# ItemType (меч, щит, шлем, нагрудник...). Для нестандартных"); stringBuilder.AppendLine("# предметов укажите категорию вручную."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Примеры:"); stringBuilder.AppendLine("# SwordMyMod = B"); stringBuilder.AppendLine("# ArmorMyMod = A, armor"); stringBuilder.AppendLine("# ShieldMyMod = S, shield"); stringBuilder.AppendLine("# QuestItemMyMod = skip"); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Встроенная поддержка уже включена для:"); stringBuilder.AppendLine("# Therzie Armory, Therzie Warfare, SouthsilArmor, CAH."); stringBuilder.AppendLine("# Их предметы не нужно добавлять сюда — список уже в моде."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Точное имя префаба можно посмотреть командой `seidr item`"); stringBuilder.AppendLine("# (наведитесь на предмет или возьмите его в руку)."); stringBuilder.AppendLine(); stringBuilder.AppendLine("# --- Ваши предметы ниже ---"); stringBuilder.AppendLine(); return stringBuilder.ToString(); } } public static class ItemTiers { private static readonly Dictionary<string, ScrollTier> Map = new Dictionary<string, ScrollTier>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<SharedData, string> SharedNames = new Dictionary<SharedData, string>(); private static bool _loaded; public static string ConfigPath => Path.Combine(Path.Combine(Paths.ConfigPath, "SeidrEnchanting"), "ItemTiers.txt"); internal static void Initialize() { Load(); } public static ScrollTier TierOf(ItemData item) { if (!TryGet(item, out var tier)) { return ScrollTier.E; } return tier; } public static bool TryGet(ItemData item, out ScrollTier tier) { tier = ScrollTier.E; if (item == null) { return false; } Load(); string text = PrefabName(item); if (!string.IsNullOrEmpty(text) && TryResolve(text, out tier)) { return true; } if (item.m_shared != null && !string.IsNullOrEmpty(item.m_shared.m_name)) { string text2 = item.m_shared.m_name; if (text2.StartsWith("$item_", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring(6); } if (TryResolve(text2, out tier)) { return true; } } return false; } private static bool TryResolve(string prefab, out ScrollTier tier) { if (ItemCatalog.TryGetTier(prefab, out tier)) { return true; } if (Map.TryGetValue(prefab, out tier)) { return true; } if (ModSupport.TryGetTier(prefab, out tier)) { return true; } return Guess(prefab, out tier); } public static string PrefabName(ItemData item) { if (item == null) { return null; } if ((Object)(object)item.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name; } Load(); if (item.m_shared != null && SharedNames.TryGetValue(item.m_shared, out var value)) { return value; } return null; } public static void RebuildShared(ObjectDB db) { if ((Object)(object)db == (Object)null || db.m_items == null) { return; } SharedNames.Clear(); foreach (GameObject item in db.m_items) { if (!((Object)(object)item == (Object)null)) { ItemDrop component = item.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null) { SharedNames[component.m_itemData.m_shared] = ((Object)item).name; } } } } public static List<string> NamesOf(ScrollTier tier) { Load(); List<string> list = new List<string>(); foreach (KeyValuePair<string, ScrollTier> item in Map) { if (item.Value == tier) { list.Add(item.Key); } } list.Sort(StringComparer.OrdinalIgnoreCase); return list; } public static int CountOf(ScrollTier tier) { return NamesOf(tier).Count; } private static void Load() { if (_loaded) { return; } _loaded = true; AddDefaults(); try { string configPath = ConfigPath; string directoryName = Path.GetDirectoryName(configPath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } if (!File.Exists(configPath)) { File.WriteAllText(configPath, BuildDefaultFile(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); Plugin.Log.LogInfo((object)("Список тиров создан: " + configPath)); } ReadFile(configPath); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Не удалось прочитать ItemTiers.txt: " + ex.Message)); } } private static void ReadFile(string path) { string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text.StartsWith("#") || text.StartsWith("//")) { continue; } int num = text.IndexOfAny(new char[2] { ':', '=' }); if (num <= 0) { continue; } string text2 = text.Substring(0, num).Trim(); if (!IsTierToken(text2)) { continue; } ScrollTier value = Scrolls.TierFrom(text2); string text3 = text.Substring(num + 1); int num2 = text3.IndexOf('#'); if (num2 >= 0) { text3 = text3.Substring(0, num2); } string[] array2 = text3.Split(','); for (int j = 0; j < array2.Length; j++) { string text4 = array2[j].Trim(); if (text4.Length != 0) { Map[text4] = value; } } } } internal static bool IsTierToken(string token) { for (int i = 0; i < Scrolls.Letters.Length; i++) { if (token.Equals(Scrolls.Letters[i], StringComparison.OrdinalIgnoreCase) || token.Equals(Scrolls.Tokens[i], StringComparison.OrdinalIgnoreCase)) { return true; } } return token.Equals("SPlus", StringComparison.OrdinalIgnoreCase); } private static string BuildDefaultFile() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Seidr Enchanting — тиры предметов по биомам."); stringBuilder.AppendLine("# Формат: <тир>: Prefab1, Prefab2, ..."); stringBuilder.AppendLine("# Тиры: E Луга и Чёрный лес, D Болота, C Горы, B Равнины,"); stringBuilder.AppendLine("# A Туманные земли, S Пепельные земли, S+ Северные земли."); stringBuilder.AppendLine("# Предмет должен быть в списке своего тира. Свиток того же тира"); stringBuilder.AppendLine("# зачаровывает его до +20. Модовые предметы без списка определяются"); stringBuilder.AppendLine("# по названию (bronze, iron, silver, blackmetal, carapace, flametal...)."); stringBuilder.AppendLine(); ScrollTier[] all = Scrolls.All; foreach (ScrollTier scrollTier in all) { stringBuilder.AppendLine("# " + Scrolls.Letter(scrollTier) + " — " + Scrolls.BiomeRu[(int)scrollTier]); stringBuilder.Append(Scrolls.Letter(scrollTier)).Append(": "); List<string> list = new List<string>(); foreach (KeyValuePair<string, ScrollTier> item in Map) { if (item.Value == scrollTier) { list.Add(item.Key); } } list.Sort(StringComparer.OrdinalIgnoreCase); stringBuilder.AppendLine(string.Join(", ", list)); stringBuilder.AppendLine(); } return stringBuilder.ToString(); } private static void Add(ScrollTier tier, params string[] names) { foreach (string text in names) { if (!string.IsNullOrEmpty(text)) { Map[text] = tier; } } } private static void AddDefaults() { Add(ScrollTier.E, "Club", "AxeStone", "AxeFlint", "SpearFlint", "KnifeFlint", "AxeEarly", "SledgeStagbreaker", "KnifeButcher", "FistBjornClaw", "ShieldWood", "ShieldWoodTower", "Bow", "PickaxeAntler", "ArmorRagsChest", "ArmorRagsLegs", "HelmetLeather", "ArmorLeatherChest", "ArmorLeatherLegs", "CapeDeerHide"); Add(ScrollTier.E, "KnifeCopper", "AtgeirBronze", "AxeBronze", "MaceBronze", "SpearBronze", "SwordBronze", "ShieldBronzeBuckler", "ShieldBoneTower", "BowFineWood", "PickaxeBronze", "HelmetTrollLeather", "ArmorTrollLeatherChest", "ArmorTrollLeatherLegs", "CapeTrollHide", "HelmetBronze", "ArmorBronzeChest", "ArmorBronzeLegs", "HelmetBerserkerHood", "ArmorBerserkerChest", "ArmorBerserkerLegs"); Add(ScrollTier.D, "AtgeirIron", "AxeIron", "MaceIron", "SledgeIron", "SwordIron", "SpearElderbark", "KnifeChitin", "ShieldBanded", "ShieldIronBuckler", "ShieldIronTower", "ShieldSerpentscale", "BowHuntsman", "PickaxeIron", "HelmetIron", "ArmorIronChest", "ArmorIronLegs", "HelmetRoot", "ArmorRootChest", "ArmorRootLegs"); Add(ScrollTier.C, "KnifeSilver", "SwordSilver", "MaceSilver", "SpearWolfFang", "BattleaxeCrystal", "FistFenrirClaw", "ShieldSilver", "BowDraugrFang", "HelmetDrake", "ArmorWolfChest", "ArmorWolfLegs", "CapeWolf", "HelmetFenring", "ArmorFenringChest", "ArmorFenringLegs"); Add(ScrollTier.B, "AtgeirBlackmetal", "SwordBlackmetal", "AxeBlackMetal", "BattleaxeBlackmetal", "MaceNeedle", "FistBjornUndeadClaw", "KnifeBlackMetal", "ShieldBlackmetal", "ShieldBlackmetalTower", "PickaxeBlackMetal", "HelmetPadded", "ArmorPaddedCuirass", "ArmorPaddedGreaves", "HelmetBerserkerUndead", "ArmorBerserkerUndeadChest", "ArmorBerserkerUndeadLegs", "CapeLinen", "CapeLox"); Add(ScrollTier.A, "AtgeirHimminAfl", "THSwordKrom", "SwordMistwalker", "AxeJotunBane", "BattleaxeSkullSplittur", "SledgeDemolisher", "SpearCarapace", "KnifeSkollAndHati", "ShieldCarapaceBuckler", "ShieldCarapace", "BowSpineSnap", "CrossbowArbalest", "StaffFireball", "StaffIceShards", "StaffSkeleton", "HelmetCarapace", "ArmorCarapaceChest", "ArmorCarapaceLegs", "HelmetMage", "ArmorMageChest", "ArmorMageLegs", "CapeFeather"); Add(ScrollTier.S, "SwordNiedhogg", "SwordNiedhoggBlood", "SwordNiedhoggLightning", "SwordNiedhoggNature", "SwordDyrnwyn", "THSwordSlayer", "THSwordSlayerBlood", "THSwordSlayerNature", "THSwordSlayerLightning", "SpearSplitner", "SpearSplitner_Blood", "SpearSplitner_Nature", "SpearSplitner_Lightning", "AxeBerzerkr", "AxeBerzerkrBlood", "AxeBerzerkrLightning", "AxeBerzerkrNature", "MaceEldner", "MaceEldnerBlood", "MaceEldnerLightning", "MaceEldnerNature", "ShieldFlametal", "ShieldFlametalTower", "BowAshlands", "BowAshlandsBlood", "BowAshlandsRoot", "BowAshlandsStorm", "CrossbowRipper", "CrossbowRipperBlood", "CrossbowRipperLightning", "CrossbowRipperNature", "StaffClusterbomb", "StaffGreenRoots", "StaffLightning", "StaffRedTroll", "HelmetAshlandsMediumHood", "ArmorAshlandsMediumChest", "ArmorAshlandsMediumLegs", "HelmetMage_Ashlands", "ArmorMageChest_Ashlands", "ArmorMageLegs_Ashlands", "HelmetFlametal", "ArmorFlametalChest", "ArmorFlametalLegs", "CapeAsksvin", "CapeAsh"); Add(ScrollTier.SPlus, "SwordCheat", "SledgeCheat", "HelmetFishingHat", "ArmorDeepNorthHeavyChest", "ArmorDeepNorthHeavylegs", "HelmetDNHeavy"); ModItems.Register(); } private static bool Guess(string rawName, out ScrollTier tier) { tier = ScrollTier.E; if (string.IsNullOrEmpty(rawName)) { return false; } string text = rawName.ToLowerInvariant(); text = text.Replace("$item_", "").Replace("$", ""); if (Contains(text, "deepnorth", "helmetdn", "storr", "_dn_", "dnheavy", "fishinghat")) { tier = ScrollTier.SPlus; return true; } if (Contains(text, "flametal", "ashlands", "asksvin", "charred", "niedhogg", "dyrnwyn", "slayer", "splitner", "berzerkr", "eldner", "ripper", "staffclusterbomb", "staffgreenroots", "stafflightning", "staffredtroll", "capeash")) { tier = ScrollTier.S; return true; } if (Contains(text, "demonic", "bloodthirst", "vampiric", "scimitar", "scythe", "fader", "queen", "yagluth", "moder")) { tier = ScrollTier.S; return true; } if (Contains(text, "carapace", "dvergr", "mistwalker", "krom", "jotun", "skullsplittur", "demolisher", "spinesnap", "arbalest", "feather", "mage", "eitr", "softtissue", "yggdrasil", "himminafl", "skoll")) { tier = ScrollTier.A; return true; } if (Contains(text, "blackmetal", "padded", "linen", "lox", "needle", "berserkerundead", "bjornundead")) { tier = ScrollTier.B; return true; } if (Contains(text, "silver", "wolf", "fenrir", "drake", "crystal", "draugr", "obsidian", "chitin")) { tier = ScrollTier.C; return true; } if (Contains(text, "iron", "root", "banded", "huntsman", "elderbark", "serpent", "bonemass", "viper", "wrench", "greatclub")) { tier = ScrollTier.D; return true; } if (Contains(text, "bronze", "troll", "finewood", "bone", "copper")) { tier = ScrollTier.E; return true; } if (Contains(text, "flint", "wood", "eikthyr", "club", "rag", "leather", "deerhide")) { tier = ScrollTier.E; return true; } return false; } private static bool Contains(string haystack, params string[] needles) { for (int i = 0; i < needles.Length; i++) { if (haystack.IndexOf(needles[i], StringComparison.Ordinal) >= 0) { return true; } } return false; } } internal static class ModItems { private static bool _registered; internal static void Register() { if (!_registered) { _registered = true; RegisterArmory(); RegisterWarfare(); RegisterSouthsil(); RegisterCah(); Plugin.Log.LogInfo((object)($"[Mods] встроенных предметов: {ItemCatalog.BuiltInCount} " + "(Therzie Armory/Warfare, SouthsilArmor, CAH).")); } } private static void RegisterArmory() { Armor(ScrollTier.E, "HelmetLeather_TW", "ArmorLeatherChest_TW", "ArmorLeatherLegs_TW", "HelmetRazorback_TW", "ArmorRazorbackChest_TW", "ArmorRazorbackLegs_TW", "HelmetHunter_TW", "ArmorHunterChest_TW", "ArmorHunterLegs_TW", "HelmetRogue_TW", "ArmorRogueChest_TW", "ArmorRogueLegs_TW", "HelmetVigorous_TW", "ArmorVigorousChest_TW", "ArmorVigorousLegs_TW", "CapeTrollBlack_TW"); Armor(ScrollTier.D, "HelmetHunterIron_TW", "ArmorHunterChestIron_TW", "ArmorHunterLegsIron_TW", "HelmetRogueIron_TW", "ArmorRogueIronChest_TW", "ArmorRogueIronLegs_TW", "HelmetVigorousIron_TW", "ArmorVigorousIronChest_TW", "ArmorVigorousIronLegs_TW", "HelmetWarrior_TW", "ArmorWarriorChest_TW", "ArmorWarriorLegs_TW"); Armor(ScrollTier.C, "HelmetHunterSilver_TW", "ArmorHunterChestSilver_TW", "ArmorHunterLegsSilver_TW", "HelmetRogueSilver_TW", "ArmorRogueSilverChest_TW", "ArmorRogueSilverLegs_TW", "HelmetVigorousSilver_TW", "ArmorVigorousSilverChest_TW", "ArmorVigorousSilverLegs_TW", "HelmetWarriorSilver_TW", "ArmorWarriorSilverChest_TW", "ArmorWarriorSilverLegs_TW", "HelmetFenrir_TW", "ArmorFenrirChest_TW", "ArmorFenrirLegs_TW", "HelmetVidar_TW", "ArmorVidarChest_TW", "ArmorVidarLegs_TW"); Armor(ScrollTier.B, "HelmetHunterBM_TW", "ArmorHunterChestBM_TW", "ArmorHunterLegsBM_TW", "HelmetRogueBM_TW", "ArmorRogueBMChest_TW", "ArmorRogueBMLegs_TW", "HelmetVigorousBM_TW", "ArmorVigorousBMChest_TW", "ArmorVigorousBMLegs_TW", "HelmetWarriorBM_TW", "ArmorWarriorBMChest_TW", "ArmorWarriorBMLegs_TW", "HelmetFenrirBM_TW", "ArmorFenrirBMChest_TW", "ArmorFenrirBMLegs_TW", "HelmetVidarBM_TW", "ArmorVidarBMChest_TW", "ArmorVidarBMLegs_TW", "HelmetBold_TW", "ArmorBoldChest_TW", "ArmorBoldLegs_TW"); Armor(ScrollTier.A, "HelmetHunterCarapace_TW", "ArmorHunterChestCarapace_TW", "ArmorHunterLegsCarapace_TW", "HelmetRogueCarapace_TW", "ArmorRogueCarapaceChest_TW", "ArmorRogueCarapaceLegs_TW", "HelmetVigorousCarapace_TW", "ArmorVigorousCarapaceChest_TW", "ArmorVigorousCarapaceLegs_TW", "HelmetWarriorCarapace_TW", "ArmorWarriorCarapaceChest_TW", "ArmorWarriorCarapaceLegs_TW", "HelmetFenrirCarapace_TW", "ArmorFenrirCarapaceChest_TW", "ArmorFenrirCarapaceLegs_TW", "HelmetVidarCarapace_TW", "ArmorVidarCarapaceChest_TW", "ArmorVidarCarapaceLegs_TW", "HelmetBoldCarapace_TW", "ArmorBoldCarapaceChest_TW", "ArmorBoldCarapaceLegs_TW", "HelmetLegion_TW", "ArmorLegionChest_TW", "ArmorLegionLegs_TW"); Armor(ScrollTier.S, "HelmetWarriorFlametal_TW", "ArmorWarriorFlametalChest_TW", "ArmorWarriorFlametalLegs_TW", "HelmetVidarFlametal_TW", "ArmorVidarFlametalChest_TW", "ArmorVidarFlametalLegs_TW", "HelmetBoldFlametal_TW", "ArmorBoldFlametalChest_TW", "ArmorBoldFlametalLegs_TW", "HelmetLegionFlametal_TW", "ArmorLegionFlametalChest_TW", "ArmorLegionFlametalLegs_TW"); Armor(ScrollTier.SPlus, "HelmetRogueTyranium_TW", "ArmorRogueTyraniumChest_TW", "ArmorRogueTyraniumLegs_TW", "HelmetVigorousTyranium_TW", "ArmorVigorousTyraniumChest_TW", "ArmorVigorousTyraniumLegs_TW", "HelmetFenrirTyranium_TW", "ArmorFenrirTyraniumChest_TW", "ArmorFenrirTyraniumLegs_TW", "BeltYmir_TW"); } private static void RegisterWarfare() { Weapon(ScrollTier.E, "AtgeirFlint_TW", "BastardFlint_TW", "BattleaxeFlint_TW", "BattleaxeBronze_TW", "BattlehammerElder_TW", "BowTrollBone_TW", "ClaymoreBronze_TW", "CrossbowWood_TW", "CrossbowEikthyr_TW", "CrossbowBronze_TW", "CudgelBronze_TW", "FistFlint_TW", "FistBronze_TW", "GreatClub_TW", "KnifeBronze_TW", "MaceFlint_TW", "SledgeStagbreaker_TW", "SledgeBronze_TW", "SwordFlint_TW", "ThrowAxeFlint_TW", "ThrowAxeBronze_TW", "WarpikeElder_TW"); Weapon(ScrollTier.D, "BastardBone_TW", "BastardChitin_TW", "BattleaxeIron_TW", "BattlehammerTrollbone_TW", "BattlehammerIron_TW", "ClaymoreBone_TW", "ClaymoreChitin_TW", "ClaymoreIron_TW", "CrossbowChitin_TW", "CrossbowIron_TW", "CudgelIron_TW", "DualKnifeBone_TW", "DualKnifeChitin_TW", "DualKnifeIron_TW", "FistIron_TW", "FistChitin_TW", "KnifeIron_TW", "KnifeWrench_TW", "KnifeViper_TW", "MaceChitin_TW", "SledgeIron_TW", "SledgeBonemass_TW", "SwordBone_TW", "SwordChitin_TW", "ThrowAxeIron_TW", "WarpikeBone_TW", "WarpikeChitin_TW"); Weapon(ScrollTier.C, "AxeSilver_TW", "AtgeirSilver_TW", "BastardSilver_TW", "BattleaxeSilver_TW", "BattleaxeCrystal_TW", "BattleaxeCrystalEmerald_TW", "BattleaxeCrystalAmethyst_TW", "BattleaxeCrystalSapphire_TW", "BattleaxeCrystalTopaz_TW", "BattlehammerSilver_TW", "ClaymoreSilver_TW", "CrossbowSilver_TW", "DualKnifeSilver_TW", "FistSilver_TW", "GreatbowModer_TW", "PickaxeSilver_TW", "SledgeSilver_TW", "ThrowAxeSilver_TW", "WarpikeObsidian_TW"); Weapon(ScrollTier.B, "BastardBlackmetal_TW", "BattleaxeBlackmetal_TW", "BattlehammerBlackmetal_TW", "BladeYagluth_TW", "BowBlackmetal_TW", "ClaymoreBlackmetal_TW", "CrossbowBlackmetal_TW", "DualKnifeBM_TW", "FistBlackmetal_TW", "GreatbowBlackmetal_TW", "LanceBlackmetal_TW", "SledgeBlackmetal_TW", "SpearBlackmetal_TW", "ThrowAxeBlackMetal_TW", "TridentBlackmetal_TW", "WarpikeBlackmetal_TW", "DualSwordScimitar_TW", "SwordScimitar_TW"); Weapon(ScrollTier.A, "AxeDvergr_TW", "BastardDvergr_TW", "BattleaxeDvergr_TW", "BattlehammerDvergr_TW", "FistDvergr_TW", "FistQueen_TW", "GreatbowDvergr_TW", "LanceDvergr_TW", "MaceDvergr_TW", "SledgeDemolisher_TW", "WarpikeDvergr_TW", "ClaymoreDvergr_TW", "ThrowAxeDvergr_TW"); Weapon(ScrollTier.S, "AxeFlametal_TW", "BastardFlametal_TW", "BattleaxeFlametal_TW", "DualKnifeFlametal_TW", "DualaxeDemonic_TW", "DualScytheBloodthirst_TW", "FistFlametal_TW", "ScytheVampiric_TW", "SledgeFlametal_TW", "WarpikeFlametal_TW", "PickaxeFader_TW"); Armor(ScrollTier.A, "CapeRotten_TW"); Shield(ScrollTier.E, "ShieldBronzeBanded_TW", "ShieldBronzeTower_TW"); Shield(ScrollTier.D, "ShieldChitin_TW", "ShieldChitinTower_TW", "ShieldBucklerChitin_TW", "ShieldChitinBuckler_TW"); Shield(ScrollTier.C, "ShieldSilverBuckler_TW"); Shield(ScrollTier.B, "ShieldBlackmetalBuckler_TW"); Shield(ScrollTier.A, "ShieldCarapaceTower_TW"); Skip("BombSpirit_TW"); } private static void RegisterSouthsil() { Armor(ScrollTier.E, "neckhelm", "neckchest", "necklegs", "chiefhelmboar", "chiefhelmdeer", "chiefchest", "chieflegs", "trollhelm", "trolllegs", "bronzetrollhelm", "heavybronzehelm", "heavybronzechest", "heavybronzelegs", "ancienthelm", "ancientchest", "ancientlegs", "ss_cape_boar"); Armor(ScrollTier.D, "ss_lightwraithhelm", "ss_lightwraith", "ss_lightwraithlegs", "ss_lightwraith_legs", "ss_cape_lightwraith", "knighthelm", "knightlegs", "knightchest", "bonetitanhelm", "bonetitanchest", "bonetitanlegs", "swamphelm", "swampchest", "swamplegs"); Armor(ScrollTier.C, "ancientironhelm", "ancientironchest", "ancientironlegs", "heavywolfhelm", "heavywolfchest", "heavywolflegs", "skoghelm", "skogchest", "skoglegs", "sven", "svenchest", "svenlegs"); Armor(ScrollTier.B, "norahhelm", "norahlegs", "norahchest", "norahhelmalt", "bphelm", "bpchest", "bplegs", "bearhelm", "bearlegs", "bearchest", "loxhunthelm", "loxhuntchest", "loxhuntlegs", "ss_barbhelm", "ss_barbchest", "ss_barblegs", "ss_serpentcape"); Armor(ScrollTier.A, "frostmagehelm", "frostmagechest", "frostmagelegs", "obswolfhelm", "obswolfchest", "obswolflegs", "druidhelm", "druidchest", "druidlegs", "heavycarhelm", "heavycarchest", "heavycarlegs", "runeknighthelm", "runeknightchest", "runeknightlegs", "bearhelm2", "bearchest2", "bearlegs2", "samuraihelm", "samuraichest", "samurailegs"); Armor(ScrollTier.S, "ss_wraithhelm", "ss_wraithchest", "ss_wraithlegs", "valkhelm", "valkchest", "valklegs", "warlordhelm", "warlordchest", "warlordlegs", "stjudehelm", "stjudechest", "stjudelegs", "heavybearhelm", "heavybearchest", "heavybearlegs", "ss_korokhelm", "ss_korokchest", "ss_koroklegs", "lagihelmalt", "lagichest", "lagipants", "ss_ashcape"); Armor(ScrollTier.SPlus, "trueskoghelm", "trueskogchest", "trueskoglegs", "ss_dragonlordhelm", "ss_dragonlordchest", "ss_dragonlordlegs", "sven2", "sven2chest", "sven2legs", "sven3", "ss_frodehelm", "ss_frodechest", "ss_frodelegs", "ss_storrhelm", "ss_storrchest", "ss_storrlegs", "wshelm", "wschest", "wslegs", "ss_storrcape"); } private static void RegisterCah() { CahWeapon(ScrollTier.E, "OrcSword", "OrcBattleaxe", "OrcMace", "OrcDagger", "OrcSpear", "OrcAtgeir", "OrcFists", "OrcBow", "OrcStaff", "ForestGreatsword", "ForestGreataxe", "ForestMace", "ForestDagger", "ForestSpear", "ForestScythe", "ForestFists", "ForestBow", "ForestChakram", "BeastlyBerry", "Ravencall", "RamHammer", "Goldtooth", "Ephemerance", "VioletReaper", "JestersToss", "SkullStrike", "Cervideus"); CahShield(ScrollTier.E, "OrcShield", "ForestShield"); CahWeapon(ScrollTier.D, "BloodstoneSword", "BloodstoneAxe", "BloodstoneMaul", "BloodstoneDaggers", "BloodstonePike", "BloodstoneClaws", "BloodstoneBow", "BloodstoneStaff", "Bonecleaver", "SoulThief", "SnakeSwordAxe", "Spiritbreakers", "SoulGlaive", "SaurusScythe", "HornedHandscythes", "DemonfaceMace", "FurnaceDagger", "FurnaceBow", "Carpshot", "Crabhammer", "OceanTrident", "SeaSong", "WaveCrusher"); CahShield(ScrollTier.D, "BloodstoneShield", "FurnaceBuckler"); CahWeapon(ScrollTier.C, "SilvercrystalGreatsword", "SilvercrystalAxes", "SilvercrystalHammer", "SilvercrystalDagger", "SilvercrystalSpear", "SilvercrystalAtgeir", "SilvercrystalKatars", "SilvercrystalBow", "SilvercrystalStaff", "Astralis", "FlowerVeil", "MountainCleaver", "Goldcleave", "Goldvein", "EchoCutter", "ToxifrostCrushers", "BloodcurseGlaives", "KrystaliumCrashers", "HallowsGrip", "WindHowl"); CahShield(ScrollTier.C, "SilvercrystalShield", "Shiverwall"); CahWeapon(ScrollTier.B, "SoulburnDualblades", "SoulburnGreatsword", "SoulburnAxe", "SoulburnGreathammer", "SoulburnDaggers", "SoulburnSpear", "SoulburnScythe", "SoulburnBow", "SoulburnCrossbow", "Arondight", "Vane", "Warmonger", "Corruptum", "Demora", "SicklySickle", "Tinkerblade", "Warbringer", "SolsticeSpear", "Sunforger", "SunfistWarpicks", "WildwindBow"); CahShield(ScrollTier.B, "SoulburnShield"); CahWeapon(ScrollTier.A, "SuccubineKatana", "SuccubineGreatsword", "SuccubineGreataxe", "SuccubineMace", "SuccubineDagger", "SuccubineSpear", "SuccubineScythe", "SuccubineSlicers", "SuccubineBow", "SuccubineRifle", "BlossomSong", "Infernus", "Jadecutter", "Soulrend", "Voidheart", "VoidAxes", "Voidshot", "DarkmoonReaper", "ReachCryonic", "ReachInfernal", "EmpiricGreataxe", "EmpiricSickles", "EmpiricSpear", "Malevolence", "Harrower", "Sparkgrinder", "Nocturata"); CahShield(ScrollTier.A, "SuccubineShield", "Soulguard", "Sparkshield"); CahWeapon(ScrollTier.S, "VolcanoKatana", "VolcanoGreatsword", "VolcanoGreataxe", "VolcanoGreathammer", "VolcanoDagger", "VolcanoCleavers", "VolcanoPike", "VolcanoGuantlets", "VolcanicBow", "VolcanoLauncher", "Laevateinn", "Rupture", "BloodsoulVane", "Arumeria", "Auretia", "Carmine", "TinkerersGlory", "Goldstride", "Deignfall", "Magmarium", "Titansbane", "DragonBow", "ThunderSteel", "UnholyStar", "EmeraldFlame"); CahShield(ScrollTier.S, "VolcanoBuckler", "VolcanoShield"); CahWeapon(ScrollTier.SPlus, "Kalduin", "Corundum", "Agnarr", "SunPiercer", "Drellvar", "StrideBreaker", "Xellumari", "Regalia", "Regulator", "Starshot", "Vastrix", "Sacrilege", "Voidmourne", "Frigada", "Agareth", "StormWalker", "StormSinger", "StormSlayers", "Sorrowshard", "Skulghasters", "Rahvitas", "Withercusp", "Shiverdeath", "DeathfrostShot", "Sieglinde", "Permafrost"); CahShield(ScrollTier.SPlus, "Gauntreid"); } private static void CahWeapon(ScrollTier tier, params string[] names) { Weapon(tier, WithSuffix(names, "_CAH")); } private static void CahShield(ScrollTier tier, params string[] names) { Shield(tier, WithSuffix(names, "_CAH")); } private static string[] WithSuffix(string[] names, string suffix) { string[] array = new string[names.Length]; for (int i = 0; i < names.Length; i++) { array[i] = names[i] + suffix; } return array; } private static void Weapon(ScrollTier tier, params string[] names) { Add(ItemCategory.Weapon, tier, names); } private static void Armor(ScrollTier tier, params string[] names) { Add(ItemCategory.Armor, tier, names); } private static void Shield(ScrollTier tier, params string[] names) { Add(ItemCategory.Shield, tier, names); } private static void Skip(params string[] names) { Add(ItemCategory.Skip, ScrollTier.E, names); } private static void Add(ItemCategory category, ScrollTier tier, string[] names) { for (int i = 0; i < names.Length; i++) { ItemCatalog.RegisterBuiltIn(names[i], category, tier); } } } internal static class ModSupport { internal static bool TryGetTier(string prefab, out ScrollTier tier) { tier = ScrollTier.E; if (string.IsNullOrEmpty(prefab)) { return false; } string text = prefab.ToLowerInvariant(); if (text.EndsWith("_tw", StringComparison.Ordinal)) { return TryTherzieTier(text, out tier); } if (text.StartsWith("ss_", StringComparison.Ordinal)) { return TrySouthsilTier(text, out tier); } return false; } internal static ItemCategory GuessCategory(string prefab) { if (string.IsNullOrEmpty(prefab)) { return ItemCategory.Auto; } string text = prefab.ToLowerInvariant(); if (!text.EndsWith("_tw", StringComparison.Ordinal)) { return ItemCategory.Auto; } if (Starts(text, "arrow", "bolt", "bomb", "mead", "rune", "mat_", "se_", "piece_", "ancientscroll", "amethyst", "diamond", "emerald", "onyx", "sapphire", "topaz", "pelt", "scraps", "essence", "fang")) { return ItemCategory.Auto; } if (Contains(text, "shield")) { return ItemCategory.Shield; } if (Starts(text, "helmet", "armor", "cape", "belt") || Contains(text, "armorchest", "armorlegs")) { return ItemCategory.Armor; } if (Starts(text, "axe", "atgeir", "bastard", "battleaxe", "battlehammer", "bow", "blade", "claymore", "crossbow", "cudgel", "dual", "fist", "greatbow", "greatclub", "knife", "lance", "mace", "pickaxe", "scythe", "sledge", "spear", "sword", "throw", "trident", "warpike", "staff")) { return ItemCategory.Weapon; } return ItemCategory.Auto; } private static bool TryTherzieTier(string name, out ScrollTier tier) { tier = ScrollTier.E; if (Contains(name, "tyranium")) { tier = ScrollTier.SPlus; return true; } if (Contains(name, "flametal", "ashlands", "asksvin", "charred", "fader", "niedhogg", "dyrnwyn", "slayer", "splitner", "berzerkr", "eldner", "ripper", "demonic", "bloodthirst", "vampiric", "scimitar", "sacrifice", "destruction", "capeash")) { tier = ScrollTier.S; return true; } if (Contains(name, "carapace", "dvergr", "demolisher", "queen", "rotten", "mistwalker", "krom", "jotun", "skullsplittur", "spinesnap", "arbalest", "feather", "mage", "eitr", "softtissue", "yggdrasil", "himminafl", "skoll", "legion")) { tier = ScrollTier.A; return true; } if (Contains(name, "blackmetal", "padded", "linen", "lox", "needle", "berserkerundead", "bjornundead", "yagluth", "bold") || ContainsToken(name, "bm") || Contains(name, "bm")) { tier = ScrollTier.B; return true; } if (Contains(name, "silver", "wolf", "fenrir", "drake", "crystal", "draugr", "obsidian", "moder", "ullr", "vidar")) { tier = ScrollTier.C; return true; } if (Contains(name, "iron", "root", "banded", "huntsman", "elderbark", "serpent", "bonemass", "viper", "wrench", "greatclub", "chitin", "elder", "bone", "trollbone", "bear", "ymir", "warrior")) { tier = ScrollTier.D; return true; } if (Contains(name, "bronze", "troll", "finewood", "copper", "leather", "razorback", "trollblack", "stag", "eikthyr")) { tier = ScrollTier.E; return true; } if (Contains(name, "flint", "wood", "club", "rag", "deerhide", "hunter", "rogue", "vigorous")) { tier = ScrollTier.E; return true; } return false; } private static bool TrySouthsilTier(string name, out ScrollTier tier) { tier = ScrollTier.E; if (Contains(name, "storr", "dragonlord", "frode", "wshelm", "wschest", "wslegs")) { tier = ScrollTier.SPlus; return true; } if (Contains(name, "korok")) { tier = ScrollTier.S; return true; } if (Contains(name, "wraith") && !Contains(name, "lightwraith")) { tier = ScrollTier.S; return true; } if (Contains(name, "ashcape", "heavybear", "valk", "warlord", "stjude", "lagi")) { tier = ScrollTier.S; return true; } if (Contains(name, "barb", "serpentcape")) {