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 Nornforged v0.2.8
BepInEx/plugins/Nornforged/Nornforged.Core.dll
Decompiled 6 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.Security.Cryptography; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; [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("Rinor")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.8.0")] [assembly: AssemblyInformationalVersion("0.2.8")] [assembly: AssemblyProduct("Nornforged.Core")] [assembly: AssemblyTitle("Nornforged.Core")] [assembly: AssemblyVersion("0.2.8.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Nornforged.Core { public static class ArsenalRegistry { public static readonly string[] Ids = new string[23] { "flanking_edge", "dueling_edge", "surrounded_edge", "relentless_edge", "grounded_impact", "martial_rhythm", "highground", "giant_hunter", "airborne_hunter", "steady_release", "pinning_shot", "shield_breath", "shield_mending", "shield_rebuke", "shield_composure", "parry_riposte", "wet_conduit", "cinder_hunter", "frozen_focus", "arcane_precision", "spell_repair", "sorcerers_breath", "soul_shelter" }; public static void Add(List<PerkDefinition> target) { M("flanking_edge", "Flanking Edge", PerkClass.Standard, 3.0, 240.0, "+{amount}% direct melee damage while behind the enemy, within the rear 120-degree arc."); M("dueling_edge", "Dueling Edge", PerkClass.Standard, 3.0, 220.0, "+{amount}% direct melee damage when the target is the only living hostile enemy within 6 m of you."); M("surrounded_edge", "Surrounded Edge", PerkClass.Special, 5.0, 420.0, "+{amount}% direct melee damage while at least 3 living hostile enemies are within 8 m of you."); M("relentless_edge", "Relentless Edge", PerkClass.Special, 5.0, 360.0, "+{amount}% direct melee damage against the same enemy you last dealt acknowledged direct damage to, within 3 s. This bonus does not stack with itself."); M("grounded_impact", "Grounded Impact", PerkClass.Standard, 5.0, 300.0, "+{amount}% native melee knockback against a grounded enemy; targets retain their native mass, resistance and knockback rules.", "attack,combat_damage,knockback"); M("martial_rhythm", "Martial Rhythm", PerkClass.Ultra, 5.0, 85.0, "Every third paid manual melee attack that deals acknowledged direct enemy damage refunds {amount}% of that attack's actual stamina cost, capped at 85%. One count and refund per attack, even when it hits several enemies.", "direct_damage,attack_stamina"); R("highground", "High Ground", PerkClass.Standard, 3.0, 240.0, "+{amount}% direct projectile damage when its launch point is at least 3 m above the impact point."); R("giant_hunter", "Giant Hunter", PerkClass.Standard, 3.0, 270.0, "+{amount}% direct projectile damage against an enemy whose maximum health is at least 3 times yours."); R("airborne_hunter", "Airborne Hunter", PerkClass.Special, 5.0, 450.0, "+{amount}% direct projectile damage against an airborne enemy that is neither grounded nor swimming."); R("steady_release", "Steady Release", PerkClass.Special, 5.0, 330.0, "+{amount}% direct projectile damage while above 50% stamina and after at least 8 s without taking health damage; checked at impact."); R("pinning_shot", "Pinning Shot", PerkClass.Standard, 5.0, 240.0, "+{amount}% native stagger buildup from projectile hits at least 15 m from the launch point; no extra health damage.", "stagger_damage,projectile"); C(S("shield_breath", "Shield Breath", PerkClass.Special, 5.0, 85.0, "A successful enemy block refunds {amount}% of stamina actually spent blocking, capped at 85%; {cooldown} s cooldown. The refund occurs after native block success is decided."), 1.0, 20.0); C(S("shield_mending", "Shield Mending", PerkClass.Special, 1.0, 60.0, "A successful enemy block restores {amount}% of this shield's missing durability; {cooldown} s cooldown.", "block,durability"), 3.0, 45.0); C(S("shield_rebuke", "Shield Rebuke", PerkClass.Ultra, 10.0, 600.0, "A successful enemy melee block deals frost damage equal to {amount}% of the physical and elemental damage actually blocked; {cooldown} s cooldown. Native frost resistance applies; no secondary triggers."), 2.0, 30.0); C(S("shield_composure", "Shield Composure", PerkClass.Standard, 5.0, 85.0, "A successful enemy projectile block shortens your remaining stamina-regeneration delay by {amount}%, capped at 85%; {cooldown} s cooldown."), 1.0, 15.0); C(S("parry_riposte", "Parry Riposte", PerkClass.Ultra, 15.0, 750.0, "A successful timed enemy shield block grants +{amount}% direct melee damage for 4 s; {cooldown} s cooldown. The same shield must remain equipped.", "block,parry"), 3.0, 35.0); E("wet_conduit", "Wet Conduit", PerkClass.Standard, 3.0, 240.0, "+{amount}% direct spell damage against an enemy with the native Wet status before impact."); E("cinder_hunter", "Cinder Hunter", PerkClass.Standard, 3.0, 240.0, "+{amount}% direct spell damage against an enemy already affected by native Burning before impact."); E("frozen_focus", "Frozen Focus", PerkClass.Special, 5.0, 360.0, "+{amount}% direct spell damage against an enemy already affected by the native Frost slow before impact."); E("arcane_precision", "Arcane Precision", PerkClass.Special, 5.0, 420.0, "+{amount}% direct spell damage while above 75% health and above 50% Eitr; checked at impact.", "direct_damage,eitr"); C(E("spell_repair", "Spell Repair", PerkClass.Special, 1.0, 60.0, "Acknowledged direct enemy spell damage restores {amount}% of the attacking staff's missing durability; {cooldown} s cooldown.", "direct_damage,durability"), 3.0, 45.0); C(E("sorcerers_breath", "Sorcerer's Breath", PerkClass.Ultra, 5.0, 75.0, "A direct offensive-spell enemy kill restores {amount}% maximum stamina; {cooldown} s cooldown. Damage-over-time and secondary kills do not count."), 3.0, 40.0); C(E("soul_shelter", "Soul Shelter", PerkClass.Ultra, 5.0, 75.0, "A direct offensive-spell enemy kill grants a barrier worth {amount}% maximum health for 6 s; {cooldown} s cooldown. Barriers take the strongest remaining amount; damage-over-time and secondary kills do not count."), 5.0, 60.0); static void C(PerkDefinition p, double min, double max) { p.Parameters.Add(new PerkParameter { Id = "cooldown", Min = min, Max = max, Unit = "s", Precision = 3, HigherIsBetter = false }); } PerkDefinition D(string id, string name, PerkClass cls, double min, double max, string trigger, string categories, string caps) { PerkDefinition perkDefinition = new PerkDefinition { Id = id, Name = name, Effect = id, Class = cls, Categories = categories.Split(',').ToList(), RequiredCapabilities = caps.Split(',').ToList(), Parameters = new List<PerkParameter> { new PerkParameter { Id = "amount", Min = min, Max = max, Precision = 3, Unit = "%" } }, Trigger = trigger, Stacking = "Conditional damage bonuses add before one multiplier and keep separate actual-damage attribution. Item-only effects use the attacking weapon or the shield that actually blocked. Character-wide cooldowns survive equipment swaps.", SafetyLimits = "Enemy direct hits only; secondary and reflected hits cannot start bonuses or rewards. Resource refunds require confirmed damage or actual successful blocks. Native resistance, armor, blocking, ownership and resource maxima remain." }; target.Add(perkDefinition); return perkDefinition; } PerkDefinition E(string id, string name, PerkClass cls, double min, double max, string text, string caps = "direct_damage") { return D(id, name, cls, min, max, text, "offensive_staff", caps); } PerkDefinition M(string id, string name, PerkClass cls, double min, double max, string text, string caps = "direct_damage") { return D(id, name, cls, min, max, text, "melee", caps); } PerkDefinition R(string id, string name, PerkClass cls, double min, double max, string text, string caps = "direct_damage,projectile") { return D(id, name, cls, min, max, text, "bow,crossbow", caps); } PerkDefinition S(string id, string name, PerkClass cls, double min, double max, string text, string caps = "block") { return D(id, name, cls, min, max, text, "shield", caps); } } } public static class Balance020 { public const string Version = "0.2.0"; public const int GameplayPrecision = 3; public static void Apply(IReadOnlyList<PerkDefinition> definitions) { Dictionary<string, (double, double)> dictionary = Ranges(); int num = 0; foreach (PerkDefinition definition in definitions) { foreach (PerkParameter parameter in definition.Parameters) { string text = definition.Id + "." + parameter.Id; if (!dictionary.TryGetValue(text, out var value)) { throw new InvalidOperationException("Missing reviewed 0.2.0 balance range: " + text); } parameter.Min = value.Item1; parameter.Max = value.Item2; parameter.Precision = 3; num++; } } if (num != dictionary.Count) { throw new InvalidOperationException("The 0.2.0 balance table contains an unused parameter range."); } } public static Dictionary<string, (double Min, double Max)> Ranges() { Dictionary<string, (double, double)> result = new Dictionary<string, (double, double)>(StringComparer.Ordinal); A("razor_edge", 1.0, 50.0); A("runic_potency", 1.0, 48.0); A("efficient_strikes", 1.0, 38.0); A("quickstrike", 0.5, 25.0); A("quick_draw", 1.0, 35.0); A("rapid_reload", 1.0, 35.0); A("steady_aim", 1.0, 38.0); A("eitr_efficiency", 0.5, 35.0); A("punishing_blows", 1.0, 55.0); A("crushing_force", 1.0, 50.0); A("duelists_guard", 1.0, 50.0); A("reinforced", 3.0, 220.0); A("lightweight", 1.0, 75.0); A("frostbound", 0.5, 28.0); A("emberbound", 0.5, 28.0); A("resource_breaker", 1.0, 65.0); A("efficient_labor", 1.0, 38.0); A("swift_labor", 0.5, 30.0); A("resource_magnet", 2.0, 180.0); A("extended_reach", 1.0, 55.0); A("haulers_strength", 1.0, 35.0); A("builders_reach", 1.0, 70.0); A("green_hands", 1.0, 45.0); A("patient_angler", 1.0, 45.0); A("long_cast", 1.0, 55.0); A("fortified", 0.5, 30.0); A("vitality", 0.5, 22.0); A("endurance", 0.5, 25.0); A("runic_reservoir", 1.0, 30.0); A("recovery", 1.0, 50.0); A("second_wind", 1.0, 50.0); A("runic_flow", 1.0, 50.0); A("fleetfoot", 0.25, 12.0); A("tireless", 1.0, 32.0); A("nimble", 1.0, 32.0); A("springstep", 1.0, 32.0); A("riverstride", 1.0, 32.0); A("quiet_steps", 1.0, 32.0); string[] array = new string[8] { "fire", "frost", "lightning", "poison", "spirit", "blunt", "slash", "pierce" }; foreach (string text in array) { A("ward_" + text, 0.5, (text == "fire" || text == "frost") ? 20 : 16); } A("soft_landing", 2.0, 75.0); A("efficient_guard", 1.0, 38.0); A("firm_grip", 1.0, 60.0); A("stormspark", 5.0, 100.0); C("stormspark", 3.0, 24.0); A("executioner", 3.0, 65.0); A("runic_reserve", 3.0, 65.0); A("veinbreaker", 10.0, 220.0); A("natures_favor", 1.0, 65.0); A("master_repair", 1.0, 9.0); C("master_repair", 1.0, 20.0); A("emergency_ward", 2.0, 35.0); C("emergency_ward", 20.0, 240.0); A("windstep", 1.0, 30.0); C("windstep", 3.0, 45.0); A("runic_guard", 2.0, 30.0); C("runic_guard", 5.0, 90.0); A("second_breath", 2.0, 35.0); C("second_breath", 15.0, 180.0); A("calm_heart", 3.0, 70.0); A("patient_recovery", 3.0, 70.0); A("deep_breath", 3.0, 70.0); A("steady_foothold", 3.0, 60.0); C("steady_foothold", 4.0, 75.0); A("travelers_vigor", 3.0, 60.0); A("thors_judgment", 20.0, 650.0); C("thors_judgment", 2.5, 40.0); A("fenrirs_hunger", 0.5, 18.0); A("fatebreaker", 10.0, 550.0); P("fatebreaker", "boss", 3.0, 200.0); C("fatebreaker", 2.0, 60.0); A("phantom_strike", 10.0, 500.0); P("phantom_strike", "chance", 2.0, 65.0); C("phantom_strike", 1.0, 35.0); A("winter_sovereign", 15.0, 600.0); C("winter_sovereign", 3.0, 65.0); A("runic_echo", 10.0, 450.0); C("runic_echo", 2.0, 50.0); A("ymirs_awakening", 50.0, 900.0); C("ymirs_awakening", 3.0, 75.0); A("earths_bounty", 5.0, 225.0); A("eternal_temper", 25.0, 99.0); A("tireless_creator", 5.0, 95.0); P("tireless_creator", "chance", 2.0, 80.0); A("runic_momentum", 2.0, 80.0); C("runic_momentum", 3.0, 75.0); A("worldsmith", 2.0, 20.0); C("worldsmith", 1.0, 45.0); A("freyrs_garden", 5.0, 80.0); A("aegirs_favor", 2.0, 90.0); A("yggdrasils_shelter", 5.0, 90.0); C("yggdrasils_shelter", 10.0, 240.0); A("bifrost_stride", 3.0, 60.0); P("bifrost_stride", "reduction", 5.0, 85.0); C("bifrost_stride", 1.0, 45.0); A("einherjars_return", 5.0, 90.0); C("einherjars_return", 600.0, 3600.0); A("heimdalls_bastion", 5.0, 90.0); C("heimdalls_bastion", 5.0, 120.0); A("guardians_wrath", 10.0, 450.0); C("guardians_wrath", 1.5, 40.0); A("runic_rebound", 10.0, 500.0); C("runic_rebound", 2.0, 60.0); A("fates_renewal", 5.0, 90.0); C("fates_renewal", 5.0, 120.0); A("ancient_resolve", 2.0, 70.0); C("ancient_resolve", 5.0, 120.0); A("travelers_mercy", 0.2, 3.0); C("travelers_mercy", 0.75, 60.0); A("norns_respite", 5.0, 350.0); A("undying_tenacity", 2.0, 70.0); A("dulled", 1.0, 32.0); A("faded_runes", 1.0, 32.0); A("exhausting", 1.0, 35.0); A("sluggish", 0.25, 15.0); A("brittle", 3.0, 65.0); A("heavy", 2.0, 100.0); A("restrictive", 0.25, 8.0); A("poor_guard", 1.0, 35.0); A("thin_plating", 1.0, 25.0); A("draining", 0.25, 15.0); A("frail", 0.25, 15.0); A("tiring", 1.0, 25.0); A("unbalanced", 1.0, 45.0); A("short_reach", 1.0, 30.0); A("slow_mending", 1.0, 25.0); A("burdened", 1.0, 25.0); A("vulnerable", 0.5, 18.0); A("runic_leak", 1.0, 35.0); return result; void A(string id, double min, double max) { result.Add(id + ".amount", (min, max)); } void C(string id, double min, double max) { P(id, "cooldown", min, max); } void P(string id, string parameter, double min, double max) { result.Add(id + "." + parameter, (min, max)); } } } public static class Balance021 { public const string Version = "0.2.1"; public const int GameplayPrecision = 3; private static readonly HashSet<string> HighRankParameters; static Balance021() { HighRankParameters = new HashSet<string>(StringComparer.Ordinal); string[] array = "razor_edge,runic_potency,punishing_blows,crushing_force,duelists_guard,reinforced,frostbound,emberbound,resource_breaker,extended_reach,haulers_strength,builders_reach,long_cast,fortified,vitality,endurance,runic_reservoir,recovery,second_wind,runic_flow,stormspark,executioner,veinbreaker,emergency_ward,runic_guard,calm_heart,patient_recovery,deep_breath,travelers_vigor,thors_judgment,fatebreaker,phantom_strike,winter_sovereign,runic_echo,ymirs_awakening,yggdrasils_shelter,heimdalls_bastion,guardians_wrath,runic_rebound,norns_respite,work_breath,work_reserve,steady_supplies,breath_of_hearth,guard_focus,storm_step,opening_cut,desperate_edge,farshot,point_blank,ambushers_edge,retaliation_edge,guardbreaker,spellbattery,followthrough,tyrs_measure,bloodstar,flanking_edge,dueling_edge,surrounded_edge,relentless_edge,grounded_impact,highground,giant_hunter,airborne_hunter,steady_release,pinning_shot,shield_rebuke,parry_riposte,wet_conduit,cinder_hunter,frozen_focus,arcane_precision,soul_shelter".Split(','); foreach (string text in array) { HighRankParameters.Add(text + ".amount"); } HighRankParameters.Add("fatebreaker.boss"); } public static bool AllowsHighRankMaximum(string definitionId, string parameterId) { return HighRankParameters.Contains(definitionId + "." + parameterId); } public static void Apply(IReadOnlyList<PerkDefinition> definitions) { Dictionary<string, double> dictionary = ReviewedMaxima(); int num = 0; foreach (PerkDefinition definition in definitions) { foreach (PerkParameter parameter in definition.Parameters) { if (dictionary.TryGetValue(definition.Id + "." + parameter.Id, out var value)) { if (parameter.Unit != "%" || value < parameter.Max) { throw new InvalidOperationException("Invalid reviewed 0.2.1 percentage maximum: " + definition.Id + "." + parameter.Id); } parameter.Max = value; num++; } parameter.Precision = 3; if (AllowsHighRankMaximum(definition.Id, parameter.Id)) { if (definition.Class == PerkClass.Negative || parameter.Unit != "%" || !parameter.HigherIsBetter) { throw new InvalidOperationException("Invalid high-rank percentage contract: " + definition.Id + "." + parameter.Id); } parameter.HighRankMaxima = new Dictionary<int, double> { [9] = parameter.Max * 1.5, [10] = parameter.Max * 2.0, [11] = parameter.Max * 2.5, [12] = parameter.Max * 3.0 }; } } } if (num != dictionary.Count) { throw new InvalidOperationException("The 0.2.1 balance table contains an unused parameter maximum."); } } public static Dictionary<string, double> ReviewedMaxima() { return new Dictionary<string, double>(StringComparer.Ordinal) { ["razor_edge.amount"] = 150.0, ["runic_potency.amount"] = 144.0, ["efficient_strikes.amount"] = 85.0, ["quickstrike.amount"] = 75.0, ["quick_draw.amount"] = 90.0, ["rapid_reload.amount"] = 85.0, ["steady_aim.amount"] = 85.0, ["eitr_efficiency.amount"] = 85.0, ["punishing_blows.amount"] = 165.0, ["crushing_force.amount"] = 150.0, ["duelists_guard.amount"] = 150.0, ["reinforced.amount"] = 660.0, ["lightweight.amount"] = 80.0, ["frostbound.amount"] = 84.0, ["emberbound.amount"] = 84.0, ["resource_breaker.amount"] = 195.0, ["efficient_labor.amount"] = 85.0, ["swift_labor.amount"] = 90.0, ["resource_magnet.amount"] = 540.0, ["extended_reach.amount"] = 165.0, ["haulers_strength.amount"] = 105.0, ["builders_reach.amount"] = 210.0, ["green_hands.amount"] = 85.0, ["patient_angler.amount"] = 85.0, ["long_cast.amount"] = 165.0, ["fortified.amount"] = 90.0, ["vitality.amount"] = 66.0, ["endurance.amount"] = 75.0, ["runic_reservoir.amount"] = 90.0, ["recovery.amount"] = 150.0, ["second_wind.amount"] = 150.0, ["runic_flow.amount"] = 150.0, ["fleetfoot.amount"] = 36.0, ["tireless.amount"] = 85.0, ["nimble.amount"] = 85.0, ["springstep.amount"] = 85.0, ["riverstride.amount"] = 85.0, ["quiet_steps.amount"] = 85.0, ["ward_fire.amount"] = 60.0, ["ward_frost.amount"] = 60.0, ["ward_lightning.amount"] = 48.0, ["ward_poison.amount"] = 48.0, ["ward_spirit.amount"] = 48.0, ["ward_blunt.amount"] = 48.0, ["ward_slash.amount"] = 48.0, ["ward_pierce.amount"] = 48.0, ["soft_landing.amount"] = 85.0, ["efficient_guard.amount"] = 85.0, ["firm_grip.amount"] = 80.0, ["stormspark.amount"] = 300.0, ["executioner.amount"] = 195.0, ["runic_reserve.amount"] = 100.0, ["veinbreaker.amount"] = 660.0, ["natures_favor.amount"] = 195.0, ["emergency_ward.amount"] = 105.0, ["windstep.amount"] = 65.0, ["runic_guard.amount"] = 90.0, ["second_breath.amount"] = 100.0, ["calm_heart.amount"] = 210.0, ["patient_recovery.amount"] = 210.0, ["deep_breath.amount"] = 210.0, ["steady_foothold.amount"] = 75.0, ["travelers_vigor.amount"] = 180.0, ["thors_judgment.amount"] = 1950.0, ["fenrirs_hunger.amount"] = 54.0, ["fatebreaker.amount"] = 1650.0, ["fatebreaker.boss"] = 600.0, ["phantom_strike.amount"] = 1500.0, ["phantom_strike.chance"] = 99.0, ["winter_sovereign.amount"] = 1800.0, ["runic_echo.amount"] = 1350.0, ["ymirs_awakening.amount"] = 2700.0, ["earths_bounty.amount"] = 250.0, ["eternal_temper.amount"] = 99.0, ["tireless_creator.amount"] = 100.0, ["tireless_creator.chance"] = 99.0, ["runic_momentum.amount"] = 90.0, ["freyrs_garden.amount"] = 85.0, ["aegirs_favor.amount"] = 99.0, ["yggdrasils_shelter.amount"] = 270.0, ["bifrost_stride.amount"] = 65.0, ["bifrost_stride.reduction"] = 85.0, ["einherjars_return.amount"] = 100.0, ["heimdalls_bastion.amount"] = 270.0, ["guardians_wrath.amount"] = 1350.0, ["runic_rebound.amount"] = 1500.0, ["fates_renewal.amount"] = 100.0, ["ancient_resolve.amount"] = 75.0, ["travelers_mercy.amount"] = 3.0, ["norns_respite.amount"] = 1050.0, ["undying_tenacity.amount"] = 75.0, ["dulled.amount"] = 64.0, ["faded_runes.amount"] = 64.0, ["exhausting.amount"] = 70.0, ["sluggish.amount"] = 30.0, ["brittle.amount"] = 80.0, ["heavy.amount"] = 200.0, ["restrictive.amount"] = 16.0, ["poor_guard.amount"] = 70.0, ["thin_plating.amount"] = 50.0, ["draining.amount"] = 30.0, ["frail.amount"] = 30.0, ["tiring.amount"] = 50.0, ["unbalanced.amount"] = 90.0, ["short_reach.amount"] = 60.0, ["slow_mending.amount"] = 50.0, ["burdened.amount"] = 50.0, ["vulnerable.amount"] = 36.0, ["runic_leak.amount"] = 70.0, ["work_stride.amount"] = 54.0, ["work_breath.amount"] = 180.0, ["work_reserve.amount"] = 90.0, ["pack_frame.amount"] = 75.0, ["work_recovery.amount"] = 85.0, ["tool_grace.amount"] = 85.0, ["tool_care.amount"] = 80.0, ["supply_sense.amount"] = 600.0, ["measured_work.amount"] = 85.0, ["rhythmic_work.amount"] = 100.0, ["craftsman_trance.amount"] = 85.0, ["field_second_wind.amount"] = 100.0, ["steady_supplies.amount"] = 360.0, ["repairing_touch.amount"] = 54.0, ["rough_care.amount"] = 130.0, ["fumbling.amount"] = 120.0, ["short_breath.amount"] = 50.0, ["overpacked.amount"] = 50.0, ["muffled_steps.amount"] = 80.0, ["shadowweave.amount"] = 75.0, ["featherbound.amount"] = 150.0, ["slow_feast.amount"] = 100.0, ["rain_shedding.amount"] = 85.0, ["camp_memory.amount"] = 100.0, ["measured_parry.amount"] = 80.0, ["longstride.amount"] = 85.0, ["rainwalker.amount"] = 65.0, ["anchor_stance.amount"] = 85.0, ["breath_of_hearth.amount"] = 480.0, ["guard_focus.amount"] = 480.0, ["skyward_leap.amount"] = 150.0, ["oathkeepers_reserve.amount"] = 240.0, ["storm_step.amount"] = 900.0, ["valkyrie_poise.amount"] = 85.0, ["opening_cut.amount"] = 195.0, ["desperate_edge.amount"] = 210.0, ["farshot.amount"] = 225.0, ["point_blank.amount"] = 150.0, ["ambushers_edge.amount"] = 180.0, ["retaliation_edge.amount"] = 360.0, ["guardbreaker.amount"] = 300.0, ["spellbattery.amount"] = 300.0, ["followthrough.amount"] = 360.0, ["tyrs_measure.amount"] = 1500.0, ["bloodstar.amount"] = 750.0, ["runesurge.amount"] = 100.0, ["quiet_construction.amount"] = 80.0, ["careful_demolition.amount"] = 85.0, ["leveling_focus.amount"] = 85.0, ["raising_focus.amount"] = 85.0, ["seed_focus.amount"] = 85.0, ["cultivation_focus.amount"] = 85.0, ["luring_glimmer.amount"] = 150.0, ["hook_window.amount"] = 200.0, ["bait_keeper.amount"] = 99.0, ["restoration_reserve.amount"] = 100.0, ["fortress_oath.amount"] = 75.0, ["weatherseal.amount"] = 95.0, ["artisan_renewal.amount"] = 100.0, ["verdant_clock.amount"] = 85.0, ["deep_roots.amount"] = 35.0, ["harvest_moon.amount"] = 80.0, ["unbroken_line.amount"] = 250.0, ["stillwater_pact.amount"] = 85.0, ["tidal_memory.amount"] = 100.0, ["hooked_fortune.amount"] = 99.0, ["earthshaper.amount"] = 180.0, ["stoneweaver.amount"] = 250.0, ["pathfinder.amount"] = 250.0, ["terraced_wisdom.amount"] = 180.0 }; } } public static class CompactPerkDescriptions { private static readonly IReadOnlyDictionary<string, string> Templates = CreateTemplates(); public static string Template(PerkDefinition def) { if (def == null) { throw new ArgumentNullException("def"); } if (def.DefinitionVersion != 1 || !Templates.TryGetValue(def.Id, out string value)) { return def.Trigger; } return value; } private static IReadOnlyDictionary<string, string> CreateTemplates() { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal) { ["razor_edge"] = "+{amount}% physical damage", ["runic_potency"] = "+{amount}% existing elemental damage", ["efficient_strikes"] = "-{amount}% attack stamina cost", ["quickstrike"] = "+{amount}% melee attack speed", ["quick_draw"] = "+{amount}% bow draw speed", ["rapid_reload"] = "-{amount}% crossbow reload time", ["steady_aim"] = "-{amount}% aiming stamina cost", ["eitr_efficiency"] = "-{amount}% Eitr cost", ["punishing_blows"] = "+{amount}% direct damage vs staggered enemies", ["crushing_force"] = "+{amount}% direct-hit stagger", ["duelists_guard"] = "+{amount}% block power", ["reinforced"] = "+{amount}% max durability", ["lightweight"] = "-{amount}% item weight", ["frostbound"] = "+{amount}% base damage as Frost", ["emberbound"] = "+{amount}% base damage as Fire", ["resource_breaker"] = "+{amount}% gathering damage", ["efficient_labor"] = "-{amount}% work stamina cost", ["swift_labor"] = "+{amount}% work, build and reel speed", ["resource_magnet"] = "+{amount}% wood/stone/ore pickup radius while held", ["extended_reach"] = "+{amount}% tool reach", ["haulers_strength"] = "+{amount}% base carry capacity", ["builders_reach"] = "+{amount}% build/repair reach", ["green_hands"] = "-{amount}% planting stamina cost", ["patient_angler"] = "-{amount}% reeling stamina cost", ["long_cast"] = "+{amount}% cast distance and line length", ["fortified"] = "+{amount}% Armor", ["vitality"] = "+{amount}% max HP from base + food", ["endurance"] = "+{amount}% max stamina from base + food", ["runic_reservoir"] = "+{amount}% existing food Eitr", ["recovery"] = "+{amount}% HP regen", ["second_wind"] = "+{amount}% stamina regen", ["runic_flow"] = "+{amount}% Eitr regen", ["fleetfoot"] = "+{amount}% walk/run speed", ["tireless"] = "-{amount}% sprint stamina cost", ["nimble"] = "-{amount}% dodge stamina cost", ["springstep"] = "-{amount}% jump stamina cost", ["riverstride"] = "-{amount}% swim stamina cost", ["quiet_steps"] = "-{amount}% sneak stamina cost", ["soft_landing"] = "-{amount}% fall damage", ["efficient_guard"] = "-{amount}% block stamina cost", ["firm_grip"] = "-{amount}% knockback taken while blocking", ["stormspark"] = "Direct hit: chain {amount}% damage to 1 enemy within 8m; CD {cooldown}s", ["executioner"] = "+{amount}% direct damage vs enemies below 20% HP", ["runic_reserve"] = "Every 5th manual spell: refund {amount}% Eitr spent", ["veinbreaker"] = "Every 6th gather hit: +{amount}% gathering damage", ["natures_favor"] = "+{amount}% average natural-resource yield on first drops", ["master_repair"] = "Repair up to 3 owned pieces within {amount}m; CD {cooldown}s", ["emergency_ward"] = "Hit leaves HP below 30%: {amount}% max-HP barrier for 4s; CD {cooldown}s", ["windstep"] = "Dodge: +{amount}% movement for 2s; CD {cooldown}s", ["runic_guard"] = "Block: {amount}% max-HP barrier for 4s; CD {cooldown}s", ["second_breath"] = "Below 20% stamina: restore {amount}% max stamina; CD {cooldown}s", ["calm_heart"] = "8s unharmed: +{amount}% HP regen", ["patient_recovery"] = "Below 50% HP: +{amount}% HP regen", ["deep_breath"] = "Below 40% stamina: +{amount}% stamina regen", ["steady_foothold"] = "Taking damage: -{amount}% stagger taken for 4s; CD {cooldown}s", ["travelers_vigor"] = "Above 80% HP: +{amount}% stamina regen", ["thors_judgment"] = "Every 6th direct hit: {amount}% lightning damage, up to 3 enemies/8m; CD {cooldown}s", ["fenrirs_hunger"] = "Heal {amount}% of direct damage dealt; shared cap 3% max HP/s", ["fatebreaker"] = "+{amount}% direct damage vs enemies below 30% HP (bosses +{boss}%); CD {cooldown}s", ["phantom_strike"] = "Direct hit: {chance}% chance of {amount}% extra damage; CD {cooldown}s", ["winter_sovereign"] = "Every 8th direct hit: {amount}% frost damage in 5m; CD {cooldown}s", ["runic_echo"] = "Every 4th manual offensive cast: repeat {amount}% direct damage; CD {cooldown}s", ["ymirs_awakening"] = "Every 8th gather hit: {amount}% gathering damage in 3m; CD {cooldown}s", ["earths_bounty"] = "+{amount}% average natural-resource yield on first drops", ["eternal_temper"] = "{amount}% chance to avoid durability loss", ["tireless_creator"] = "Successful work: {chance}% chance to refund {amount}% stamina spent", ["runic_momentum"] = "Every 5th successful work action: +{amount}% work speed for 6s; CD {cooldown}s", ["worldsmith"] = "Repair up to 10 owned pieces within {amount}m; CD {cooldown}s", ["freyrs_garden"] = "Left Shift: plant 3x3, -{amount}% total stamina cost; needs all seeds", ["aegirs_favor"] = "Catch: {amount}% chance of 1 extra matching fish", ["yggdrasils_shelter"] = "Hit leaves HP below 35%: {amount}% max-HP barrier for 6s; CD {cooldown}s", ["bifrost_stride"] = "Dodge: +{amount}% movement for 3s, next dodge cost -{reduction}%; CD {cooldown}s", ["einherjars_return"] = "Survive lethal hit at {amount}% max HP + 2s protection; shared CD {cooldown}s", ["heimdalls_bastion"] = "3 blocks in 5s: {amount}% max-HP barrier for 6s; CD {cooldown}s", ["guardians_wrath"] = "Block: next direct melee hit within 3s deals +{amount}%; CD {cooldown}s", ["runic_rebound"] = "Block enemy projectile: reflect {amount}% incoming damage; CD {cooldown}s", ["fates_renewal"] = "Below 15% stamina: restore {amount}% max stamina; CD {cooldown}s", ["ancient_resolve"] = "Taking damage: -{amount}% damage taken for 4s; CD {cooldown}s", ["travelers_mercy"] = "Dodge: heal {amount}% max HP (shared cap 3%/s); CD {cooldown}s", ["norns_respite"] = "12s unharmed: +{amount}% HP regen", ["undying_tenacity"] = "Below 30% HP: -{amount}% stagger taken", ["dulled"] = "-{amount}% physical/gathering damage", ["faded_runes"] = "-{amount}% existing elemental spell damage", ["exhausting"] = "+{amount}% stamina costs", ["sluggish"] = "-{amount}% attack/work animation speed", ["brittle"] = "-{amount}% max durability", ["heavy"] = "+{amount}% item weight", ["restrictive"] = "-{amount}% movement speed", ["poor_guard"] = "-{amount}% block power", ["thin_plating"] = "-{amount}% Armor", ["draining"] = "-{amount}% max stamina from base + food", ["frail"] = "-{amount}% max HP from base + food", ["tiring"] = "-{amount}% stamina regen", ["unbalanced"] = "+{amount}% knockback taken", ["short_reach"] = "-{amount}% tool reach", ["slow_mending"] = "-{amount}% HP regen", ["burdened"] = "-{amount}% base carry capacity", ["vulnerable"] = "+{amount}% physical damage taken after resistance", ["runic_leak"] = "+{amount}% Eitr costs", ["work_stride"] = "Successful work: +{amount}% movement for 8s with same tool", ["work_breath"] = "Successful work: +{amount}% stamina regen for 8s with same tool", ["work_reserve"] = "+{amount}% max stamina from base + food while held", ["pack_frame"] = "-{amount}% materials/seed/bait/fish weight while held", ["work_recovery"] = "Successful work: -{amount}% remaining stamina regen delay", ["tool_grace"] = "-{amount}% tool equip/sheathe time", ["tool_care"] = "-{amount}% tool durability loss", ["supply_sense"] = "+{amount}% materials/seed/bait/fish pickup radius while held", ["measured_work"] = "After 3 work successes: -{amount}% work cost; resets on swap/10s idle", ["rhythmic_work"] = "Every 3rd work success: refund {amount}% stamina spent", ["craftsman_trance"] = "Every 6th work success: next 3 work costs -{amount}%; CD {cooldown}s; reset on swap/10s idle", ["field_second_wind"] = "Every 8th work success: restore {amount}% max stamina; CD {cooldown}s", ["steady_supplies"] = "Every 8th work success: +{amount}% carry capacity for 12s with same tool; CD {cooldown}s", ["repairing_touch"] = "Every 10th work success: repair {amount}% missing tool durability; CD {cooldown}s", ["tool_courier"] = "Every 8th work success: pull up to 24 supplies within {amount}m; CD {cooldown}s", ["rough_care"] = "+{amount}% tool durability loss", ["fumbling"] = "+{amount}% tool equip/sheathe time", ["short_breath"] = "Successful work: -{amount}% stamina regen for 8s with same tool", ["overpacked"] = "+{amount}% materials/seed/bait/fish weight while held", ["muffled_steps"] = "-{amount}% noise radius (80% cap)", ["shadowweave"] = "-{amount}% visibility while sneaking (75% cap)", ["featherbound"] = "+{amount}% jump height (150% cap)", ["slow_feast"] = "+{amount}% food duration (100% cap)", ["rain_shedding"] = "-{amount}% Wet duration (85% cap)", ["camp_memory"] = "+{amount}% next Rested duration (100% cap)", ["measured_parry"] = "+{amount}% shield parry bonus (80% cap)", ["longstride"] = "Running for 4s: -{amount}% jump stamina cost", ["rainwalker"] = "While Wet: +{amount}% movement speed", ["anchor_stance"] = "After blocking still for 1.5s: -{amount}% block cost", ["breath_of_hearth"] = "While Resting: +{amount}% HP regen", ["guard_focus"] = "Block: +{amount}% stamina regen for 6s; CD {cooldown}s", ["skyward_leap"] = "1 extra jump/landing, +{amount}% height (150% cap); stamina required; CD {cooldown}s", ["oathkeepers_reserve"] = "Block: gain {amount}% blocked damage as stamina (15% max-stamina cap); CD {cooldown}s", ["storm_step"] = "Dodge: +{amount}% stamina regen for 4s; CD {cooldown}s", ["valkyrie_poise"] = "-{amount}% fall damage; 30% prevented damage to stamina (20% max cap); refund CD {cooldown}s", ["opening_cut"] = "+{amount}% direct damage vs targets above 90% HP", ["desperate_edge"] = "Below 25% stamina: +{amount}% direct damage", ["farshot"] = "+{amount}% direct projectile damage at 15m+ from launch", ["point_blank"] = "+{amount}% direct projectile damage within 5m of launch", ["ambushers_edge"] = "+{amount}% direct damage vs unalerted enemies", ["retaliation_edge"] = "After taking HP damage: +{amount}% direct damage for 4s", ["guardbreaker"] = "+{amount}% direct damage vs blocking humanoids", ["spellbattery"] = "Above 80% Eitr: +{amount}% direct spell damage", ["followthrough"] = "Direct hit: next direct hit on a different enemy within 3s deals +{amount}%", ["tyrs_measure"] = "Below 50% HP: +{amount}% direct boss damage; CD {cooldown}s", ["bloodstar"] = "Direct enemy kill: +{amount}% direct damage for 6s; no buffed-kill refresh; CD {cooldown}s", ["runesurge"] = "Direct spell hit: refund {amount}% Eitr spent (20% max Eitr/cast cap); CD {cooldown}s", ["quiet_construction"] = "-{amount}% build/repair/demolition noise", ["careful_demolition"] = "-{amount}% successful demolition stamina cost", ["leveling_focus"] = "-{amount}% level-ground stamina cost", ["raising_focus"] = "-{amount}% raise-ground stamina cost", ["seed_focus"] = "-{amount}% planting stamina cost", ["cultivation_focus"] = "-{amount}% cultivation stamina cost", ["luring_glimmer"] = "+{amount}% fish attraction radius; correct bait required", ["hook_window"] = "+{amount}% manual hook window (base 0.5s); correct bait", ["bait_keeper"] = "Catch: {amount}% chance to return 1 bait used", ["restoration_reserve"] = "Manual repair: restore {amount}% max stamina; CD {cooldown}s", ["fortress_oath"] = "New owned pieces take {amount}% less hit damage (75% cap)", ["weatherseal"] = "New owned pieces take {amount}% less weather damage; need support", ["artisan_renewal"] = "Manual repair: restore {amount}% missing hammer durability; CD {cooldown}s", ["verdant_clock"] = "New plants: -{amount}% growth time (85% cap); normal growing conditions", ["deep_roots"] = "New plants: -{amount}% spacing radius (35% cap); normal growing conditions", ["harvest_moon"] = "Plant: advance up to 6 nearby owned healthy plants by {amount}% base growth; CD {cooldown}s", ["unbroken_line"] = "+{amount}% line stretch before breaking", ["stillwater_pact"] = "Hooked fish struggling: -{amount}% fishing stamina cost", ["tidal_memory"] = "Catch: restore {amount}% max stamina; CD {cooldown}s", ["hooked_fortune"] = "Correct-bait nibble: {amount}% auto-hook chance; reel normally; CD {cooldown}s", ["earthshaper"] = "+{amount}% level-ground radius (8m cap); whole area needs permission", ["stoneweaver"] = "+{amount}% height per paid raise-ground action; native terrain limits", ["pathfinder"] = "+{amount}% paint-ground radius (8m cap); whole area needs permission", ["terraced_wisdom"] = "+{amount}% smoothing radius (8m cap); whole area needs permission", ["flanking_edge"] = "+{amount}% direct melee damage from rear 120-degree arc", ["dueling_edge"] = "+{amount}% direct melee damage vs sole enemy within 6m", ["surrounded_edge"] = "3+ enemies within 8m: +{amount}% direct melee damage", ["relentless_edge"] = "+{amount}% direct melee damage vs last directly hit enemy for 3s; no stacking", ["grounded_impact"] = "+{amount}% melee knockback vs grounded enemies", ["martial_rhythm"] = "Every 3rd paid melee attack with direct damage: refund {amount}% stamina spent (85% cap)", ["highground"] = "+{amount}% direct projectile damage launched 3m+ above impact", ["giant_hunter"] = "+{amount}% direct projectile damage vs enemies with 3x your max HP", ["airborne_hunter"] = "+{amount}% direct projectile damage vs airborne enemies; not swimmers", ["steady_release"] = "At impact: above 50% stamina + 8s unharmed, +{amount}% direct projectile damage", ["pinning_shot"] = "+{amount}% projectile stagger at 15m+ from launch; no extra HP damage", ["shield_breath"] = "Enemy block: refund {amount}% block stamina spent (85% cap); CD {cooldown}s", ["shield_mending"] = "Enemy block: repair {amount}% missing shield durability; CD {cooldown}s", ["shield_rebuke"] = "Enemy melee block: return {amount}% blocked damage as Frost; CD {cooldown}s", ["shield_composure"] = "Enemy projectile block: -{amount}% stamina regen delay (85% cap); CD {cooldown}s", ["parry_riposte"] = "Parry: +{amount}% direct melee damage for 4s with same shield; CD {cooldown}s", ["wet_conduit"] = "+{amount}% direct spell damage vs Wet enemies", ["cinder_hunter"] = "+{amount}% direct spell damage vs Burning enemies", ["frozen_focus"] = "+{amount}% direct spell damage vs Frost-slowed enemies", ["arcane_precision"] = "At impact: above 75% HP + 50% Eitr, +{amount}% direct spell damage", ["spell_repair"] = "Direct spell hit: repair {amount}% missing staff durability; CD {cooldown}s", ["sorcerers_breath"] = "Direct spell kill: restore {amount}% max stamina; no DOT/procs; CD {cooldown}s", ["soul_shelter"] = "Direct spell kill: {amount}% max-HP barrier for 6s, strongest only; no DOT/procs; CD {cooldown}s" }; string[] array = new string[8] { "fire", "frost", "lightning", "poison", "spirit", "blunt", "slash", "pierce" }; foreach (string text in array) { dictionary.Add("ward_" + text, "-{amount}% incoming " + text + " damage after resistance"); } return dictionary; } } public static class ConfigurationValidator { public static List<string> Validate(GameplayConfig config, IEnumerable<ItemProfile>? profiles = null, bool requireDiversePools = true) { List<string> list = new List<string>(); if (config.SchemaVersion != 1) { list.Add("Unsupported gameplay configuration schema."); } if (config.Rarities == null || config.StrengthBands == null || config.Perks == null || config.Limits == null || config.ItemOverrides == null) { list.Add("Configuration tables must not be null."); return list; } if (config.Rarities.Any((RarityDefinition r) => r == null) || config.StrengthBands.Any((StrengthBand b) => b == null) || config.Perks.Any((PerkDefinition p) => p == null) || config.ItemOverrides.Any((ItemOverride o) => o == null)) { list.Add("Configuration tables must not contain null entries."); return list; } bool flag = config.Rarities.Any((RarityDefinition r) => r.Rank == 0); if (config.Rarities.Count != (flag ? 19 : 18)) { list.Add("The full 18 nonzero rarities are required, optionally with rank-zero Default as a nineteenth outcome."); } if (((IEnumerable<RarityDefinition>)config.Rarities).Sum((Func<RarityDefinition, long>)((RarityDefinition r) => r.Weight)) != 10000) { list.Add("Rarity weights must total exactly 10,000; automatic normalization is forbidden."); } if (config.Rarities.Select((RarityDefinition r) => r.Id).Distinct<string>(StringComparer.Ordinal).Count() != config.Rarities.Count) { list.Add("Rarity IDs must be unique."); } if (config.Rarities.Select((RarityDefinition r) => r.Rank).Distinct().Count() != config.Rarities.Count) { list.Add("Rarity ranks must be unique."); } foreach (RarityDefinition rarity in config.Rarities) { if (string.IsNullOrWhiteSpace(rarity.Id) || string.IsNullOrWhiteSpace(rarity.Name)) { list.Add("Rarity IDs and names must be present."); } if (rarity.Weight < 0 || rarity.Rank < -6 || rarity.Rank > 12) { list.Add("Invalid rarity weight or rank: " + rarity.Id); } if (rarity.Rank == 0 != (rarity.Id == "default")) { list.Add("Only Default may use rank zero: " + rarity.Id); } if (PerkSelector.Slots(rarity).Any((int n) => n < 0 || n > 12) || ((IEnumerable<int>)PerkSelector.Slots(rarity)).Sum((Func<int, long>)((int n) => n)) != Math.Abs((long)rarity.Rank)) { list.Add("Perk allocation must equal absolute rarity rank: " + rarity.Id); } if ((rarity.Rank < 0 && rarity.NegativeSlots != -rarity.Rank) || (rarity.Rank > 0 && rarity.NegativeSlots != 0)) { list.Add("Only negative rarities allocate penalties: " + rarity.Id); } if (rarity.UltraSlots > 0 && rarity.Rank != 12) { list.Add("Ultra perks are exclusive to Transcendent."); } if (!Regex.IsMatch(rarity.Color ?? "", "^#[0-9a-fA-F]{6}$")) { list.Add("Invalid rarity color: " + rarity.Id); } } if (config.StrengthBands.Count == 0 || ((IEnumerable<StrengthBand>)config.StrengthBands).Sum((Func<StrengthBand, long>)((StrengthBand b) => b.Weight)) != 10000) { list.Add("Strength band weights must total exactly 10,000."); } int num = 0; foreach (StrengthBand strengthBand in config.StrengthBands) { if (strengthBand.MinScore != num || strengthBand.MaxScore < strengthBand.MinScore || strengthBand.MaxScore > 10000 || strengthBand.Weight < 0) { list.Add("Strength bands must cover scores 0–10000 exactly once in order, without gaps or overlap."); } num = strengthBand.MaxScore + 1; } if (num != 10001) { list.Add("Strength bands must include the exact score 10000."); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (PerkDefinition perk in config.Perks) { if (!hashSet.Add(perk.Id) || string.IsNullOrWhiteSpace(perk.Id)) { list.Add("Missing or duplicate perk ID: " + perk.Id); } if (string.IsNullOrWhiteSpace(perk.Name) || string.IsNullOrWhiteSpace(perk.Effect) || string.IsNullOrWhiteSpace(perk.Trigger) || string.IsNullOrWhiteSpace(perk.Stacking) || string.IsNullOrWhiteSpace(perk.SafetyLimits)) { list.Add("Perk must declare its name, implemented effect, trigger, stacking, and safety rules: " + perk.Id); } if (perk.Weight < 0 || perk.DefinitionVersion < 1 || !Enum.IsDefined(typeof(PerkClass), perk.Class)) { list.Add("Invalid perk weight, class, or definition version: " + perk.Id); } if (perk.Categories == null || perk.RequiredCapabilities == null || perk.ExcludedNativeEffects == null || perk.ExclusionGroups == null || perk.IncompatibleIds == null || perk.Parameters == null) { list.Add("Perk metadata collections must be defined: " + perk.Id); continue; } if (perk.Parameters.Count == 0) { list.Add("Every perk requires a randomized numerical parameter: " + perk.Id); } HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); foreach (PerkParameter parameter in perk.Parameters) { if (parameter == null) { list.Add("Perk parameter must not be null: " + perk.Id); continue; } if (!hashSet2.Add(parameter.Id) || string.IsNullOrWhiteSpace(parameter.Id)) { list.Add("Duplicate or absent parameter: " + perk.Id); } if (!Finite(parameter.Min) || !Finite(parameter.Max) || parameter.Min >= parameter.Max || parameter.Precision < 0 || parameter.Precision > 6 || string.IsNullOrWhiteSpace(parameter.Unit)) { list.Add("Invalid randomized parameter range, precision, or unit: " + perk.Id + "." + parameter.Id); } if (parameter.HighRankMaxima == null) { list.Add("High-rank maxima must be an object, empty for untiered or historical ranges: " + perk.Id + "." + parameter.Id); } else { if (parameter.HighRankMaxima.Count <= 0) { continue; } if (parameter.HighRankMaxima.Count != 4) { list.Add("A high-rank maximum map must declare all four ranks 9 through 12, or be empty: " + perk.Id + "." + parameter.Id); } if (!Balance021.AllowsHighRankMaximum(perk.Id, parameter.Id) || perk.Class == PerkClass.Negative || parameter.Unit != "%" || !parameter.HigherIsBetter) { list.Add("High-rank maxima are only supported for reviewed uncapped beneficial percentages: " + perk.Id + "." + parameter.Id); } double num2 = parameter.Max; for (int num3 = 9; num3 <= 12; num3++) { if (parameter.HighRankMaxima.TryGetValue(num3, out var value)) { if (!Finite(value) || value < num2 || value > 9000.0) { list.Add("High-rank maxima must be finite, nondecreasing, at least the base maximum and at most 9000: " + perk.Id + "." + parameter.Id); } num2 = value; } } if (parameter.HighRankMaxima.Keys.Any((int rank) => rank < 9 || rank > 12)) { list.Add("High-rank maxima support only ranks 9 through 12: " + perk.Id + "." + parameter.Id); } } } } if (((IEnumerable<PerkDefinition>)config.Perks).Sum((Func<PerkDefinition, long>)((PerkDefinition p) => p.Weight)) > int.MaxValue) { list.Add("Perk weight total exceeds supported integer range."); } foreach (PerkDefinition item in config.Perks.Where((PerkDefinition p) => p.IncompatibleIds != null)) { foreach (string incompatibleId in item.IncompatibleIds) { if (!hashSet.Contains(incompatibleId) || incompatibleId == item.Id) { list.Add("Unknown or self-referencing incompatible perk: " + item.Id + " -> " + incompatibleId); } } } BalanceLimits limits = config.Limits; double[] array = new double[6] { limits.MovementSpeedPercent, limits.AttackSpeedPercent, limits.CostReductionPercent, limits.DamageReductionPercent, limits.ResourceBonusPercent, limits.HealingMaxHealthPerSecondPercent }; foreach (double num5 in array) { if (!Finite(num5) || num5 < 0.0 || num5 > 1000.0) { list.Add("Aggregate percent caps must be finite and between 0 and 1000."); } } if (limits.CostReductionPercent >= 100.0 || limits.DamageReductionPercent >= 100.0) { list.Add("Cost and damage reduction caps must be below 100%."); } if (!Finite(limits.StatFloorMultiplier) || limits.StatFloorMultiplier <= 0.0 || limits.StatFloorMultiplier > 1.0 || !Finite(limits.MaximumCostMultiplier) || limits.MaximumCostMultiplier < 1.0 || limits.MaximumCostMultiplier > 20.0) { list.Add("Stat floors and action-cost limits must preserve usable equipment."); } HashSet<string> hashSet3 = new HashSet<string>(StringComparer.Ordinal); foreach (ItemOverride itemOverride in config.ItemOverrides) { if (string.IsNullOrWhiteSpace(itemOverride.Prefab) || !hashSet3.Add(itemOverride.Prefab) || (itemOverride.Enabled && string.IsNullOrWhiteSpace(itemOverride.Category)) || itemOverride.Capabilities == null || itemOverride.NativeEffects == null) { list.Add("Invalid or duplicate supported-item override: " + itemOverride.Prefab); } } if (list.Count == 0 && profiles != null) { foreach (ItemProfile profile in profiles) { list.AddRange(ValidateProfile(config, profile, requireDiversePools)); } } return list; } public static List<string> ValidateProfile(GameplayConfig config, ItemProfile profile, bool requireDiversePools = false) { List<string> list = new List<string>(); if (string.IsNullOrWhiteSpace(profile.Category) || profile.Capabilities == null || profile.NativeEffects == null) { list.Add("Item classification is incomplete: " + profile.Prefab); return list; } PerkSelector perkSelector = new PerkSelector(config.Perks); foreach (RarityDefinition rarity in config.Rarities) { try { if (!perkSelector.CanComplete(profile, rarity)) { list.Add(profile.Prefab + " (" + profile.Category + ") cannot complete " + rarity.Name + "."); } } catch (InvalidOperationException ex) { list.Add(profile.Prefab + ": " + ex.Message); break; } } if (requireDiversePools) { int[] array = new int[4] { 15, 6, 6, 8 }; int cls; for (cls = 0; cls < 4; cls++) { if (config.Rarities.Any((RarityDefinition r) => PerkSelector.Slots(r)[cls] > 0)) { int num = config.Perks.Count((PerkDefinition p) => p.Class == (PerkClass)cls && Compatibility.Supports(p, profile)); if (num < array[cls]) { string[] obj = new string[8] { profile.Category, " has ", num.ToString(), " eligible ", null, null, null, null }; PerkClass perkClass = (PerkClass)cls; obj[4] = perkClass.ToString(); obj[5] = " perks; requires at least "; obj[6] = array[cls].ToString(); obj[7] = " for random variety."; list.Add(string.Concat(obj)); } } } } return list; } private static bool Finite(double value) { if (!double.IsNaN(value)) { return !double.IsInfinity(value); } return false; } } public sealed class DamageContribution { public string OwnerId { get; set; } = ""; public string OriginId { get; set; } = ""; public string SourceId { get; set; } = "base"; public string Element { get; set; } = "damage"; public double Amount { get; set; } public DamageContribution Copy(double? amount = null) { return new DamageContribution { OwnerId = OwnerId, OriginId = OriginId, SourceId = SourceId, Element = Element, Amount = (amount ?? Amount) }; } } public static class DamagePartition { public static bool Finite(double v) { if (!double.IsNaN(v)) { return !double.IsInfinity(v); } return false; } public static List<DamageContribution> Reconcile(IEnumerable<DamageContribution> sources, IReadOnlyDictionary<string, double> typedDamage, double actualDamage, string fallbackOwner = "", string fallbackOrigin = "") { if (!Finite(actualDamage) || actualDamage <= 0.0) { return new List<DamageContribution>(); } List<DamageContribution> source = sources.Where((DamageContribution x) => Finite(x.Amount) && x.Amount > 0.0).ToList(); List<DamageContribution> list = new List<DamageContribution>(); foreach (KeyValuePair<string, double> pair in typedDamage.Where<KeyValuePair<string, double>>((KeyValuePair<string, double> x) => Finite(x.Value) && x.Value > 0.0)) { List<DamageContribution> source2 = source.Where((DamageContribution x) => x.Element == pair.Key).ToList(); double total = source2.Sum((DamageContribution x) => x.Amount); if (total <= 0.0) { list.Add(new DamageContribution { OwnerId = fallbackOwner, OriginId = fallbackOrigin, Element = pair.Key, Amount = pair.Value }); } else { list.AddRange(source2.Select((DamageContribution x) => x.Copy(pair.Value * x.Amount / total))); } } double num = list.Sum((DamageContribution x) => x.Amount); if (num <= 0.0) { return new List<DamageContribution> { new DamageContribution { OwnerId = fallbackOwner, OriginId = fallbackOrigin, Amount = actualDamage } }; } foreach (DamageContribution item in list) { item.Amount *= actualDamage / num; } list.OrderByDescending((DamageContribution x) => x.Amount).First().Amount += actualDamage - list.Sum((DamageContribution x) => x.Amount); return Merge(list); } public static List<DamageContribution> Merge(IEnumerable<DamageContribution> values) { return (from x in values where Finite(x.Amount) && x.Amount > 0.0 group x by x.OwnerId + "\u001f" + x.SourceId + "\u001f" + x.Element).Select(delegate(IGrouping<string, DamageContribution> g) { DamageContribution damageContribution = g.Last().Copy(); damageContribution.Amount = g.Sum((DamageContribution x) => x.Amount); return damageContribution; }).ToList(); } public static List<DamageContribution> RoundPartition(IEnumerable<DamageContribution> values, double total) { List<DamageContribution> list = Merge(values); if (list.Count == 0) { return list; } long num = (long)Math.Round(Math.Max(0.0, total) * 1000.0, MidpointRounding.AwayFromZero); double num2 = list.Sum((DamageContribution x) => x.Amount); if (num2 <= 0.0) { return new List<DamageContribution>(); } List<(int, double)> list2 = new List<(int, double)>(); long num3 = 0L; for (int num4 = 0; num4 < list.Count; num4++) { double num5 = list[num4].Amount / num2 * (double)num; long num6 = (long)Math.Floor(num5); list[num4].Amount = (double)num6 / 1000.0; num3 += num6; list2.Add((num4, num5 - (double)num6)); } foreach (var item in (from x in list2 orderby x.fraction descending, x.index select x).Take((int)Math.Max(0L, num - num3))) { list[item.Item1].Amount += 0.001; } return list; } } public sealed class DamageLedgerSnapshot { public double WindowSeconds { get; set; } public double TotalDamage { get; set; } public double Dps => TotalDamage / WindowSeconds; public int HitCount { get; set; } public bool CapacityLimited { get; set; } public List<DamageContribution> Sources { get; set; } = new List<DamageContribution>(); } public sealed class DamageLedger { private sealed class Entry { internal double Time; internal double Actual; internal string Id = ""; internal List<DamageContribution> Parts = new List<DamageContribution>(); } public const int MaximumEventSources = 256; private readonly LinkedList<Entry> entries = new LinkedList<Entry>(); private readonly Dictionary<string, double> ids = new Dictionary<string, double>(StringComparer.Ordinal); private readonly Queue<(string id, double time)> idOrder = new Queue<(string, double)>(); private readonly object gate = new object(); private readonly int capacity; private readonly double retention; private double lastEviction = double.NegativeInfinity; public int RetainedEvents { get { lock (gate) { return entries.Count; } } } public int RetainedIds { get { lock (gate) { return ids.Count; } } } public DamageLedger(int capacity = 60000, double retentionSeconds = 1800.0) { if (capacity < 1 || retentionSeconds < 1.0) { throw new ArgumentOutOfRangeException(); } this.capacity = capacity; retention = retentionSeconds; } public bool Add(string id, double now, double actual, IEnumerable<DamageContribution> contributions) { if (string.IsNullOrWhiteSpace(id) || !DamagePartition.Finite(now) || !DamagePartition.Finite(actual) || actual <= 0.0) { return false; } lock (gate) { Prune(now); if (ids.ContainsKey(id)) { return false; } List<DamageContribution> list = DamagePartition.Merge(contributions); double num = list.Sum((DamageContribution x) => x.Amount); if (list.Count == 0 || list.Count > 256 || !DamagePartition.Finite(num) || num <= 0.0) { return false; } foreach (DamageContribution item in list) { item.Amount *= actual / num; } ids[id] = now; idOrder.Enqueue((id, now)); entries.AddLast(new Entry { Id = id, Time = now, Actual = actual, Parts = list }); while (entries.Count > capacity) { lastEviction = entries.First.Value.Time; entries.RemoveFirst(); } while (ids.Count > capacity * 2) { (string, double) tuple = idOrder.Dequeue(); ids.Remove(tuple.Item1); } return true; } } private void Prune(double now) { while (entries.First != null && entries.First.Value.Time <= now - retention) { entries.RemoveFirst(); } while (idOrder.Count > 0 && idOrder.Peek().time <= now - retention) { (string, double) tuple = idOrder.Dequeue(); ids.Remove(tuple.Item1); } } public DamageLedgerSnapshot Snapshot(double now, double windowSeconds) { if (!DamagePartition.Finite(now) || !DamagePartition.Finite(windowSeconds) || windowSeconds <= 0.0) { throw new ArgumentOutOfRangeException(); } lock (gate) { Prune(now); windowSeconds = Math.Min(retention, windowSeconds); List<Entry> list = entries.Where((Entry x) => x.Time > now - windowSeconds && x.Time <= now).ToList(); return new DamageLedgerSnapshot { WindowSeconds = windowSeconds, TotalDamage = list.Sum((Entry x) => x.Actual), HitCount = list.Count, CapacityLimited = (lastEviction > now - windowSeconds), Sources = DamagePartition.Merge(list.SelectMany((Entry x) => x.Parts)) }; } } public void Clear() { lock (gate) { entries.Clear(); ids.Clear(); idOrder.Clear(); lastEviction = double.NegativeInfinity; } } } public sealed class DamageOverTimePool { private sealed class Bucket { internal List<DamageContribution> Remaining = new List<DamageContribution>(); internal List<DamageContribution> LastWeights = new List<DamageContribution>(); } public const int MaximumSources = 1024; private readonly Dictionary<string, Bucket> buckets = new Dictionary<string, Bucket>(StringComparer.Ordinal); private Bucket Get(string element) { if (!buckets.TryGetValue(element, out Bucket value)) { value = (buckets[element] = new Bucket()); } return value; } public void Add(string element, double nativeBefore, double nativeAfter, IEnumerable<DamageContribution> incoming) { Bucket bucket = Get(element); Sync(bucket, element, Math.Max(0.0, nativeBefore)); double num = Math.Max(0.0, nativeAfter - nativeBefore); if (num > 0.0) { bucket.Remaining = Bound(DamagePartition.Merge(bucket.Remaining.Concat(Scale(incoming, element, num))), element); } bucket.LastWeights = bucket.Remaining.Select((DamageContribution x) => x.Copy()).ToList(); } public void Replace(string element, double nativeBefore, double attempted, double nativeAfter, IEnumerable<DamageContribution> incoming) { Bucket bucket = Get(element); Sync(bucket, element, Math.Max(0.0, nativeBefore)); if (attempted >= nativeBefore) { bucket.Remaining = Bound(Scale(incoming, element, Math.Max(0.0, nativeAfter)), element); bucket.LastWeights = bucket.Remaining.Select((DamageContribution x) => x.Copy()).ToList(); } } public List<DamageContribution> Take(string element, double tickDamage) { if (tickDamage <= 0.0) { return new List<DamageContribution>(); } Bucket bucket = Get(element); List<DamageContribution> result = Scale((bucket.Remaining.Count > 0) ? bucket.Remaining : bucket.LastWeights, element, tickDamage); double num = bucket.Remaining.Sum((DamageContribution x) => x.Amount); if (num > 0.0) { bucket.Remaining = Scale(bucket.Remaining, element, Math.Max(0.0, num - tickDamage)); } return result; } private static List<DamageContribution> Scale(IEnumerable<DamageContribution> source, string element, double amount) { if (amount <= 0.0) { return new List<DamageContribution>(); } List<DamageContribution> list = DamagePartition.Merge(source.Where((DamageContribution x) => x.Element == element)); double num = list.Sum((DamageContribution x) => x.Amount); if (num <= 0.0) { return new List<DamageContribution> { new DamageContribution { Element = element, Amount = amount } }; } foreach (DamageContribution item in list) { item.Amount *= amount / num; } return list; } private static List<DamageContribution> Bound(List<DamageContribution> rows, string element) { if (rows.Count <= 1024) { return rows; } List<DamageContribution> list = rows.OrderByDescending((DamageContribution x) => x.Amount).Take(1023).ToList(); list.Add(new DamageContribution { Element = element, Amount = rows.Sum((DamageContribution x) => x.Amount) - list.Sum((DamageContribution x) => x.Amount) }); return list; } private static void Sync(Bucket b, string element, double native) { double num = b.Remaining.Sum((DamageContribution x) => x.Amount); if (num > native) { b.Remaining = Scale(b.Remaining, element, native); } else if (native > num) { b.Remaining.Add(new DamageContribution { Element = element, Amount = native - num }); } } } public static class Defaults { public const string Version = "0.2.2"; public static List<RarityDefinition> Rarities() { return new List<RarityDefinition> { R("worthless", "Worthless", -6, 30, "#60646C"), R("ruined", "Ruined", -5, 70, "#746357"), R("broken", "Broken", -4, 150, "#8D5550"), R("damaged", "Damaged", -3, 250, "#AA725C"), R("worn", "Worn", -2, 300, "#A8977C"), R("inferior", "Inferior", -1, 400, "#B6B0A3"), R("default", "Default", 0, 3500, "#B9B9B9"), R("common", "Common", 1, 2500, "#E0E0E0"), R("uncommon", "Uncommon", 2, 1500, "#63C76A"), R("rare", "Rare", 3, 660, "#4B92F1"), R("very_rare", "Very Rare", 4, 300, "#50CFD8"), R("exceptional", "Exceptional", 5, 150, "#6B63E8"), R("epic", "Epic", 6, 75, "#AB62EB"), R("heroic", "Heroic", 7, 40, "#DF69B2"), R("legendary", "Legendary", 8, 20, "#F49B3D"), R("mythic", "Mythic", 9, 18, "#F05B68", 8, 1), R("exalted", "Exalted", 10, 15, "#DDB52F", 8, 2), R("divine", "Divine", 11, 12, "#FFE48A", 9, 2), R("transcendent", "Transcendent", 12, 10, "#BEF6FF", 9, 0, 3) }; } private static RarityDefinition R(string id, string name, int rank, int weight, string color, int standard = 0, int special = 0, int ultra = 0) { return new RarityDefinition { Id = id, Name = name, Rank = rank, Weight = weight, Color = color, NegativeSlots = ((rank < 0) ? (-rank) : 0), StandardSlots = ((rank >= 0) ? ((standard == 0) ? rank : standard) : 0), SpecialSlots = special, UltraSlots = ultra }; } public static List<StrengthBand> StrengthBands() { return new List<StrengthBand> { new StrengthBand { MinScore = 0, MaxScore = 999, Weight = 8000 }, new StrengthBand { MinScore = 1000, MaxScore = 2499, Weight = 1400 }, new StrengthBand { MinScore = 2500, MaxScore = 4999, Weight = 450 }, new StrengthBand { MinScore = 5000, MaxScore = 7999, Weight = 120 }, new StrengthBand { MinScore = 8000, MaxScore = 9499, Weight = 28 }, new StrengthBand { MinScore = 9500, MaxScore = 10000, Weight = 2 } }; } } public static class EffectiveRange { public static PerkParameter ForRank(PerkParameter parameter, int rank) { if (parameter == null) { throw new ArgumentNullException("parameter"); } double value; double max = ((parameter.HighRankMaxima != null && parameter.HighRankMaxima.TryGetValue(rank, out value)) ? value : parameter.Max); return new PerkParameter { Id = parameter.Id, Min = parameter.Min, Max = max, Unit = parameter.Unit, Precision = parameter.Precision, HigherIsBetter = parameter.HigherIsBetter, HighRankMaxima = new Dictionary<int, double>() }; } } public sealed class FateGenerator { private readonly GameplayConfig config; private readonly IRandomSource rarityRandom; private readonly IRandomSource selectionRandom; private readonly IRandomSource cosmeticRandom; private readonly StrengthSampler strength; private readonly PerkSelector selector; private readonly Dictionary<string, List<string>> validationCache = new Dictionary<string, List<string>>(StringComparer.Ordinal); private readonly object gate = new object(); public FateGenerator(GameplayConfig config, IRandomSource? rarityRng = null, IRandomSource? selectionRng = null, IRandomSource? strengthRng = null, IRandomSource? cosmeticRng = null) { List<string> list = ConfigurationValidator.Validate(config); if (list.Count != 0) { throw new ArgumentException(string.Join("\n", list)); } this.config = JsonConvert.DeserializeObject<GameplayConfig>(JsonConvert.SerializeObject((object)config)); rarityRandom = rarityRng ?? new CryptoRandomSource(); selectionRandom = selectionRng ?? new CryptoRandomSource(); cosmeticRandom = cosmeticRng ?? new CryptoRandomSource(); strength = new StrengthSampler(this.config.StrengthBands, strengthRng ?? new CryptoRandomSource()); selector = new PerkSelector(this.config.Perks); } public List<string> Validate(ItemProfile profile) { string key = JsonConvert.SerializeObject((object)new string[4] { profile.Prefab, profile.Category, string.Join("|", profile.Capabilities.OrderBy<string, string>((string s) => s, StringComparer.Ordinal)), string.Join("|", profile.NativeEffects.OrderBy<string, string>((string s) => s, StringComparer.Ordinal)) }); lock (gate) { if (!validationCache.TryGetValue(key, out List<string> value)) { value = ConfigurationValidator.ValidateProfile(config, profile); validationCache.Add(key, value); } return new List<string>(value); } } public Fateprint Roll(ItemProfile profile, string transactionId, string itemId, string crafterId, string crafterName) { if (string.IsNullOrWhiteSpace(transactionId) || string.IsNullOrWhiteSpace(itemId)) { throw new ArgumentException("Each committed craft requires transaction and item identifiers."); } List<string> list = Validate(profile); if (list.Count != 0) { throw new InvalidOperationException(string.Join("\n", list)); } lock (gate) { RarityDefinition rarity = WeightedRandom.Select(config.Rarities, (RarityDefinition r) => r.Weight, rarityRandom); List<PerkDefinition> list2 = selector.Select(profile, rarity, selectionRandom); return new Fateprint { ItemId = itemId, TransactionId = transactionId, RarityId = rarity.Id, Rank = rarity.Rank, CrafterId = (crafterId ?? ""), CrafterName = (crafterName ?? ""), CosmeticSeed = cosmeticRandom.NextInt(int.MaxValue), Perks = list2.Select((PerkDefinition d) => strength.Roll(d, rarity.Rank)).ToList(), Epithet = EpithetFor(list2) }; } } private static string EpithetFor(IEnumerable<PerkDefinition> definitions) { HashSet<string> hashSet = new HashSet<string>(definitions.Select((PerkDefinition p) => p.Id), StringComparer.Ordinal); if (hashSet.Contains("thors_judgment") || hashSet.Contains("stormspark")) { return "Storm-Etched"; } if (hashSet.Contains("winter_sovereign") || hashSet.Contains("frostbound")) { return "Frost-Kissed"; } if (hashSet.Contains("earths_bounty") || hashSet.Contains("natures_favor")) { return "Deepvein"; } if (hashSet.Contains("yggdrasils_shelter") || hashSet.Contains("emergency_ward")) { return "Oathguard"; } return ""; } } public sealed class GameplayConfigUpgradeResult { public bool Upgraded { get; internal set; } public string Json { get; internal set; } = ""; public string FromVersion { get; internal set; } = ""; public string ToVersion { get; internal set; } = "0.2.2"; public string Diagnostic { get; internal set; } = ""; } public static class GameplayConfigUpgrade { public const string LegacyDefaultVersion = "0.1.0"; public static string LegacyDefaultJson() { return FrozenDefaultJson("0.1.0"); } public static string FrozenDefaultJson(string version) { if (version != "0.1.0" && version != "0.2.0" && version != "0.2.1") { throw new ArgumentException("No frozen default exists for version " + version, "version"); } using Stream stream = typeof(GameplayConfigUpgrade).Assembly.GetManifestResourceStream("Nornforged.Core.Defaults.v" + version + ".json") ?? throw new InvalidOperationException("The frozen " + version + " default configuration resource is missing."); using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } public static GameplayConfigUpgradeResult UpgradeUntouchedDefaults(string json) { //IL_013f: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_004d: Expected O, but got Unknown GameplayConfigUpgradeResult gameplayConfigUpgradeResult = new GameplayConfigUpgradeResult { Json = (json ?? "") }; if (string.IsNullOrWhiteSpace(json) || json.Length > 2097152) { gameplayConfigUpgradeResult.Diagnostic = "Configuration was preserved unchanged; it is empty or exceeds the supported size."; return gameplayConfigUpgradeResult; } try { JsonLoadSettings val = new JsonLoadSettings { DuplicatePropertyNameHandling = (DuplicatePropertyNameHandling)2, CommentHandling = (CommentHandling)0 }; JObject val2 = JObject.Parse(json, val); string text = JsonConvert.SerializeObject((object)new GameplayConfig { Perks = Registry.CreateDefault() }, (Formatting)1); if (JToken.DeepEquals((JToken)(object)val2, (JToken)(object)JObject.Parse(text, val))) { gameplayConfigUpgradeResult.FromVersion = "0.2.2"; gameplayConfigUpgradeResult.Diagnostic = "Configuration already matches the 0.2.2 defaults; no file change is needed."; return gameplayConfigUpgradeResult; } string[] array = new string[3] { "0.2.1", "0.2.0", "0.1.0" }; foreach (string text2 in array) { if (JToken.DeepEquals((JToken)(object)val2, (JToken)(object)JObject.Parse(FrozenDefaultJson(text2), val))) { gameplayConfigUpgradeResult.FromVersion = text2; break; } } if (gameplayConfigUpgradeResult.FromVersion.Length == 0) { gameplayConfigUpgradeResult.Diagnostic = "Configuration preserved unchanged. Only complete untouched 0.1.0, 0.2.0 or 0.2.1 defaults are automatically upgraded; compare custom settings with the 0.2.2 defaults manually."; return gameplayConfigUpgradeResult; } gameplayConfigUpgradeResult.Json = text; gameplayConfigUpgradeResult.Upgraded = true; gameplayConfigUpgradeResult.Diagnostic = "Untouched default gameplay configuration upgraded from " + gameplayConfigUpgradeResult.FromVersion + " to 0.2.2. Existing item Fateprints and prepared-transaction snapshots are unchanged."; return gameplayConfigUpgradeResult; } catch (JsonException ex) { JsonException ex2 = ex; gameplayConfigUpgradeResult.Diagnostic = "Configuration preserved unchanged because its JSON could not be safely compared: " + ((Exception)(object)ex2).Message; return gameplayConfigUpgradeResult; } } } public enum PerkClass { Standard, Special, Ultra, Negative } public sealed class ItemProfile { public string Prefab { get; set; } = ""; public string Category { get; set; } = ""; public HashSet<string> Capabilities { get; set; } = new HashSet<string>(StringComparer.Ordinal); public HashSet<string> NativeEffects { get; set; } = new HashSet<string>(StringComparer.Ordinal); } public sealed class PerkParameter { public string Id { get; set; } = ""; public double Min { get; set; } public double Max { get; set; } public string Unit { get; set; } = "%"; public int Precision { get; set; } = 3; public bool HigherIsBetter { get; set; } = true; [JsonProperty(/*Could not decode attribute arguments.*/)] public Dictionary<int, double> HighRankMaxima { get; set; } = new Dictionary<int, double>(); } public sealed class PerkDefinition { public string Id { get; set; } = ""; public string Name { get; set; } = ""; public PerkClass Class { get; set; } public List<string> Categories { get; set; } = new List<string>(); public List<string> RequiredCapabilities { get; set; } = new List<string>(); public List<string> ExcludedNativeEffects { get; set; } = new List<string>(); public int Weight { get; set; } = 1; public List<string> ExclusionGroups { get; set; } = new List<string>(); public List<string> IncompatibleIds { get; set; } = new List<string>(); public List<PerkParameter> Parameters { get; set; } = new List<PerkParameter>(); public string Effect { get; set; } = ""; public string Trigger { get; set; } = "While equipped."; public string Stacking { get; set; } = "Add contributions before aggregate caps."; public string SafetyLimits { get; set; } = ""; public int DefinitionVersion { get; set; } = 1; } public sealed class RarityDefinition { public string Id { get; set; } = ""; public string Name { get; set; } = ""; public int Rank { get; set; } public int Weight { get; set; } public int StandardSlots { get; set; } public int SpecialSlots { get; set; } public int UltraSlots { get; set; } public int NegativeSlots { get; set; } public string Color { get; set; } = "#FFFFFF"; public int PerkCount => StandardSlots + SpecialSlots + UltraSlots + NegativeSlots; } public sealed class StrengthBand { public int MinScore { get; set; } public int MaxScore { get; set; } public int Weight { get; set; } } public sealed class RolledPerk { public string Id { get; set; } = ""; public int DefinitionVersion { get; set; } = 1; public Dictionary<string, double> Values { get; set; } = new Dictionary<string, double>(StringComparer.Ordinal); public Dictionary<string, int> QualityScores { get; set; } = new Dictionary<string, int>(StringComparer.Ordinal); } public sealed class Fateprint { public const int CurrentSchema = 2; public int SchemaVersion { get; set; } = 2; public string ItemId { get; set; } = ""; public string TransactionId { get; set; } = ""; public string RarityId { get; set; } = ""; public int Rank { get; set; } public List<RolledPerk> Perks { get; set; } = new List<RolledPerk>(); public string CrafterId { get; set; } = ""; public string CrafterName { get; set; } = ""; public int CosmeticSeed { get; set; } public string Epithet { get; set; } = ""; } public sealed class BalanceLimits { public double MovementSpeedPercent { get; set; } = 65.0; public double AttackSpeedPercent { get; set; } = 90.0; public double CostReductionPercent { get; set; } = 85.0; public double DamageReductionPercent { get; set; } = 75.0; public double ResourceBonusPercent { get; set; } = 250.0; public double HealingMaxHealthPerSecondPercent { get; set; } = 3.0; public double StatFloorMultiplier { get; set; } = 0.2; public double MaximumCostMultiplier { get; set; } = 3.0; } public sealed class ItemOverride { public string Prefab { get; set; } = ""; public bool Enabled { get; set; } = true; public string Category { get; set; } = ""; public List<string> Capabilities { get; set; } = new List<string>(); public List<string> NativeEffects { get; set; } = new List<string>(); } public sealed class GameplayConfig { public int SchemaVersion { get; set; } = 1; [JsonProperty(/*Could not decode attribute arguments.*/)] public List<RarityDefinition> Rarities { get; set; } = Defaults.Rarities(); [JsonProperty(/*Could not decode attribute arguments.*/)] public List<StrengthBand> StrengthBands { get; set; } = Defaults.StrengthBands(); public List<PerkDefinition> Perks { get; set; } = new List<PerkDefinition>(); public BalanceLimits Limits { get; set; } = new BalanceLimits(); public List<ItemOverride> ItemOverrides { get; set; } = new List<ItemOverride>(); } public static class Compatibility { public static bool Supports(PerkDefinition perk, ItemProfile item) { if (perk.Weight > 0 && (perk.Categories.Count == 0 || perk.Categories.Contains("*") || perk.Categories.Contains(item.Category)) && perk.RequiredCapabilities.All(item.Capabilities.Contains)) { return !perk.ExcludedNativeEffects.Any(item.NativeEffects.Contains); } return false; } public static bool CanCombine(PerkDefinition left, PerkDefinition right) { if (left.Id != right.Id && !left.IncompatibleIds.Contains(right.Id) && !right.IncompatibleIds.Contains(left.Id)) { return !left.ExclusionGroups.Intersect<string>(right.ExclusionGroups, StringComparer.Ordinal).Any(); } return false; } } public sealed class PerkSelector { private readonly IReadOnlyList<PerkDefinition> registry; public PerkSelector(IReadOnlyList<PerkDefinition> registry) { this.registry = registry; } public static int[] Slots(RarityDefinition rarity) { return new int[4] { rarity.StandardSlots, rarity.SpecialSlots, rarity.UltraSlots, rarity.NegativeSlots }; } public bool CanComplete(ItemProfile profile, RarityDefinition rarity) { List<PerkDefinition> pool = registry.Where((PerkDefinition p) => Compatibility.Supports(p, profile)).ToList(); int budget = 250000; return Complete(pool, Slots(rarity), ref budget); } public List<PerkDefinition> Select(ItemProfile profile, RarityDefinition rarity, IRandomSource random) { List<PerkDefinition> list = registry.Where((PerkDefinition p) => Compatibility.Supports(p, profile)).ToList(); int[] array = Slots(rarity); List<PerkDefinition> list2 = new List<PerkDefinition>(); int cls; for (cls = 0; cls < array.Length; cls++) { while (array[cls] > 0) { array[cls]--; List<PerkDefinition> list3 = new List<PerkDefinition>(); foreach (PerkDefinition candidate in list.Where((PerkDefinition p) => p.Class == (PerkClass)cls)) { List<PerkDefinition> pool = list.Where((PerkDefinition p) => Compatibility.CanCombine(candidate, p)).ToList(); int budget = 250000; if (Complete(pool, array, ref budget)) { list3.Add(candidate); } } if (list3.Count == 0) { throw new InvalidOperationException("Cannot complete " + rarity.Name + " allocation for " + profile.Prefab + ". Craft must be blocked before materials are consumed."); } PerkDefinition winner = WeightedRandom.Select(list3, (PerkDefinition p) => p.Weight, random); list2.Add(winner); list.RemoveAll((PerkDefinition p) => !Compatibility.CanCombine(winner, p)); } } return list2; } private static bool Complete(List<PerkDefinition> pool, int[] remaining, ref int budget) { if (--budget <= 0) { throw new InvalidOperationException("Perk compatibility search exceeded its safety budget; simplify exclusions. Configuration is not approved for crafting."); } int chosenClass = -1; int num = int.MaxValue; int cls; for (cls = 0; cls < remaining.Length; cls++) { if (remaining[cls] != 0) { int num2 = pool.Count((PerkDefinition p) => p.Class == (PerkClass)cls); if (num2 < remaining[cls]) { return false; } int num3 = num2 - remaining[cls]; if (num3 < num) { num = num3; chosenClass = cls; } } } if (chosenClass < 0) { return true; } List<PerkDefinition> list = pool.Where((PerkDefinition p) => p.Class == (PerkClass)chosenClass).ToList(); for (int num4 = 0; num4 <= list.Count - remaining[chosenClass]; num4++) { PerkDefinition candidate = list[num4]; HashSet<string> earlier = new HashSet<string>(from p in list.Take(num4 + 1) select p.Id, StringComparer.Ordinal); List<PerkDefinition> pool2 = pool.Where((PerkDefinition p) => !earlier.Contains(p.Id) && Compatibility.CanCombine(candidate, p)).ToList(); remaining[chosenClass]--; bool num5 = Complete(pool2, remaining, ref budget); remaining[chosenClass]++; if (num5) { return true; } } return false; } } public sealed class FateprintReadResult { public Fateprint? Value { get; internal set; } public bool IsValid => Value != null; public bool Migrated { get; internal set; } public string Diagnostic { get; internal set; } = ""; public string OriginalJson { get; internal set; } = ""; } public static class FateprintSerializer { public const string CustomDataKey = "nornforged.fate"; private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings { TypeNameHandling = (TypeNameHandling)0, MaxDepth = 20, Culture = CultureInfo.InvariantCulture, FloatParseHandling = (FloatParseHandling)0, MissingMemberHandling = (MissingMemberHandling)0 }; public static string Serialize(Fateprint fateprint) { string text = Validate(fateprint); if (text != null) { throw new ArgumentException(text); } return JsonConvert.SerializeObject((object)fateprint, (Formatting)0, Settings); } public static FateprintReadResult Deserialize(string json) { //IL_005a: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) FateprintReadResult fateprintReadResult = new FateprintReadResult { OriginalJson = (json ?? "") }; if (string.IsNullOrWhiteSpace(json)) { fateprintReadResult.Diagnostic = "No Fateprint: equipment remains Unrated."; return fateprintReadResult; } if (json.Length > 131072) { fateprintReadResult.Diagnostic = "Oversized Fateprint disabled; original metadata preserved."; return fateprintReadResult; } try { JObject val = JsonConvert.DeserializeObject<JObject>(json, Settings); if (val == null) { throw new JsonException("Fateprint object is missing."); } int valueOrDefault = ((JToken)val).Value<int?>((object)"SchemaVersion").GetValueOrDefault(); if (valueOrDefault != 1 && valueOrDefault != 2) { throw new JsonException("Unknown Fateprint schema " + valueOrDefault + "."); } if (valueOrDefault == 1) { val["SchemaVersion"] = JToken.op_Implicit(2); JToken obj = val["Perks"]; foreach (JObject item in ((IEnumerable)(((object)((obj is JArray) ? obj : null)) ?? ((object)new JArray()))).OfType<JObject>()) { if (item["DefinitionVersion"] == null) { item["DefinitionVersion"] = JToken.op_Implicit(1); } } if (val["CosmeticSeed"] == null) { val["CosmeticSeed"] = JToken.op_Implicit(0); } if (val["Epithet"] == null) { val["Epithet"] = JToken.op_Implicit(""); } fateprintReadResult.Migrated = true; } Fateprint fateprint = ((JToken)val).ToObject<Fateprint>(JsonSerializer.Create(Settings)); string text = ((fateprint == null) ? "Missing Fateprint object." : Validate(fateprint)); if (text != null) { throw new JsonException(text); } fateprintReadResult.Value = fateprint; fateprintReadResult.Diagnostic = (fateprintReadResult.Migrated ? "Migrated Fateprint schema 1 to 2 without changing rolled values." : ""); } catch (Exception ex) when (ex is JsonException || ex is ArgumentException || ex is InvalidOperationException || ex is FormatException || ex is OverflowException) { fateprintReadResult.Value = null; fateprintReadResult.Diagnostic = "Fateprint effects disabled; base item and original metadata preserved: " + ex.Message; } return fateprintReadResult; } public static string? Validate(Fateprint value) { if (value.SchemaVersion != 2) { return "Unsupported Fateprint schema."; } if (string.IsNullOrWhiteSpace(value.ItemId) || value.ItemId.Length > 256 || string.IsNullOrWhiteSpace(value.TransactionId) || value.TransactionId.Length > 256 || string.IsNullOrWhiteSpace(value.RarityId)) { return "Fateprint item, transaction, and rarity identifiers are required."; } if (value.CrafterId == null || value.CrafterName == null || value.Epithet == null) { return "Fateprint optional text must be an empty string rather than null."; } if (value.Rank < -6 || value.Rank > 12 || value.Perks == null || value.Perks.Count != Math.Abs(value.Rank)) { return "Fateprint rank does not match its exact perk count."; } if (value.Rank == 0 != (value.RarityId == "default")) { return "Only a signed Default outcome may have rank zero and no perks."; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (RolledPerk perk in value.Perks) { if (perk == null || string.IsNullOrWhiteSpace(perk.Id) || !hashSet.Add(perk.Id) || perk.DefinitionVersion < 1 || perk.Values == null || perk.QualityScores == null) { return "Fateprint contains invalid or duplicate perk definitions."; } if (perk.Values.Count == 0 || perk.Values.Count > 32 || perk.Values.Count != perk.QualityScores.Count) { return "Fateprint numerical parameters are incomplete."; } foreach (KeyValuePair<string, double> value3 in perk.Values) { if (string.IsNullOrWhiteSpace(value3.Key) || double.IsNaN(value3.Value) || double.IsInfinity(value3.Value) || !perk.QualityScores.TryGetValue(value3.Key, out var value2) || value2 < 0 || value2 > 10000) { return "Fateprint contains an invalid numerical value or quality score."; } } } return null; } public static Fateprint Clone(Fateprint value) { return Deserialize(Serialize(value)).Value; } } public sealed class TransactionReceipt { public string TransactionId { get; set; } = ""; public string RequestFingerprint { get; set; } = ""; public List<Fateprint> Items { get; set; } = new List<Fateprint>(); } public sealed class CraftTransactionLedger { private readonly object gate = new object(); private readonly Dictionary<string, TransactionReceipt> receipts = new Dictionary<string, TransactionReceipt>(StringComparer.Ordinal); public int Count { get { lock (gate) { return receipts.Count; } } } public CraftTransactionLedger(string? snapshot = null) { if (string.IsNullOrWhiteSpace(snapshot)) { return; } foreach (TransactionReceipt receipt in JsonConvert.DeserializeObject<List<TransactionReceipt>>(snapshot) ?? throw new ArgumentException("Ledger is missing.")) { if (string.IsNullOrWhiteSpace(receipt.TransactionId) || string.IsNullOrWhiteSpace(receipt.RequestFingerprint) || receipt.Items == null || receipt.Items.Count == 0 || receipt.Items.Any((Fateprint f) => FateprintSerializer.Validate(f) != null || f.TransactionId != receipt.TransactionId)) { throw new ArgumentException("Corrupt authority transaction ledger. Do not silently reset it."); } receipts.Add(receipt.TransactionId, receipt); } } public List<Fateprint> GetOrCommit(string transactionId, string fingerprint, Func<List<Fateprint>> generate, Action<string> persistSnapshot) { if (string.IsNullOrWhiteSpace(transactionId) || string.IsNullOrWhiteSpace(fingerprint)) { throw new ArgumentException("Transaction ID and immutable request fingerprint are required."); } lock (gate) { if (receipts.TryGetValue(transactionId, out TransactionReceipt value)) { if (value.RequestFingerprint != fingerprint) { throw new InvalidOperationException("Transaction identifier reused with a different request."); } return value.Items.Select(FateprintSerializer.Clone).ToList(); } List<Fateprint> list = generate(); if (list == null || list.Count == 0 || list.Any((Fateprint f) => FateprintSerializer.Validate(f) != null || f.TransactionId != transactionId) || list.Select((Fateprint f) => f.ItemId).Distinct<string>(StringComparer.Ordinal).Count() != list.Count) { throw new InvalidOperationException("Generated transaction must contain valid, individually identified item outcomes."); } TransactionReceipt transactionReceipt = new TransactionReceipt { TransactionId = transactionId, RequestFingerprint = fingerprint, Items = list.Select(FateprintSerializer.Clone).ToList() }; receipts.Add(transactionId, transactionReceipt); try { persistSnapshot(SnapshotUnsafe()); } catch { receipts.Remove(transactionId); throw; } return transactionReceipt.Items.Select(FateprintSerializer.Clone).ToList(); } } public string Snapshot() { lock (gate) { return SnapshotUnsafe(); } } private string SnapshotUnsafe() { return JsonConvert.SerializeObject((object)receipts.Values.OrderBy<TransactionReceipt, string>((TransactionReceipt r) => r.TransactionId, StringComparer.Ordinal)); } } public interface IRandomSource { int NextInt(int exclusiveUpperBound); } public sealed class CryptoRandomSource : IRandomSource { public int NextInt(int exclusiveUpperBound) { if (exclusiveUpperBound <= 0) { throw new ArgumentOutOfRangeException("exclusiveUpperBound"); } return RandomNumberGenerator.GetInt32(exclusiveUpperBound); } } public sealed class DeterministicRandomSource : IRandomSource { private ulong state; public DeterministicRandomSource(ulong seed) { state = seed; } private ulong Next() { ulong num = (state += 11400714819323198485uL); long num2 = (long)(num ^ (num >> 30)) * -4658895280553007687L; long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L; return (ulong)(num3 ^ (num3 >>> 31)); } public int NextInt(int exclusiveUpperBound) { if (exclusiveUpperBound <= 0) { throw new ArgumentOutOfRangeException("exclusiveUpperBound"); } ulong num = (ulong)exclusiveUpperBound; ulong num2 = (0 - num) % num; ulong num3; do { num3 = Next(); } while (num3 < num2); return (int)(num3 % num); } } public static class WeightedRandom { public static T Select<T>(IReadOnlyList<T> choices, Func<T, int> weight, IRandomSource random) { long num = 0L; foreach (T choice in choices) { int num2 = weight(choice); if (num2 < 0) { throw new ArgumentException("Weights must not be negative."); } num += num2; } if (num <= 0 || num > int.MaxValue) { throw new ArgumentException("Weight total must be positive and fit in a signed integer."); } int num3 = random.NextInt((int)num); foreach (T choice2 in choices) { num3 -= weight(choice2); if (num3 < 0) { return choice2; } } throw new InvalidOperationException("Random source returned an out-of-bounds ticket."); } } public sealed class StrengthSampler { private readonly IReadOnlyList<StrengthBand> bands; private readonly IRandomSource random; public StrengthSampler(IReadOnlyList<StrengthBand> bands, IRandomSource random) { this.bands = bands; this.random = random; } public int SampleScore() { StrengthBand strengthBand = WeightedRandom.Select(bands, (StrengthBand b) => b.Weight, random); return strengthBand.MinScore + random.NextInt(strengthBand.MaxScore - strengthBand.MinScore + 1); } public static double Interpolate(PerkParameter parameter, int score) { if (score < 0 || score > 10000) { throw new ArgumentOutOfRangeException("score"); } double num = (double)score / 10000.0; return Math.Round(parameter.HigherIsBetter ? (parameter.Min + num * (parameter.Max - parameter.Min)) : (parameter.Max - num * (parameter.Max - parameter.Min)), parameter.Precision, MidpointRounding.AwayFromZero); } public RolledPerk Roll(PerkDefinition definition, int rank = 0) { RolledPerk rolledPerk = new RolledPerk { Id = definition.Id, DefinitionVersion = definition.DefinitionVersion }; foreach (PerkParameter parameter in definition.Parameters) { int num = SampleScore(); rolledPerk.QualityScores.Add(parameter.Id, num); rolledPerk.Values.Add(parameter.Id, Interpolate(EffectiveRange.ForRank(parameter, rank), num)); } return rolledPerk; } } public static class Registry { public static readonly string[] Categories = new string[17] { "melee", "bow", "crossbow", "offensive_staff", "support_staff", "shield", "pickaxe", "axe", "hammer", "hoe", "cultivator", "fishing", "helmet", "chest", "legs", "cape", "accessory" }; public static List<PerkDefinition> CreateDefault() { List<PerkDefinition> result = new List<PerkDefinition>(); string cats = "melee,bow,crossbow,offensive_staff,axe,pickaxe"; string cats2 = "pickaxe,axe,hammer,hoe,cultivator,fishing"; S("razor_edge", "Razor Edge", 2.0, 32.0, "+{amount}% physical damage on this weapon.", cats, "physical"); S("runic_potency", "Runic Potency", 2.0, 25.0, "+{amount}% existing elemental damage; added elements are excluded from this baseline.", cats, "elemental"); S("efficient_strikes", "Efficient Strikes", 2.0, 20.0, "-{amount}% attack stamina cost on this weapon.", cats, "attack_stamina"); S("quickstrike", "Quickstrike", 1.0, 12.0, "+{amount}% melee attack animation speed.", "melee,axe,pickaxe"); S("quick_draw", "Quick Draw", 3.0, 20.0, "+{amount}% bow draw speed.", "bow"); S("rapid_reload", "Rapid Reload", 3.0, 18.0, "-{amount}% crossbow reload time.", "crossbow"); S("steady_aim", "Steady Aim", 3.0, 20.0, "-{amount}% aiming stamina cost.", "bow,crossbow", "aim_stamina"); S("eitr_efficiency", "Eitr Efficiency", 2.0, 18.0, "-{amount}% Eitr cost.", "", "eitr"); S("punishing_blows", "Punishing Blows", 3.0, 25.0, "+{amount}% direct damage against staggered enemies.", cats); S("crushing_force", "Crushing Force", 3.0, 25.0, "+{amount}% stagger buildup on direct hits.", cats, "stagger_damage"); S("duelists_guard", "Duelist's Guard", 3.0, 25.0, "+{amount}% item block power.", "", "block"); S("reinforced", "Reinforced", 10.0, 120.0, "+{amount}% maximum item durability.", "", "durability"); S("lightweight", "Lightweight", 5.0, 60.0, "-{amount}% item weight.", "", "weight"); S("frostbound", "Frostbound", 2.0, 15.0, "Add frost damage equal to {amount}% of baseline attack damage, before Nornforged bonuses.", cats); S("emberbound", "Emberbound", 2.0, 15.0, "Add fire damage equal to {amount}% of baseline attack damage, before Nornforged bonuses.", cats); S("resource_breaker", "Resource Breaker", 3.0, 35.0, "+{amount}% native gathering damage; native tool tier is unchanged.", "pickaxe,axe", "gathering"); S("efficient_labor", "Efficient Labor", 3.0, 20.0, "-{amount}% tool action stamina cost.", cats2, "stamina"); S("swift_labor", "Swift Labor", 2.0, 15.0, "+{amount}% working animation, building cadence and fishing reeling speed.", cats2, "working"); S("resource_magnet", "Resource Magnet", 10.0, 100.0, "+{amount}% pickup radius for wood, stone and ore resource materials while this tool is held.", "pickaxe,axe", "gathering"); S("extended_reach", "Extended Reach", 5.0, 25.0, "+{amount}% tool working reach.", cats2, "working"); S("haulers_strength", "Hauler's Strength", 3.0, 15.0, "+{amount}% carrying capacity from the unmodified vanilla baseline."); S("builders_reach", "Builder's Reach", 5.0, 30.0, "+{amount}% building and repair reach.", "hammer"); S("green_hands", "Green Hands", 3.0, 25.0, "-{amount}% planting stamina cost.", "cultivator"); S("patient_angler", "Patient Angler", 3.0, 25.0, "-{amount}% reeling stamina cost.", "fishing"); S("long_cast", "Long Cast", 5.0, 25.0, "+{amount}% horizontal casting distance with the same vertical arc, and maximum fishing line length.", "fishing"); S("fortified", "Fortified", 2.0, 15.0, "+{amount}% armor on this item.", "helmet,chest,legs,cape", "armor"); S("vitality", "Vitality", 2.0, 10.0, "+{amount}% maximum health from vanilla base health plus current food."); S("endurance", "Endurance", 2.0, 10.0, "+{amount}% maximum stamina from vanilla base stamina plus current food."); S("runic_reservoir", "Runic Reservoir", 2.0, 12.0, "+{amount}% maximum Eitr from current food; grants no Eitr without a native Eitr pool.", "", "mage"); S("recovery", "Recovery", 3.0, 20.0, "+{amount}% food and status-effect health regeneration."); S("second_wind", "Second Wind", 3.0, 20.0, "+{amount}% stamina regeneration."); S("runic_flow", "Runic Flow", 3.0, 20.0, "+{amount}% Eitr regeneration.", "", "mage"); S("fleetfoot", "Fleetfoot", 1.0, 5.0, "+{amount}% walking, jogging and running speed while equipped."); S("tireless", "Tireless", 3.0, 15.0, "-{amount}% sprint stamina cost."); S("nimble", "Nimble", 3.0, 15.0, "-{amount}% dodge stamina cost."); S("springstep", "Springstep", 3.0, 15.0, "-{amount}% jump stamina cost."); S("riverstride", "Riverstride", 3.0, 15.0, "-{amount}% swimming stamina cost."); S("quiet_steps", "Quiet Steps", 3.0, 15.0, "-{amount}% sneaking stamina cost."); string[] array = new string[8] { "fire", "frost", "lightning", "poison", "spirit", "blunt", "slash", "pierce" }; foreach (string text in array) { S("ward_" + text, char.ToUpperInvariant(text[0]) + text.Substring(1) + " Ward", 2.0, (text == "fire" || text == "frost") ? 10 : 6, "-{amount}% incoming " + text + " damage after native resistance.").ExcludedNativeEffects.Add(text + "_immunity"); } S("soft_landing", "Soft Landing", 10.0, 60.0, "-{amount}% fall damage.", "legs,cape").ExcludedNativeEffects.Add("fall_immunity"); S("efficient_guard", "Efficient Guard", 3.0, 20.0, "-{amount}% blocking stamina cost.", "shield"); S("firm_grip", "Firm Grip", 5.0, 30.0, "-{amount}% knockback received while blocking.", "shield"); Cool(A("stormspark", "Stormspark", 15.0, 40.0, "A direct hit chains {amount}% of damage actually dealt to one other enemy within 8 m; {cooldown} s cooldown.", cats), 6.0, 12.0); A("executioner", "Executioner", 10.0, 30.0, "+{amount}% direct damage against enemies below 20% health.", cats); A("runic_reserve", "Runic Reserve", 10.0, 30.0, "Every fifth manual spell refunds {amount}% of Eitr actually spent.", "offensive_staff,support_staff", "eitr"); A("veinbreaker", "Veinbreaker", 30.0, 90.0, "Every sixth successful gathering strike deals +{amount}% gathering damage.", "pickaxe,axe", "gathering"); A("natures_favor", "Nature's Favor", 5.0, 20.0, "+{amount}% average yield when eligible natural resources are first produced.", "pickaxe,axe", "gathering"); PerkDefinition perkDefinition = A("master_repair", "Master Repair", 2.0, 5.0, "A successful repair
BepInEx/plugins/Nornforged/Nornforged.dll
Decompiled 6 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.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Nornforged.Audio; using Nornforged.Compatibility; using Nornforged.Core; using Nornforged.Crafting; using Nornforged.Effects; using Nornforged.Items; using Nornforged.Networking; using Nornforged.UI; using Nornforged.Visuals; using TMPro; using UnityEngine; using UnityEngine.EventSystems; 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("Rinor")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Permanent, independently rolled equipment Fateprints for Valheim.")] [assembly: AssemblyFileVersion("0.2.8.0")] [assembly: AssemblyInformationalVersion("0.2.8")] [assembly: AssemblyProduct("Nornforged")] [assembly: AssemblyTitle("Nornforged")] [assembly: AssemblyVersion("0.2.8.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Nornforged { [BepInPlugin("rinor.nornforged", "Nornforged — Fate at the Forge", "0.2.8")] [BepInDependency("com.jotunn.jotunn", "2.30.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "rinor.nornforged"; public const string PluginName = "Nornforged — Fate at the Forge"; public const string PluginVersion = "0.2.8"; private Harmony? _harmony; private NornPresentation? _presentation; private ConfigEntry<float> _intensity; private ConfigEntry<bool> _trails; private ConfigEntry<bool> _particles; private ConfigEntry<bool> _reduced; private ConfigEntry<bool> _crafter; private ConfigEntry<bool> _inventoryAnimation; private ConfigEntry<bool> _sounds; private ConfigEntry<bool> _dropSounds; private ConfigEntry<bool> _perkCount; private ConfigEntry<bool> _perkAnimation; private ConfigEntry<float> _soundVolume; private ZNet? _session; private float _nextDiagnostics; private string _startupConfig = ""; private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings { TypeNameHandling = (TypeNameHandling)0, MaxDepth = 32, MissingMemberHandling = (MissingMemberHandling)1 }; public static Plugin? Current { get; private set; } public static GameplayConfig Settings { get; private set; } = new GameplayConfig { Perks = Registry.CreateDefault() }; public static IReadOnlyList<PerkDefinition> Registry => Settings.Perks; public static FateGenerator Generator { get; private set; } = null; public static string ConfigurationProblem { get; private set; } = ""; public static string SettingsPath => Path.Combine(Paths.ConfigPath, "Nornforged", "gameplay.json"); public static string JournalDirectory => Path.Combine(Paths.ConfigPath, "Nornforged", "authority"); private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Expected O, but got Unknown Current = this; _intensity = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals", "Intensity", 0.8f, new ConfigDescription("Client-only intensity for elemental weapon and armor effects from Mythic (+9). Saved perk quality and percentage determine each element's strength. Zero hides world effects.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _trails = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "Trails", true, "Reserved legacy preference. Elemental effects follow their attachment without world-space trails."); _particles = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "Particles", true, "Show original elemental flames, frost, sparks and mist on equipment from Mythic (+9). Disabling hides all added world effects; inventory icons are unchanged."); _reduced = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "ReducedEffects", false, "Use fewer, slower elemental particles and disable activation pulses."); _crafter = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "ShowCrafter", true, "Show the saved original crafter in F6 details. Compact tooltips retain Valheim's own crafter line without adding a duplicate."); _inventoryAnimation = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "AnimatedRarityBackgrounds", true, "Animate rarity-colored inventory and hotbar backgrounds from Mythic (+9), including Transcendent's aurora, corner runes and orbiting sparks. Epic (+6) through Legendary (+8) backgrounds stay static. Disabling animation retains borders and decorations."); _perkCount = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "ShowPerkCount", true, "Show a small rarity-colored perk count, such as +8, without a badge background at the lower right of inventory and hotbar slots, slightly raised above durability. Normal equipment has no badge."); _perkAnimation = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "AnimatedPerkNames", true, "Gently brighten beneficial perk names with saved PQS of at least 90% in the hover panel; 99% and above receive a stronger sheen. Text never moves or flashes. ReducedEffects also disables this animation."); _sounds = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "RaritySounds", true, "Play original rarity-tier chimes on a newly revealed Fateprint."); _dropSounds = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "GroundDropSounds", true, "Play a positional rarity sound when rolled equipment is actually dropped. Loading stored ground items is silent."); _soundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "Volume", 0.45f, new ConfigDescription("Rarity sound volume, also respecting the game's audio mixer.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); Directory.CreateDirectory(Path.GetDirectoryName(SettingsPath)); try { if (!File.Exists(SettingsPath)) { File.WriteAllText(SettingsPath, JsonConvert.SerializeObject((object)Settings, (Formatting)1)); } _startupConfig = File.ReadAllText(SettingsPath); GameplayConfigUpgradeResult val = GameplayConfigUpgrade.UpgradeUntouchedDefaults(_startupConfig); if (val.Upgraded) { string text = SettingsPath + "." + Guid.NewGuid().ToString("N") + ".tmp"; string text2 = SettingsPath + "." + val.FromVersion + "." + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + ".bak"; File.WriteAllText(text, val.Json); File.Replace(text, SettingsPath, text2); _startupConfig = val.Json; ((BaseUnityPlugin)this).Logger.LogInfo((object)(val.Diagnostic + " Backup: " + text2)); } else if (val.FromVersion.Length == 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)val.Diagnostic); } ApplyConfiguration(_startupConfig); } catch (Exception ex) { ConfigurationProblem = "Invalid Nornforged gameplay configuration: " + ex.Message; LogError(ConfigurationProblem); } NetworkAuthority.Initialize("0.2.8", JournalDirectory, () => Settings, ApplyConfiguration, LogWarning); CraftingService.Initialize(delegate(ItemData item) { FateUi.Reveal(item); if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { EffectRuntime.Invalidate(Player.m_localPlayer); } }); EffectRuntime.Activated = (Action<Player, string>)Delegate.Combine(EffectRuntime.Activated, new Action<Player, string>(FateVisuals.Activate)); _harmony = new Harmony("rinor.nornforged"); try { _harmony.PatchAll(typeof(Plugin).Assembly); InventoryCompatibility.Install(_harmony, LogInfo); ToolModInterop.Install(_harmony); } catch (Exception ex2) { ConfigurationProblem = "Nornforged game hooks did not initialize: " + ex2.Message; LogError(ex2.ToString()); throw; } RegisterCommands(); _presentation = new NornPresentation(((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Nornforged — Fate at the Forge 0.2.8. Target: Valheim 1.0.15, BepInEx 5.4.23.5, Jotunn 2.30.0. F6 Fateprint; F9 damage ledger."); } public static void ApplyConfiguration(string json) { //IL_0554: Unknown result type (might be due to invalid IL or missing references) //IL_0564: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) if (json.Length > 2097152) { throw new InvalidDataException("Gameplay configuration exceeds 2 MiB."); } GameplayConfig val = JsonConvert.DeserializeObject<GameplayConfig>(json, JsonSettings) ?? throw new InvalidDataException("Missing configuration object."); List<string> list = ConfigurationValidator.Validate(val, (IEnumerable<ItemProfile>)Registry.ValidationProfiles(), true); if (list.Count > 0) { throw new InvalidDataException(string.Join("\n", list)); } Dictionary<string, PerkDefinition> dictionary = Registry.CreateDefault().ToDictionary<PerkDefinition, string>((PerkDefinition d) => d.Id, StringComparer.Ordinal); foreach (PerkDefinition perk in val.Perks) { if (!dictionary.TryGetValue(perk.Id, out var value) || perk.Effect != value.Effect || perk.DefinitionVersion != value.DefinitionVersion) { list.Add("Unsupported effect ID or definition version: " + perk.Id); continue; } if (!(from val4 in perk.Parameters select val4.Id into x orderby x select x).SequenceEqual(from val4 in value.Parameters select val4.Id into x orderby x select x)) { list.Add("Effect parameters cannot be renamed or removed: " + perk.Id); } if (perk.Class != value.Class) { list.Add("A perk's execution class cannot be changed: " + perk.Id); } foreach (PerkParameter p in perk.Parameters) { PerkParameter val2 = ((IEnumerable<PerkParameter>)value.Parameters).FirstOrDefault((Func<PerkParameter, bool>)((PerkParameter x) => x.Id == p.Id)); if (p.Min < 0.0 || p.Max > (double)((p.Unit == "s") ? 86400 : ((p.Unit == "m") ? 32 : ((p.Unit == "%") ? 3000 : 1000)))) { list.Add("Parameter outside supported safety bounds: " + perk.Id + "." + p.Id); } if (val2 != null && (p.Unit != val2.Unit || p.HigherIsBetter != val2.HigherIsBetter)) { list.Add("Parameter units/direction are execution contracts: " + perk.Id + "." + p.Id); } if (((p.Id == "chance" || perk.Id == "aegirs_favor" || perk.Id == "bait_keeper" || perk.Id == "hooked_fortune") && p.Max > 100.0) || (perk.Id == "eternal_temper" && p.Max > 99.0)) { list.Add("Trigger chance exceeds the fixed safety limit: " + perk.Id); } } if (value.RequiredCapabilities.Except(perk.RequiredCapabilities).Any() || value.ExcludedNativeEffects.Except(perk.ExcludedNativeEffects).Any() || value.ExclusionGroups.Except(perk.ExclusionGroups).Any() || value.IncompatibleIds.Except(perk.IncompatibleIds).Any()) { list.Add("Required compatibility or exclusion rule removed: " + perk.Id); } if (value.Categories.Count > 0 && (perk.Categories.Count == 0 || perk.Categories.Except(value.Categories).Any())) { list.Add("Unsupported category expansion: " + perk.Id); } } foreach (ItemOverride itemOverride in val.ItemOverrides) { if (!Registry.Categories.Contains(itemOverride.Category) && itemOverride.Enabled) { list.Add("Unknown item override category: " + itemOverride.Category); } } if (list.Count > 0) { throw new InvalidDataException(string.Join("\n", list)); } FateGenerator val3 = new FateGenerator(val, (IRandomSource)null, (IRandomSource)null, (IRandomSource)null, (IRandomSource)null); Settings = val; Generator = val3; ConfigurationProblem = ""; Classification.ConfigProvider = () => Settings; if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { EffectRuntime.Invalidate(Player.m_localPlayer); } } private void Update() { if ((Object)(object)_session != (Object)(object)ZNet.instance) { _session = ZNet.instance; if (Object.op_Implicit((Object)(object)_session) && _session.IsServer()) { try { ApplyConfiguration(_startupConfig); } catch (Exception ex) { ConfigurationProblem = ex.Message; } } } NetworkAuthority.Update(); CraftingService.Update(); _presentation?.Tick(); if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsDedicated()) { FateHoverController.Tick(blocked: true); return; } FateVisuals.Intensity = _intensity.Value; FateVisuals.Trails = _trails.Value; FateVisuals.Particles = _particles.Value; FateVisuals.Reduced = _reduced.Value; FateIcon.AnimatedBackgrounds = _inventoryAnimation.Value; FateIcon.ReducedMotion = _reduced.Value; FateIcon.ShowPerkCount = _perkCount.Value; FateHoverController.AnimatedQuality = _perkAnimation.Value; FateAudio.Enabled = _sounds.Value; FateAudio.GroundDrops = _dropSounds.Value; FateAudio.Volume = _soundVolume.Value; FateUi.ShowCrafter = _crafter.Value; NornPresentation? presentation = _presentation; if (presentation == null || !presentation.IsOpen) { FateUi.Tick(); } else { FateUi.Close(); } FateHoverController.Tick(_presentation?.IsOpen ?? false); if (ConfigurationProblem.Length > 0 && Object.op_Implicit((Object)(object)Player.m_localPlayer) && Time.unscaledTime > _nextDiagnostics) { _nextDiagnostics = Time.unscaledTime + 30f; ((Character)Player.m_localPlayer).Message((MessageType)1, ConfigurationProblem, 0, (Sprite)null, false); } } private void OnGUI() { if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated()) { FateUi.Draw(); } } private void OnDestroy() { _presentation?.Dispose(); FateUi.Close(); FateHoverController.Shutdown(); FateIcon.Shutdown(); FateAudio.Shutdown(); EffectRuntime.Activated = (Action<Player, string>)Delegate.Remove(EffectRuntime.Activated, new Action<Player, string>(FateVisuals.Activate)); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } FateVisuals.Shutdown(); Current = null; } private void RegisterCommands() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand("nornforged", "Nornforged: status | inspect | validate | simulate [count] | damage | welcome | recover. No command awards equipment.", (ConsoleEvent)delegate(ConsoleEventArgs args) { //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Expected O, but got Unknown switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "status") { case "recover": args.Context.AddString(CraftingService.RecoverLegacyPending()); break; case "welcome": _presentation?.RequestWelcome(); args.Context.AddString("The Nornforged guide opens in the current world."); break; case "damage": _presentation?.OpenDetails(); break; case "status": args.Context.AddString(string.Format("Nornforged {0}; authority ready={1}; definitions={2}; {3}", "0.2.8", NetworkAuthority.Ready, Registry.Count, ConfigurationProblem)); break; case "inspect": { Player localPlayer = Player.m_localPlayer; ItemData val2 = FateUi.Hovered ?? (Object.op_Implicit((Object)(object)localPlayer) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null); if (val2 == null) { args.Context.AddString("Hover equipment in an inventory or equip it first."); } else { Fateprint val3 = ItemFate.Read(val2); args.Context.AddString((val3 != null) ? FateUi.Describe(val3, expanded: true) : (ItemFate.HasMetadata(val2) ? "Fateprint inactive or pending; original metadata has been preserved." : "Unrated")); } break; } case "validate": { List<string> list = ConfigurationValidator.Validate(Settings, (IEnumerable<ItemProfile>)Registry.ValidationProfiles(), true); if (Object.op_Implicit((Object)(object)ObjectDB.instance)) { foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item)) { ItemProfile val4 = Classification.Describe(recipe.m_item.m_itemData); if (val4 != null) { list.AddRange(Generator.Validate(val4)); } } } } args.Context.AddString((list.Count == 0) ? "All configured and currently loaded craftable equipment pools validate." : string.Join("\n", list)); break; } case "simulate": { int result = 10000; if (args.Length > 2 && !int.TryParse(args[2], out result)) { result = 10000; } result = Math.Max(1, Math.Min(1000000, result)); DeterministicRandomSource val = new DeterministicRandomSource(1313821262uL); Dictionary<string, int> dictionary = Settings.Rarities.ToDictionary((RarityDefinition r) => r.Id, (RarityDefinition _) => 0); for (int num = 0; num < result; num++) { dictionary[WeightedRandom.Select<RarityDefinition>((IReadOnlyList<RarityDefinition>)Settings.Rarities, (Func<RarityDefinition, int>)((RarityDefinition r) => r.Weight), (IRandomSource)(object)val).Id]++; } args.Context.AddString("Isolated deterministic rarity simulation; does not consume gameplay RNG:"); { foreach (RarityDefinition rarity in Settings.Rarities) { args.Context.AddString($"{rarity.Name}: {dictionary[rarity.Id]}/{result} = {100.0 * (double)dictionary[rarity.Id] / (double)result:F4}% (configured {(double)rarity.Weight / 100.0:F2}%)"); } break; } } default: args.Context.AddString("Use nornforged status, inspect, validate, simulate [1..1000000], damage, welcome, or recover."); break; } }, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } public static void LogInfo(string message) { Plugin? current = Current; if (current != null) { ((BaseUnityPlugin)current).Logger.LogInfo((object)message); } } public static void LogWarning(string message) { Plugin? current = Current; if (current != null) { ((BaseUnityPlugin)current).Logger.LogWarning((object)message); } } public static void LogError(string message) { Plugin? current = Current; if (current != null) { ((BaseUnityPlugin)current).Logger.LogError((object)message); } } } } namespace Nornforged.Visuals { public sealed class ElementalVfx : MonoBehaviour { private sealed class Channel { internal int Element; internal bool Accent; internal ParticleSystem System; internal ParticleSystemRenderer Renderer; internal Particle[] Particles = Array.Empty<Particle>(); } private static readonly Dictionary<int, Material> Materials = new Dictionary<int, Material>(); private static readonly Dictionary<int, Texture2D> Textures = new Dictionary<int, Texture2D>(); private static readonly HashSet<ElementalVfx> Live = new HashSet<ElementalVfx>(); private static Shader? shader; private static readonly float[] RankPower = new float[4] { 1f, 1.32f, 1.72f, 2.2f }; private readonly List<Channel> channels = new List<Channel>(); private readonly List<ParticleSystem> systems = new List<ParticleSystem>(); private Vector3[] anchors = Array.Empty<Vector3>(); private FateVisualStyle style = new FateVisualStyle(); private float scale = 1f; private bool armor; private bool visible = true; private bool configured; private bool reduced; private int rank; private int seed; private int budget; private int setKey; private float pulseUntil; private float nextAnimation; private string signature = ""; public bool HasVisual { get { if (configured) { return channels.Count > 0; } return false; } } public int ParticleCapacity => channels.Sum((Channel c) => c.Particles.Length); public int ChannelCount => channels.Select((Channel c) => c.Element).Distinct().Count(); public IReadOnlyList<ParticleSystem> Systems => systems; public IReadOnlyList<Vector3> Anchors => anchors; public float EffectivePower { get { if (!configured || rank < 9) { return 0f; } float num = ((style.ActiveElements.Count == 0) ? 0.48f : ((IEnumerable<int>)style.ActiveElements).Max((Func<int, float>)style.ElementStrength)); float num2 = (FateVisuals.Reduced ? 0f : (Mathf.SmoothStep(0f, 1f, Mathf.Clamp01((pulseUntil - Time.unscaledTime) / 0.65f)) * 0.2f)); return RankPower[Mathf.Clamp(rank - 9, 0, 3)] * Mathf.Clamp01(FateVisuals.Intensity) * (0.24f + 0.76f * num) * (1f + num2); } } public static int SharedMaterialCount => Materials.Count; public static int SharedTextureCount => Textures.Count; public static string SelectedShader { get { if (!Object.op_Implicit((Object)(object)shader)) { return "unavailable"; } return ((Object)shader).name; } } public static bool IsOwned(Renderer renderer) { if (Object.op_Implicit((Object)(object)renderer)) { return Object.op_Implicit((Object)(object)((Component)renderer).GetComponent<ElementalVfxMarker>()); } return false; } public void SetBudget(int particles) { budget = Mathf.Clamp(particles, 8, 150); } public void Configure(int rank, int seed, FateVisualStyle style, Vector3[] anchors, float scale, bool armor, int? setKey = null) { if (rank < 9 || FateVisuals.Intensity <= 0f || !FateVisuals.Particles || !FateVisuals.Available) { ClearVisual(); return; } this.rank = Mathf.Clamp(rank, 9, 12); this.seed = seed; this.style = style; this.scale = Mathf.Clamp(scale, 0.06f, 3f); this.armor = armor; this.setKey = setKey ?? seed; string text = this.rank + ":" + seed + ":" + style.Signature + ":" + armor + ":" + budget + ":" + FateVisuals.Reduced; UpdateAnchors(anchors); if (configured && signature == text) { SetVisible(value: true); return; } ClearVisual(); if (!EnsureShader()) { return; } signature = text; configured = true; reduced = FateVisuals.Reduced; visible = true; Live.Add(this); int[] array = ((style.ActiveElements.Count > 0) ? style.ActiveElements.ToArray() : new int[1] { -1 }); int num = ((budget > 0) ? budget : (armor ? 40 : 140)); if (reduced) { num = Math.Min(num, armor ? 14 : 28); } float num2 = ((array[0] < 0) ? 0.48f : ((IEnumerable<int>)array).Average((Func<int, float>)style.ElementStrength)); int num3 = Mathf.Clamp(Mathf.RoundToInt((float)num * (0.6f + 0.4f * (float)(this.rank - 9) / 3f) * (0.7f + 0.3f * num2)), array.Length * 2, num); int num4 = Math.Max(2, num3 / array.Length); for (int i = 0; i < array.Length; i++) { int num5 = num4 + ((i < num3 % array.Length) ? 1 : 0); int num6 = ((!reduced) ? Math.Max(1, num5 / ((this.rank >= 11) ? 3 : 4)) : 0); AddChannel(array[i], accent: false, num5 - num6); if (num6 > 0) { AddChannel(array[i], accent: true, num6); } } ((Behaviour)this).enabled = true; AnimatePreview(Time.unscaledTime); } public void UpdateAnchors(Vector3[] positions) { //IL_0010: 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) //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_0047: 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) anchors = (Vector3[])((positions != null && positions.Length != 0) ? ((Array)positions) : ((Array)new Vector3[1] { Vector3.zero })); Bounds localBounds = default(Bounds); ((Bounds)(ref localBounds))..ctor(anchors[0], Vector3.zero); for (int i = 1; i < anchors.Length; i++) { ((Bounds)(ref localBounds)).Encapsulate(anchors[i]); } ((Bounds)(ref localBounds)).Expand(Mathf.Max(0.35f, scale * 2.8f)); foreach (Channel channel in channels) { if (Object.op_Implicit((Object)(object)channel.Renderer)) { ((Renderer)channel.Renderer).localBounds = localBounds; } } } private static bool EnsureShader() { if (Object.op_Implicit((Object)(object)shader)) { return true; } Shader[] source = Resources.FindObjectsOfTypeAll<Shader>(); string[] array = new string[4] { "Particles/Standard Unlit", "Particles/Standard Unlit2", "Legacy Shaders/Particles/Additive", "Particles/Additive" }; foreach (string name in array) { Shader val = Shader.Find(name); if (!Object.op_Implicit((Object)(object)val) || !val.isSupported) { val = ((IEnumerable<Shader>)source).FirstOrDefault((Func<Shader, bool>)((Shader s) => Object.op_Implicit((Object)(object)s) && s.isSupported && ((Object)s).name == name)); } if (Object.op_Implicit((Object)(object)val) && val.isSupported) { shader = val; return true; } } return false; } private static Material MaterialFor(int element, bool accent) { //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_005e: 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_0071: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) int key = ((element < 0) ? 5 : element) * 2 + (accent ? 1 : 0); if (Materials.TryGetValue(key, out Material value) && Object.op_Implicit((Object)(object)value)) { return value; } Texture2D val = TextureFor(element, accent); Textures[key] = val; value = new Material(shader) { name = "Nornforged elemental " + key, mainTexture = (Texture)(object)val, renderQueue = 3002 }; value.shaderKeywords = Array.Empty<string>(); Set(value, "_SrcBlend", 5f); Set(value, "_DstBlend", 1f); Set(value, "_BlendOp", 0f); Set(value, "_ZWrite", 0f); Set(value, "_ZTest", 4f); Set(value, "_Cull", 0f); Set(value, "_LightingEnabled", 0f); Set(value, "_SoftParticlesEnabled", 0f); Set(value, "_CameraFadingEnabled", 0f); Set(value, "_Mode", 4f); Set(value, "_InvFade", 1f); Set(value, "_DistortionEnabled", 0f); if (value.HasProperty("_TintColor")) { value.SetColor("_TintColor", new Color(1.65f, 1.65f, 1.65f, 1f)); } if (value.HasProperty("_Color")) { value.SetColor("_Color", new Color(1.65f, 1.65f, 1.65f, 1f)); } value.DisableKeyword("SOFTPARTICLES_ON"); value.DisableKeyword("_FADING_ON"); value.DisableKeyword("_ALPHATEST_ON"); value.DisableKeyword("_ALPHAPREMULTIPLY_ON"); value.EnableKeyword("_ALPHABLEND_ON"); value.SetOverrideTag("RenderType", "Transparent"); Materials[key] = value; return value; } private static void Set(Material material, string property, float value) { if (material.HasProperty(property)) { material.SetFloat(property, value); } } private static Texture2D TextureFor(int element, bool accent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(64, 96, (TextureFormat)4, false) { name = "Nornforged original " + element + (accent ? " sparks" : " plume"), wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; Color[] array = (Color[])(object)new Color[6144]; for (int i = 0; i < 96; i++) { float num3; float num8; for (int j = 0; j < 64; array[i * 64 + j] = new Color(1f, num3, num3, Mathf.Clamp01(num8)), j++) { float num = ((float)j + 0.5f) / 64f * 2f - 1f; float num2 = ((float)i + 0.5f) / 96f; num3 = 1f; if (element == 0 && !accent) { float num4 = 0.22f * Mathf.Sin(num2 * 5.5f) * num2; float num5 = 0.44f * Mathf.Pow(1f - num2, 0.8f) + 0.022f; float num6 = Mathf.Exp((0f - Mathf.Pow((num - num4) / num5, 2f)) * 2.1f); float num7 = Mathf.Exp((0f - Mathf.Pow((num + 0.2f - 0.4f * num2) / (num5 * 0.45f), 2f)) * 2.5f) * Mathf.Sin(num2 * MathF.PI); num8 = Mathf.Clamp01(num6 + num7 * 0.42f) * Mathf.Sin(Mathf.Clamp01(num2 / 0.17f) * MathF.PI / 2f) * Mathf.Pow(1f - num2, 0.7f); num3 = 0.7f + 0.3f * Mathf.Clamp01(num6 - num2 * 0.7f); continue; } if (element == 1 && accent) { float num9 = (num2 - 0.5f) * 2f; float num10 = Mathf.Sqrt(num * num + num9 * num9); float num11 = Mathf.Pow(Mathf.Abs(Mathf.Cos(Mathf.Atan2(num9, num) * 3f)), 28f) * Mathf.Pow(1f - Mathf.Clamp01(num10), 0.7f); float num12 = Mathf.Exp(0f - Mathf.Pow((num10 - 0.36f) / 0.035f, 2f)) * 0.45f; num8 = Mathf.Clamp01(num11 + num12) * Mathf.Clamp01((1f - num10) * 8f); continue; } int num13; switch (element) { case 2: { float num14 = 0.3f * Mathf.Sin(num2 * 19f) + 0.1f * Mathf.Sin(num2 * 47f); float num15 = Mathf.Exp(0f - Mathf.Pow((num - num14) / (accent ? 0.045f : 0.09f), 2f)); float num16 = Mathf.Exp(0f - Mathf.Pow((num - num14) / 0.3f, 2f)) * 0.18f; num8 = (num15 + num16) * Mathf.Pow(Mathf.Sin(num2 * MathF.PI), 0.7f); continue; } default: num13 = ((element < 0) ? 1 : 0); break; case 4: num13 = 1; break; } if (((uint)num13 & (accent ? 1u : 0u)) != 0) { float num17 = (num2 - 0.5f) * 2f; float num18 = Mathf.Sqrt(num * num + num17 * num17); num8 = Mathf.Exp(0f - Mathf.Pow((Mathf.Abs(num) + Mathf.Abs(num17) - 0.54f) / 0.07f, 2f)) * 0.85f + Mathf.Exp((0f - num18) * num18 * 22f) * 0.35f; continue; } float num19 = (num2 - 0.5f) * 2f; float num20 = num * num + num19 * num19; num8 = Mathf.Exp((0f - num20) * (accent ? 15f : ((element == 1) ? 5.5f : 7.5f))); num8 *= Mathf.Clamp01((1f - num20) * 4f); if (element == 3 && accent) { num8 *= 0.65f + 0.35f * Mathf.Pow(Mathf.Sin(Mathf.Sqrt(num20) * 9f), 2f); } } } val.SetPixels(array); val.Apply(false, true); return val; } private void AddChannel(int element, bool accent, int count) { //IL_006c: 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_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_00a4: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: 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_0164: Unknown result type (might be due to invalid IL or missing references) if (count > 0) { GameObject val = new GameObject("Nornforged " + ((element < 0) ? "fate" : (new string[5] { "fire", "frost", "storm", "venom", "spirit" })[element]) + (accent ? " accents" : " mantle"), new Type[1] { typeof(ElementalVfxMarker) }); val.transform.SetParent(((Component)this).transform, false); val.layer = ((Component)this).gameObject.layer; ParticleSystem val2 = val.AddComponent<ParticleSystem>(); val2.Stop(true, (ParticleSystemStopBehavior)0); MainModule main = val2.main; ((MainModule)(ref main)).playOnAwake = false; ((MainModule)(ref main)).loop = false; ((MainModule)(ref main)).duration = 5f; ((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(5f); ((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(0f); ((MainModule)(ref main)).maxParticles = count; ((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)0; ((MainModule)(ref main)).scalingMode = (ParticleSystemScalingMode)0; ((MainModule)(ref main)).startSize3D = true; ((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)3; EmissionModule emission = val2.emission; ((EmissionModule)(ref emission)).enabled = false; ShapeModule shape = val2.shape; ((ShapeModule)(ref shape)).enabled = false; ColorOverLifetimeModule colorOverLifetime = val2.colorOverLifetime; ((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = false; SizeOverLifetimeModule sizeOverLifetime = val2.sizeOverLifetime; ((SizeOverLifetimeModule)(ref sizeOverLifetime)).enabled = false; VelocityOverLifetimeModule velocityOverLifetime = val2.velocityOverLifetime; ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).enabled = false; NoiseModule noise = val2.noise; ((NoiseModule)(ref noise)).enabled = false; ParticleSystemRenderer component = val.GetComponent<ParticleSystemRenderer>(); ((Renderer)component).sharedMaterial = MaterialFor(element, accent); component.renderMode = (ParticleSystemRenderMode)0; component.alignment = (ParticleSystemRenderSpace)0; component.sortMode = (ParticleSystemSortMode)0; ((Renderer)component).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)component).receiveShadows = false; ((Renderer)component).lightProbeUsage = (LightProbeUsage)0; ((Renderer)component).reflectionProbeUsage = (ReflectionProbeUsage)0; ((Renderer)component).motionVectorGenerationMode = (MotionVectorGenerationMode)2; component.minParticleSize = 0f; component.maxParticleSize = 0.25f; ((Renderer)component).allowOcclusionWhenDynamic = false; Channel item = new Channel { Element = element, Accent = accent, System = val2, Renderer = component, Particles = (Particle[])(object)new Particle[count] }; channels.Add(item); systems.Add(val2); UpdateAnchors(anchors); val2.Play(false); val2.Pause(false); } } private static float Unit(int number) { int num = (number ^ (number >>> 16)) * 2146121005; int num2 = (num ^ (num >>> 15)) * -2073254261; return (float)(uint)((num2 ^ (num2 >>> 16)) & 0xFFFFFF) / 16777216f; } public void AnimatePreview(float time) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0044: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_0072: 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_0061: 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_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_034b: 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_0398: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04b5: Unknown result type (might be due to invalid IL or missing references) //IL_0563: Unknown result type (might be due to invalid IL or missing references) //IL_0565: Unknown result type (might be due to invalid IL or missing references) //IL_0569: Unknown result type (might be due to invalid IL or missing references) //IL_0573: Unknown result type (might be due to invalid IL or missing references) //IL_0578: Unknown result type (might be due to invalid IL or missing references) //IL_0581: Unknown result type (might be due to invalid IL or missing references) //IL_058b: Unknown result type (might be due to invalid IL or missing references) //IL_0592: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Unknown result type (might be due to invalid IL or missing references) //IL_059c: Unknown result type (might be due to invalid IL or missing references) //IL_05a1: Unknown result type (might be due to invalid IL or missing references) //IL_05ef: Unknown result type (might be due to invalid IL or missing references) //IL_05f1: Unknown result type (might be due to invalid IL or missing references) //IL_05f5: Unknown result type (might be due to invalid IL or missing references) //IL_05ff: Unknown result type (might be due to invalid IL or missing references) //IL_0604: Unknown result type (might be due to invalid IL or missing references) //IL_0607: Unknown result type (might be due to invalid IL or missing references) //IL_0611: Unknown result type (might be due to invalid IL or missing references) //IL_0618: Unknown result type (might be due to invalid IL or missing references) //IL_061d: Unknown result type (might be due to invalid IL or missing references) //IL_0622: Unknown result type (might be due to invalid IL or missing references) //IL_0627: Unknown result type (might be due to invalid IL or missing references) //IL_0665: Unknown result type (might be due to invalid IL or missing references) //IL_0667: Unknown result type (might be due to invalid IL or missing references) //IL_066b: Unknown result type (might be due to invalid IL or missing references) //IL_0677: Unknown result type (might be due to invalid IL or missing references) //IL_067c: Unknown result type (might be due to invalid IL or missing references) //IL_0685: Unknown result type (might be due to invalid IL or missing references) //IL_068f: Unknown result type (might be due to invalid IL or missing references) //IL_0696: Unknown result type (might be due to invalid IL or missing references) //IL_069b: Unknown result type (might be due to invalid IL or missing references) //IL_06a0: Unknown result type (might be due to invalid IL or missing references) //IL_06a5: 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_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_0408: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_043a: Unknown result type (might be due to invalid IL or missing references) //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_04fb: Unknown result type (might be due to invalid IL or missing references) //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_0507: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Unknown result type (might be due to invalid IL or missing references) //IL_0511: Unknown result type (might be due to invalid IL or missing references) //IL_0905: Unknown result type (might be due to invalid IL or missing references) //IL_0926: Unknown result type (might be due to invalid IL or missing references) //IL_0910: Unknown result type (might be due to invalid IL or missing references) //IL_07dd: Unknown result type (might be due to invalid IL or missing references) //IL_07df: Unknown result type (might be due to invalid IL or missing references) //IL_07e7: Unknown result type (might be due to invalid IL or missing references) //IL_07ec: Unknown result type (might be due to invalid IL or missing references) //IL_07f4: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Unknown result type (might be due to invalid IL or missing references) //IL_0803: Unknown result type (might be due to invalid IL or missing references) //IL_080d: Unknown result type (might be due to invalid IL or missing references) //IL_0814: Unknown result type (might be due to invalid IL or missing references) //IL_0819: Unknown result type (might be due to invalid IL or missing references) //IL_081e: Unknown result type (might be due to invalid IL or missing references) //IL_073c: Unknown result type (might be due to invalid IL or missing references) //IL_073e: Unknown result type (might be due to invalid IL or missing references) //IL_0746: Unknown result type (might be due to invalid IL or missing references) //IL_074b: Unknown result type (might be due to invalid IL or missing references) //IL_0753: Unknown result type (might be due to invalid IL or missing references) //IL_0758: Unknown result type (might be due to invalid IL or missing references) //IL_075f: Unknown result type (might be due to invalid IL or missing references) //IL_0769: Unknown result type (might be due to invalid IL or missing references) //IL_0770: Unknown result type (might be due to invalid IL or missing references) //IL_0775: Unknown result type (might be due to invalid IL or missing references) //IL_077a: Unknown result type (might be due to invalid IL or missing references) //IL_0948: Unknown result type (might be due to invalid IL or missing references) //IL_094d: Unknown result type (might be due to invalid IL or missing references) //IL_087f: Unknown result type (might be due to invalid IL or missing references) //IL_0881: Unknown result type (might be due to invalid IL or missing references) //IL_0889: Unknown result type (might be due to invalid IL or missing references) //IL_088e: Unknown result type (might be due to invalid IL or missing references) //IL_0896: Unknown result type (might be due to invalid IL or missing references) //IL_089b: Unknown result type (might be due to invalid IL or missing references) //IL_08a2: Unknown result type (might be due to invalid IL or missing references) //IL_08a9: Unknown result type (might be due to invalid IL or missing references) //IL_08b0: Unknown result type (might be due to invalid IL or missing references) //IL_08b5: Unknown result type (might be due to invalid IL or missing references) //IL_08ba: Unknown result type (might be due to invalid IL or missing references) //IL_098e: Unknown result type (might be due to invalid IL or missing references) //IL_0996: Unknown result type (might be due to invalid IL or missing references) //IL_099f: Unknown result type (might be due to invalid IL or missing references) //IL_09ab: Unknown result type (might be due to invalid IL or missing references) //IL_09ad: Unknown result type (might be due to invalid IL or missing references) //IL_09bf: Unknown result type (might be due to invalid IL or missing references) //IL_09f5: Unknown result type (might be due to invalid IL or missing references) //IL_09f7: Unknown result type (might be due to invalid IL or missing references) if (!configured) { return; } if (FateVisuals.Intensity <= 0f || !FateVisuals.Particles) { SetVisible(value: false); return; } Vector3 val = ((Component)this).transform.InverseTransformDirection(Vector3.up); Vector3 normalized = ((Vector3)(ref val)).normalized; val = Vector3.Cross(normalized, Vector3.forward); Vector3 val2 = ((Vector3)(ref val)).normalized; if (((Vector3)(ref val2)).sqrMagnitude < 0.1f) { val2 = Vector3.right; } val = Vector3.Cross(normalized, val2); Vector3 normalized2 = ((Vector3)(ref val)).normalized; float num = (float)(rank - 9) / 3f; foreach (Channel channel in channels) { if (!Object.op_Implicit((Object)(object)channel.System) || !Object.op_Implicit((Object)(object)channel.Renderer)) { continue; } ((Component)channel.Renderer).gameObject.layer = ((Component)this).gameObject.layer; ((Renderer)channel.Renderer).enabled = visible && ((Behaviour)this).isActiveAndEnabled; float num2 = ((channel.Element < 0) ? 0.48f : style.ElementStrength(channel.Element)); float num3 = (0.24f + 0.76f * num2) * RankPower[rank - 9] * Mathf.Clamp01(FateVisuals.Intensity); float num4 = (0.65f + 0.65f * num2) * (1f + num * 0.48f) * scale; if (armor) { num4 *= 0.8f; } Color val3 = ((channel.Element < 0) ? style.ColorAt(0.45f, rank) : style.ElementColor(channel.Element)); float num5 = (reduced ? 1f : (1f + Mathf.SmoothStep(0f, 1f, Mathf.Clamp01((pulseUntil - Time.unscaledTime) / 0.65f)) * 0.2f)); float num6 = time + (float)(setKey & 0x3FF) * 0.017f; int num7 = channel.Particles.Length; for (int i = 0; i < num7; i++) { int num8 = seed + i * 199 + channel.Element * 9281 + (channel.Accent ? 31 : 0); float num9 = Unit(num8); float num10 = Unit(num8 + 71); float num11 = Unit(num8 + 127); float num12 = ((channel.Element == 0) ? (0.58f + num10 * 0.28f) : ((channel.Element == 1) ? (1.1f + num10 * 0.45f) : (0.85f + num10 * 0.5f))); if (reduced) { num12 *= 1.7f; } float num13 = Mathf.Repeat(num6 / num12 + ((float)i + 0.5f) / (float)num7, 1f); float num14 = Mathf.SmoothStep(0f, 1f, num13 / 0.18f) * Mathf.SmoothStep(0f, 1f, (1f - num13) / 0.48f); int num15 = Mathf.Clamp((int)(num9 * (float)anchors.Length), 0, anchors.Length - 1); Vector3 val4 = anchors[num15]; float num16 = num10 * MathF.PI * 2f + num6 * (reduced ? 0.12f : (0.35f + num * 0.3f)); float num17 = (0.018f + 0.055f * num13) * num4; Vector3 val5 = val2 * Mathf.Cos(num16) + normalized2 * Mathf.Sin(num16); float num18 = 0f; float num19; float num20; switch (channel.Element) { case 0: { float num21 = (channel.Accent ? 0.46f : 0.2f) * num13 * num4; val4 += normalized * num21 + val5 * num17 + val2 * Mathf.Sin(num13 * 5f + num10 * 6f) * 0.016f * num4; num19 = (channel.Accent ? 0.023f : 0.11f) * num4 * (0.7f + num11 * 0.55f); num20 = (channel.Accent ? (num19 * 1.6f) : (num19 * (2.6f + num13 * 1.9f))); num18 = (num10 - 0.5f) * 22f; break; } case 1: val4 += val5 * num17 * (channel.Accent ? 1.35f : 0.7f) + normalized * (channel.Accent ? (0.025f * Mathf.Sin(num16)) : (0.025f - num13 * 0.1f)) * num4; num19 = (channel.Accent ? 0.077f : 0.15f) * num4 * (0.75f + num11 * 0.5f); num20 = num19; num18 = num10 * 360f + num13 * (float)(channel.Accent ? 80 : 12); break; case 2: val4 += val5 * num17 * 1.25f + normalized * (num13 - 0.5f) * 0.05f * num4; num19 = (channel.Accent ? 0.055f : 0.13f) * num4; num20 = num19 * (channel.Accent ? 2f : 2.8f); num18 = num10 * 360f + num13 * 30f; break; case 3: val4 += val5 * num17 * 1.45f + normalized * num13 * 0.16f * num4; num19 = (channel.Accent ? 0.055f : 0.14f) * num4 * (0.5f + num13); num20 = num19 * 1.25f; num18 = num10 * 360f; break; default: val4 += val5 * num17 * (1f + num) + normalized * (num13 - 0.2f) * 0.16f * num4; num19 = (channel.Accent ? 0.072f : 0.14f) * num4; num20 = num19 * (channel.Accent ? 1f : 1.8f); num18 = (channel.Accent ? (45f + num13 * 40f) : (num10 * 360f)); break; } if (rank == 10 && !reduced) { float num22 = (((i & 1) == 0) ? 1 : (-1)); float num23 = num13 * MathF.PI * 2f + num10 * 2f; val4 += (val2 * Mathf.Cos(num23) + normalized2 * Mathf.Sin(num23)) * num22 * 0.023f * num4; if (!channel.Accent && channel.Element == 0) { num18 += num22 * Mathf.Sin(num13 * MathF.PI) * 12f; } } if (rank == 11 && channel.Accent && !reduced) { float num24 = num13 * MathF.PI * 2f + num10 * 6f; val4 += (val2 * Mathf.Cos(num24) + normalized2 * Mathf.Sin(num24) * 0.55f) * 0.065f * num4; num20 *= 1.16f; } if (rank == 12 && !reduced) { float num25 = (((i & 1) == 0) ? 1 : (-1)); float num26 = num13 * MathF.PI * 4f + num10 * 6f; float num27 = (channel.Accent ? 0.07f : 0.028f); val4 += (val2 * Mathf.Cos(num26) + normalized2 * Mathf.Sin(num26)) * num25 * num27 * num4; if (!channel.Accent) { num20 *= ((channel.Element == 1) ? 1.45f : 1.22f); num19 *= 0.84f; num18 += num25 * Mathf.Sin(num13 * 5f) * 18f; } } Color val6 = Color.Lerp(val3, (Color)((channel.Element == 0) ? new Color(1f, 0.8f, 0.34f) : Color.white), channel.Accent ? (0.28f + num * 0.2f) : 0.08f); val6.a = Mathf.Clamp01(((channel.Element == 1 && !channel.Accent) ? 0.28f : 0.65f) * num3 * num14 * num5); Particle[] particles = channel.Particles; int num28 = i; Particle val7 = default(Particle); ((Particle)(ref val7)).position = val4; ((Particle)(ref val7)).velocity = Vector3.zero; ((Particle)(ref val7)).startColor = Color32.op_Implicit(val6); ((Particle)(ref val7)).startSize3D = new Vector3(num19, num20, num19); ((Particle)(ref val7)).rotation = num18; ((Particle)(ref val7)).startLifetime = 5f; ((Particle)(ref val7)).remainingLifetime = 5f; ((Particle)(ref val7)).randomSeed = (uint)(num8 + 1); particles[num28] = val7; } channel.System.SetParticles(channel.Particles, num7); } } public void SetVisible(bool value) { visible = value; foreach (Channel channel in channels) { if (Object.op_Implicit((Object)(object)channel.Renderer)) { ((Renderer)channel.Renderer).enabled = value && ((Behaviour)this).isActiveAndEnabled && FateVisuals.Intensity > 0f && FateVisuals.Particles; } } } public void Pulse() { if (configured && !reduced) { pulseUntil = Time.unscaledTime + 0.65f; } } private void LateUpdate() { if (configured && visible && !(Time.unscaledTime < nextAnimation)) { nextAnimation = Time.unscaledTime + (FateVisuals.Reduced ? 0.065f : 0.016f); AnimatePreview(Time.unscaledTime); } } public void ClearVisual() { configured = false; foreach (Channel channel in channels) { if (Object.op_Implicit((Object)(object)channel.System)) { ((Renderer)channel.Renderer).enabled = false; channel.System.Stop(false, (ParticleSystemStopBehavior)0); ((Component)channel.System).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)channel.System).gameObject); } } channels.Clear(); systems.Clear(); } private void OnDisable() { foreach (Channel channel in channels) { if (Object.op_Implicit((Object)(object)channel.Renderer)) { ((Renderer)channel.Renderer).enabled = false; } } } private void OnEnable() { if (configured) { SetVisible(visible); } } private void OnDestroy() { ClearVisual(); Live.Remove(this); } public static void Shutdown() { ElementalVfx[] array = Live.ToArray(); foreach (ElementalVfx elementalVfx in array) { if (Object.op_Implicit((Object)(object)elementalVfx)) { elementalVfx.ClearVisual(); } } Live.Clear(); foreach (Material value in Materials.Values) { if (Object.op_Implicit((Object)(object)value)) { Object.Destroy((Object)(object)value); } } Materials.Clear(); foreach (Texture2D value2 in Textures.Values) { if (Object.op_Implicit((Object)(object)value2)) { Object.Destroy((Object)(object)value2); } } Textures.Clear(); shader = null; } } internal sealed class ElementalVfxMarker : MonoBehaviour { } public sealed class FateVisualProfile { public int Rank { get; } public string Theme { get; } public int FlameSurfaces { get; } public int RibbonCount { get; } public int ParticleLimit { get; } public float EmissionRate { get; } public float Height { get; } public float Motion { get; } public float Radiance { get; } public int MotifCount { get; } internal FateVisualProfile(int rank, string theme, int surfaces, int ribbons, int particles, float emission, float height, float motion, float radiance, int motifs = 0) { Rank = rank; Theme = theme; FlameSurfaces = surfaces; RibbonCount = ribbons; ParticleLimit = particles; EmissionRate = emission; Height = height; Motion = motion; Radiance = radiance; MotifCount = motifs; } } public sealed class FateVisualStyle { private readonly int[] powers; private readonly int[] active; private readonly int fallback; private readonly FateVisualStyle? setPalette; private static readonly Color[] Colors = (Color[])(object)new Color[5] { new Color(1f, 0.19f, 0.025f), new Color(0.18f, 0.79f, 1f), new Color(0.72f, 0.35f, 1f), new Color(0.39f, 1f, 0.08f), new Color(0.91f, 0.95f, 1f) }; public string Signature { get; } public string PaletteSignature => setPalette?.Signature ?? Signature; public int ElementCount => active.Length; public IReadOnlyList<int> ActiveElements => active; public float ElementStrength(int channel) { if (channel < 0 || channel >= powers.Length || powers[channel] <= 0) { return 0f; } return (float)(powers[channel] - 1) / 10000f; } public Color ElementColor(int channel) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (channel < 0 || channel >= Colors.Length) { return Color.white; } return Colors[channel]; } public FateVisualStyle(int fire = 0, int frost = 0, int lightning = 0, int poison = 0, int spirit = 0, int fallbackTheme = 0) { powers = new int[5] { fire, frost, lightning, poison, spirit }.Select((int x) => Math.Max(0, Math.Min(10001, x))).ToArray(); active = (from i in Enumerable.Range(0, 5) where powers[i] > 0 select i).ToArray(); fallback = Math.Max(0, Math.Min(2, fallbackTheme)); Signature = string.Join(",", powers.Select((int x) => x.ToString(CultureInfo.InvariantCulture))) + "," + fallback.ToString(CultureInfo.InvariantCulture); } private FateVisualStyle(FateVisualStyle own, FateVisualStyle palette) { powers = own.powers; active = own.active; fallback = own.fallback; setPalette = palette; Signature = own.Signature + "|" + palette.Signature; } public FateVisualStyle WithSetPalette(FateVisualStyle palette) { return new FateVisualStyle(this, palette); } public static FateVisualStyle Combine(IEnumerable<FateVisualStyle> styles) { int[] array = new int[5]; int num = 0; foreach (FateVisualStyle style in styles) { for (int i = 0; i < 5; i++) { array[i] = Math.Max(array[i], style.powers[i]); } num = Math.Max(num, style.fallback); } if (num == 0 && array.All((int v) => v == 0)) { num = 1; } return new FateVisualStyle(array[0], array[1], array[2], array[3], array[4], num); } public static FateVisualStyle Decode(string? value) { if (string.IsNullOrEmpty(value) || value.Length > 48) { return new FateVisualStyle(); } string[] array = value.Split(','); if (array.Length != 6) { return new FateVisualStyle(); } int[] array2 = new int[6]; for (int i = 0; i < 6; i++) { if (!int.TryParse(array[i], NumberStyles.None, CultureInfo.InvariantCulture, out array2[i]) || array2[i] < 0 || array2[i] > ((i == 5) ? 2 : 10001)) { return new FateVisualStyle(); } } return new FateVisualStyle(array2[0], array2[1], array2[2], array2[3], array2[4], array2[5]); } public static FateVisualStyle FromFate(Fateprint? fate, ItemData? item = null) { //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0267: 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_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) if (fate == null) { return new FateVisualStyle(); } int[] array = new int[5]; int num = 0; foreach (RolledPerk perk in fate.Perks) { if (!EffectRuntime.IsImplemented(perk)) { continue; } double value; double num2 = (perk.Values.TryGetValue("amount", out value) ? Math.Max(0.0, value) : 0.0); if (num2 <= 0.0 || double.IsNaN(num2) || double.IsInfinity(num2)) { continue; } double num3 = num2 / (num2 + 20.0); int[] array2 = perk.QualityScores.Values.Where((int s) => s >= 0 && s <= 10000).ToArray(); double x = ((array2.Length != 0) ? (array2.Average() / 10000.0) : num3); double num4 = ((array2.Length != 0) ? (0.45 * num3 + 0.55 * Math.Pow(x, 1.35)) : num3); int val = (int)Math.Round(10000.0 * num4, MidpointRounding.AwayFromZero); val = Math.Max(0, Math.Min(10000, val)) + 1; string id = perk.Id; if (id == "emberbound" || id == "ward_fire") { array[0] = Math.Max(array[0], val); } switch (id) { case "frostbound": case "winter_sovereign": case "shield_rebuke": case "ward_frost": array[1] = Math.Max(array[1], val); break; } switch (id) { case "stormspark": case "thors_judgment": case "ward_lightning": array[2] = Math.Max(array[2], val); break; } if (id == "ward_poison") { array[3] = Math.Max(array[3], val); } if (id == "ward_spirit") { array[4] = Math.Max(array[4], val); } if (id == "runic_potency" && item != null) { DamageTypes damages = item.m_shared.m_damages; DamageTypes damagesPerLevel = item.m_shared.m_damagesPerLevel; if (damages.m_fire + damagesPerLevel.m_fire > 0f) { array[0] = Math.Max(array[0], val); } if (damages.m_frost + damagesPerLevel.m_frost > 0f) { array[1] = Math.Max(array[1], val); } if (damages.m_lightning + damagesPerLevel.m_lightning > 0f) { array[2] = Math.Max(array[2], val); } if (damages.m_poison + damagesPerLevel.m_poison > 0f) { array[3] = Math.Max(array[3], val); } if (damages.m_spirit + damagesPerLevel.m_spirit > 0f) { array[4] = Math.Max(array[4], val); } } if (id.Contains("ward") || id.Contains("guard") || id.Contains("shelter") || id.Contains("bastion")) { num = Math.Max(num, 1); } if (id.Contains("bounty") || id.Contains("vein") || id.Contains("garden") || id.Contains("resource") || id.Contains("ymirs")) { num = 2; } } return new FateVisualStyle(array[0], array[1], array[2], array[3], array[4], num); } private void Stops(float t, out int a, out int b, out float blend) { float num = Mathf.Clamp01(t) * (float)Math.Max(0, active.Length - 1); a = Mathf.FloorToInt(num); b = Math.Min(active.Length - 1, a + 1); blend = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0.28f, 0.72f, num - (float)a)); } public Color ColorAt(float t, int rank) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (active.Length == 0 && setPalette != null) { return setPalette.ColorAt(t, rank); } if (active.Length == 0) { if (fallback != 1) { if (fallback != 2) { return FateUi.ColorOf(rank); } return new Color(0.44f, 0.93f, 0.62f); } return new Color(0.95f, 0.91f, 0.63f); } Stops(t, out var a, out var b, out var blend); return Color.Lerp(Colors[active[a]], Colors[active[b]], blend); } public float EnergyAt(float t) { if (active.Length == 0) { return 0.8f; } Stops(t, out var a, out var b, out var blend); return 0.22f + 0.78f * Mathf.Lerp((float)(powers[active[a]] - 1), (float)(powers[active[b]] - 1), blend) / 10000f; } } public static class FateVisuals { private sealed class ActivationState { public float At = -10f; } public const int FirstVisibleRank = 9; public static float Intensity = 0.8f; public static bool Trails = true; public static bool Particles = true; public static bool Reduced; private static readonly FateVisualProfile[] Profiles = new FateVisualProfile[4] { new FateVisualProfile(9, "Kindled essence", 0, 0, 28, 0f, 1f, 1f, 1f), new FateVisualProfile(10, "Elemental mantle", 0, 0, 32, 0f, 1.16f, 1f, 1.28f), new FateVisualProfile(11, "Ascendant corona", 0, 0, 36, 0f, 1.34f, 1f, 1.6f), new FateVisualProfile(12, "Sovereign aurora", 0, 0, 40, 0f, 1.55f, 1f, 2f) }; private static readonly ConditionalWeakTable<Player, ActivationState> Activations = new ConditionalWeakTable<Player, ActivationState>(); internal static int Revision => Mathf.RoundToInt(Mathf.Clamp01(Intensity) * 1000f) * 8 + (Trails ? 1 : 0) + (Particles ? 2 : 0) + (Reduced ? 4 : 0); public static bool Available { get { if (Object.op_Implicit((Object)(object)ZNet.instance)) { return !ZNet.instance.IsDedicated(); } return true; } } public static int SharedMaterialCount => ElementalVfx.SharedMaterialCount; public static int SharedTextureCount => ElementalVfx.SharedTextureCount; public static string SelectedShader => ElementalVfx.SelectedShader; public static FateVisualProfile Profile(int rank) { return Profiles[Mathf.Clamp(rank - 9, 0, Profiles.Length - 1)]; } public static void Shutdown() { WeaponSurfaceGlow.Shutdown(); FateEquipmentAnchors[] array = Object.FindObjectsByType<FateEquipmentAnchors>((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (FateEquipmentAnchors obj in array) { obj.ClearAll(); Object.Destroy((Object)(object)obj); } FateGlow[] array2 = Object.FindObjectsByType<FateGlow>((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (FateGlow obj2 in array2) { obj2.ClearVisual(); Object.Destroy((Object)(object)obj2); } ElementalVfx.Shutdown(); } public static void Activate(Player player, string effect) { if (!Object.op_Implicit((Object)(object)player)) { return; } var anon = (from i in EffectRuntime.Equipped(player) select new { Item = i, Fate = ItemFate.Read(i) } into x where x.Fate != null && x.Fate.Perks.Any((RolledPerk p) => p.Id == effect) orderby x.Fate.Rank descending select x).FirstOrDefault(); int? obj; if (anon == null) { obj = null; } else { Fateprint fate = anon.Fate; obj = ((fate != null) ? new int?(fate.Rank) : ((int?)null)); } int? num = obj; int valueOrDefault = num.GetValueOrDefault(); if (valueOrDefault >= 9) { FateVisualStyle fateVisualStyle = FateVisualStyle.FromFate(anon.Fate, anon.Item); ZNetView component = ((Component)player).GetComponent<ZNetView>(); ItemProfile? obj2 = Classification.Describe(anon.Item); bool flag = obj2 != null && !obj2.Capabilities.Contains("worn"); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { ZDO zDO = component.GetZDO(); zDO.Set("nornforged.activation.effect", effect); zDO.Set("nornforged.activation.rank", valueOrDefault); zDO.Set("nornforged.activation.style", fateVisualStyle.Signature); zDO.Set("nornforged.activation.weapon", flag); zDO.Set("nornforged.activation.seed", anon.Fate.CosmeticSeed); zDO.Set("nornforged.activation.time", ZNet.instance.GetTime().Ticks); } PlayActivation(player, effect, valueOrDefault, fateVisualStyle, flag ? new int?(anon.Fate.CosmeticSeed) : ((int?)null)); } } internal static void PlayActivation(Player player, string effect, int rank, FateVisualStyle? style = null, int? weaponSeed = null) { if (!Available || Reduced || Intensity <= 0f || !Object.op_Implicit((Object)(object)player) || rank < 9) { return; } ActivationState value = Activations.GetValue(player, (Player _) => new ActivationState()); if (Time.unscaledTime - value.At < 0.1f) { return; } value.At = Time.unscaledTime; if (weaponSeed.HasValue) { WeaponSurfaceGlow[] componentsInChildren = ((Component)player).GetComponentsInChildren<WeaponSurfaceGlow>(); foreach (WeaponSurfaceGlow weaponSurfaceGlow in componentsInChildren) { if (weaponSurfaceGlow.Seed == weaponSeed.Value) { weaponSurfaceGlow.Pulse(); } } return; } FateGlow[] componentsInChildren2 = ((Component)player).GetComponentsInChildren<FateGlow>(); foreach (FateGlow fateGlow in componentsInChildren2) { if (fateGlow.Rank == rank) { fateGlow.Pulse(); } } } public static FateGlow? AttachPreview(GameObject model, int rank, int seed, FateVisualStyle style, bool aura = false) { return Attach(model, rank, seed, ground: false, aura, style); } public static FateGlow? AttachWorn(GameObject anchor, int rank, int seed, FateVisualStyle style, string slot) { //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)anchor) || !Available) { return null; } FateGlow fateGlow = anchor.GetComponent<FateGlow>(); if (rank < 9 || Intensity <= 0f || !Particles) { if (Object.op_Implicit((Object)(object)fateGlow)) { fateGlow.ClearVisual(); Object.Destroy((Object)(object)fateGlow); } return null; } if (Object.op_Implicit((Object)(object)fateGlow) && fateGlow.HasVisual && fateGlow.Rank == rank && fateGlow.Seed == seed && fateGlow.SettingsRevision == Revision && fateGlow.StyleSignature == style.Signature) { return fateGlow; } float size = slot switch { "shoulder" => 0.22f, "accessory" => 0.14f, "helmet" => 0.13f, _ => 0.2f, }; float span = slot switch { "shoulder" => 0.6f, "chest" => 0.65f, "leg" => 0.8f, _ => 0.35f, }; if (!Object.op_Implicit((Object)(object)fateGlow)) { fateGlow = anchor.AddComponent<FateGlow>(); } fateGlow.Configure(rank, seed, size, ground: false, aura: true, Vector3.zero, style, Vector3.up, span); return fateGlow; } internal static FateGlow? Attach(GameObject model, int rank, int seed, bool ground = false, bool aura = false, FateVisualStyle? style = null, bool marker = false) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0229: 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_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023f: 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_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0248: 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_026f: 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_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_017c: 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_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016e: 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_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)model) || !Available) { return null; } style = style ?? new FateVisualStyle(); FateGlow component = model.GetComponent<FateGlow>(); if (rank < 9 || Intensity <= 0f || !Particles) { if (Object.op_Implicit((Object)(object)component)) { component.ClearVisual(); Object.Destroy((Object)(object)component); } return null; } if (Object.op_Implicit((Object)(object)component) && component.HasVisual && component.Rank == rank && component.Seed == seed && component.SettingsRevision == Revision && component.StyleSignature == style.Signature) { return component; } float num = 0.14f; float num2 = 0.7f; Vector3 center = Vector3.zero; Vector3 value = Vector3.up; if (aura) { num = 0.3f; center = Vector3.up * 0.9f; num2 = 0.8f; } else { Renderer[] array = (from r in model.GetComponentsInChildren<Renderer>(true) where !(r is ParticleSystemRenderer) && !(r is LineRenderer) && !(r is TrailRenderer) && !((Object)r).name.StartsWith("Nornforged", StringComparison.Ordinal) select r).ToArray(); if (array.Length != 0) { Bounds val = default(Bounds); bool flag = false; Renderer[] array2 = array; foreach (Renderer val2 in array2) { MeshFilter component2 = ((Component)val2).GetComponent<MeshFilter>(); Mesh val3 = ((component2 != null) ? component2.sharedMesh : null); SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)(object)((val2 is SkinnedMeshRenderer) ? val2 : null); Bounds val5 = (Bounds)((val4 != null) ? ((Renderer)val4).localBounds : (Object.op_Implicit((Object)(object)val3) ? val3.bounds : new Bounds(Vector3.zero, Vector3.one * 0.2f))); for (int num4 = 0; num4 < 8; num4++) { Vector3 val6 = model.transform.InverseTransformPoint(((Component)val2).transform.TransformPoint(((Bounds)(ref val5)).center + Vector3.Scale(((Bounds)(ref val5)).extents, new Vector3((float)(((num4 & 1) != 0) ? 1 : (-1)), (float)(((num4 & 2) != 0) ? 1 : (-1)), (float)(((num4 & 4) != 0) ? 1 : (-1)))))); if (!flag) { ((Bounds)(ref val))..ctor(val6, Vector3.zero); flag = true; } else { ((Bounds)(ref val)).Encapsulate(val6); } } } center = ((Bounds)(ref val)).center; Vector3 size = ((Bounds)(ref val)).size; value = ((size.x > size.y && size.x > size.z) ? Vector3.right : ((size.z > size.y) ? Vector3.forward : Vector3.up)); num2 = Mathf.Clamp(Mathf.Max(size.x, Mathf.Max(size.y, size.z)), 0.2f, 2.5f); num = Mathf.Clamp(num2 * 0.15f, 0.065f, ground ? 0.2f : 0.3f); } } if (marker) { num *= 0.68f; num2 = 0.12f; } FateGlow obj = (Object.op_Implicit((Object)(object)component) ? component : model.AddComponent<FateGlow>()); obj.Configure(rank, seed, num, ground, aura, center, style, value, num2, marker); return obj; } } public sealed class FateGlow : MonoBehaviour { private GameObject? root; private ElementalVfx? emitter; private Light? illumination; private LODGroup? nativeLod; private LODGroup? particleLod; private FateVisualStyle style = new FateVisualStyle(); private Vector3[] points = Array.Empty<Vector3>(); private Transform[] following = Array.Empty<Transform>(); private Vector3[] followedPoints = Array.Empty<Vector3>(); private float size; private bool armor; private bool ground; private int? setSeed; private bool centerpiece; private float nextDistance; private bool distanceVisible = true; public int Rank { get; private set; } public int Seed { get; private set; } public int SettingsRevision { get; private set; } public string StyleSignature => style.Signature; public int FlameVertexCount => 0; public int MotifVertexCount => 0; public int ParticleCapacity { get { if (!Object.op_Implicit((Object)(object)emitter)) { return 0; } return emitter.ParticleCapacity; } } public int RibbonCount => 0; public bool HasVisual { get { if (Object.op_Implicit((Object)(object)root) && Object.op_Implicit((Object)(object)emitter)) { return emitter.HasVisual; } return false; } } public bool Coordinated => setSeed.HasValue; public float AnimationPhase => Time.time + (float)((setSeed ?? Seed) & 0x3FF) * 0.011f; public bool CheapIndicator { get { if (!ground) { return FateVisuals.Reduced; } return true; } } public ElementalVfx? Emitter => emitter; public void Configure(int rank, int seed, float size, bool ground, bool aura, Vector3 center, FateVisualStyle? style = null, Vector3? axis = null, float span = 0.7f, bool marker = false, bool activation = false, bool grandBurst = false) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: 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_0154: 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_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) ClearVisual(); Rank = rank; Seed = seed; SettingsRevision = FateVisuals.Revision; this.style = style ?? new FateVisualStyle(); this.size = (aura ? Mathf.Clamp(0.55f + span * 0.15f, 0.5f, 0.75f) : Mathf.Clamp(span * 0.55f, 0.15f, 0.65f)); this.ground = ground; armor = aura; setSeed = null; centerpiece = false; if (rank < 9 || FateVisuals.Intensity <= 0f || !FateVisuals.Particles || !FateVisuals.Available) { return; } root = new GameObject("Nornforged elemental mantle"); root.transform.SetParent(((Component)this).transform, false); root.transform.localPosition = center; Vector3 val = (Vector3)(((??)axis) ?? Vector3.up); Vector3 normalized = ((Vector3)(ref val)).normalized; val = Vector3.Cross(normalized, (Mathf.Abs(Vector3.Dot(normalized, Vector3.forward)) > 0.9f) ? Vector3.right : Vector3.forward); Vector3 normalized2 = ((Vector3)(ref val)).normalized; val = Vector3.Cross(normalized, normalized2); Vector3 normalized3 = ((Vector3)(ref val)).normalized; points = (Vector3[])(object)new Vector3[18]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 6; j++) { float num = (float)j * MathF.PI / 3f; points[i * 6 + j] = normalized * ((float)(i - 1) * span * 0.3f) + (normalized2 * Mathf.Cos(num) + normalized3 * Mathf.Sin(num)) * size; } } emitter = root.AddComponent<ElementalVfx>(); Reconfigure(); if (activation) { emitter.Pulse(); } } private void Reconfigure() { if (Object.op_Implicit((Object)(object)emitter)) { emitter.Configure(Rank, Seed, style, points, size, armor, setSeed); emitter.SetVisible(distanceVisible); SyncLod(); } } private void SyncLod() { //IL_00e2: 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_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)root) || !Object.op_Implicit((Object)(object)emitter)) { return; } if (!Object.op_Implicit((Object)(object)nativeLod)) { nativeLod = ((IEnumerable<LODGroup>)((Component)((Component)this).transform).GetComponentsInParent<LODGroup>(true)).FirstOrDefault((Func<LODGroup, bool>)((LODGroup l) => !Object.op_Implicit((Object)(object)((Component)l).GetComponent<ElementalVfxMarker>()))); } if (!Object.op_Implicit((Object)(object)nativeLod)) { return; } if (!Object.op_Implicit((Object)(object)particleLod)) { root.AddComponent<ElementalVfxMarker>(); particleLod = root.AddComponent<LODGroup>(); } Renderer[] renderers = emitter.Systems.Select((ParticleSystem p) => (Renderer)(object)((Component)p).GetComponent<ParticleSystemRenderer>()).ToArray(); particleLod.fadeMode = nativeLod.fadeMode; particleLod.animateCrossFading = nativeLod.animateCrossFading; particleLod.SetLODs(nativeLod.GetLODs().Select(delegate(LOD l) { //IL_0002: 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_0048: 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) LOD result = default(LOD); ((LOD)(ref result))..ctor(l.screenRelativeTransitionHeight, l.renderers.Any((Renderer r) => Object.op_Implicit((Object)(object)r) && !ElementalVfx.IsOwned(r)) ? renderers : Array.Empty<Renderer>()); result.fadeTransitionWidth = l.fadeTransitionWidth; return result; }).ToArray()); particleLod.localReferencePoint = ((Component)particleLod).transform.InverseTransformPoint(((Component)nativeLod).transform.TransformPoint(nativeLod.localReferencePoint)); Vector3 lossyScale = ((Component)nativeLod).transform.lossyScale; Vector3 lossyScale2 = ((Component)particleLod).transform.lossyScale; particleLod.size = nativeLod.size * Mathf.Max(new float[3] { Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.y), Mathf.Abs(lossyScale.z) }) / Mathf.Max(new float[4] { 0.001f, Mathf.Abs(lossyScale2.x), Mathf.Abs(lossyScale2.y), Mathf.Abs(lossyScale2.z) }); particleLod.enabled = distanceVisible && nativeLod.enabled; } public void CoordinateSet(int? seed, bool centerpiece = false) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) if (setSeed != seed || this.centerpiece != centerpiece) { setSeed = seed; this.centerpiece = centerpiece; Reconfigure(); if (centerpiece && armor && Object.op_Implicit((Object)(object)root) && !FateVisuals.Reduced && !Object.op_Implicit((Object)(object)illumination)) { illumination = root.AddComponent<Light>(); illumination.type = (LightType)2; illumination.shadows = (LightShadows)0; illumination.renderMode = (LightRenderMode)2; illumination.range = 0.75f; illumination.color = style.ColorAt(0.5f, Rank); illumination.bounceIntensity = 0f; } UpdateLight(); } } private void UpdateLight() { if (Object.op_Implicit((Object)(object)illumination)) { ((Behaviour)illumination).enabled = centerpiece && distanceVisible && ((Behaviour)this).isActiveAndEnabled && Object.op_Implicit((Object)(object)emitter) && emitter.HasVisual && FateVisuals.Particles && !FateVisuals.Reduced && FateVisuals.Intensity > 0f && (!Object.op_Implicit((Object)(object)nativeLod) || emitter.Systems.Any((ParticleSystem s) => ((Renderer)((Component)s).GetComponent<ParticleSystemRenderer>()).isVisible)); illumination.intensity = (Object.op_Implicit((Object)(object)emitter) ? Mathf.Min(1.4f, emitter.EffectivePower * 0.85f) : 0f); } } public void FollowParts(params Transform[] parts) { Transform[] second = parts.Where((Transform p) => Object.op_Implicit((Object)(object)p)).Take(4).ToArray(); if (!following.SequenceEqual(second)) { following = second; followedPoints = (Vector3[])(object)new Vector3[following.Length * 6]; UpdateAnchors(); } } private void UpdateAnchors() { //IL_008d: 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_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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)root) || !Object.op_Implicit((Object)(object)emitter) || following.Length == 0) { return; } Vector3 val2 = default(Vector3); for (int i = 0; i < following.Length; i++) { Transform val = following[i]; if (Object.op_Implicit((Object)(object)val)) { for (int j = 0; j < 6; j++) { float num = (float)j * MathF.PI / 3f; ((Vector3)(ref val2))..ctor(Mathf.Cos(num) * 0.105f, 0f, Mathf.Sin(num) * 0.105f); followedPoints[i * 6 + j] = root.transform.InverseTransformPoint(val.position + val.rotation * Vector3.Scale(val2, ((Component)this).transform.lossyScale)); } } } emitter.UpdateAnchors(followedPoints); } private void LateUpdate() { //IL_006a: 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_007a: 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) if (!Object.op_Implicit((Object)(object)emitter)) { return; } if (Time.unscaledTime >= nextDistance) { Transform val = (Object.op_Implicit((Object)(object)GameCamera.instance) ? ((Component)GameCamera.instance).transform : (Object.op_Implicit((Object)(object)Camera.main) ? ((Component)Camera.main).transform : null)); float num = (ground ? 35 : 65); int num2; if (Object.op_Implicit((Object)(object)val)) { Vector3 val2 = val.position - ((Component)this).transform.position; num2 = ((((Vector3)(ref val2)).sqrMagnitude < num * num) ? 1 : 0); } else { num2 = 1; } distanceVisible = (byte)num2 != 0; emitter.SetVisible(distanceVisible); SyncLod(); nextDistance = Time.unscaledTime + 0.3f; } if (distanceVisible) { UpdateAnchors(); } UpdateLight(); } public void AnimatePreview(Camera? camera = null) { UpdateAnchors(); if (Object.op_Implicit((Object)(object)emitter)) { emitter.AnimatePreview(Time.unscaledTime); } UpdateLight(); } public void Pulse() { if (Object.op_Implicit((Object)(object)emitter)) { emitter.Pulse(); } } public void ClearVisual() { if (Object.op_Implicit((Object)(object)illumination)) { ((Behaviour)illumination).enabled = false; } if (Object.op_Implicit((Object)(object)particleLod)) { particleLod.enabled = false; } if (Object.op_Implicit((Object)(object)emitter)) { emitter.ClearVisual(); } if (Object.op_Implicit((Object)(object)root)) { root.SetActive(false); Object.Destroy((Object)(object)root); } root = null; emitter = null; illumination = null; nativeLod = null; particleLod = null; following = Array.Empty<Transform>(); followedPoints = Array.Empty<Vector3>(); points = Array.Empty<Vector3>(); } private void OnDisable() { if (Object.op_Implicit((Object)(object)emitter)) { emitter.SetVisible(value: false); } if (Object.op_Implicit((Object)(object)illumination)) { ((Behaviour)illumination).enabled = false; } } private void OnEnable() { if (Object.op_Implicit((Object)(object)emitter)) { emitter.SetVisible(distanceVisible); } UpdateLight(); } private void OnDestroy() { ClearVisual(); } } public sealed class FateEquipmentAnchors : MonoBehaviour { private readonly Dictionary<string, GameObject> mounts = new Dictionary<string, GameObject>(StringComparer.Ordinal); private readonly Dictionary<string, Transform> bones = new Dictionary<string, Transform>(StringComparer.Ordinal); private Transform[]? legBones; public int ActiveSlots => mounts.Values.Count((GameObject g) => Object.op_Implicit((Object)(object)g) && g.activeSelf && (g.GetComponent<FateGlow>()?.HasVisual ?? false)); public int PooledSlots => mounts.Count; public void Apply(string slot, int rank, int seed, FateVisualStyle style, int? coordinationSeed = null, bool centerpiece = false) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0218: 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_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: 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) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: 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_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) if (slot != "helmet" && slot != "chest" && slot != "leg" && slot != "shoulder" && slot != "accessory") { return; } if (rank < 9 || FateVisuals.Intensity <= 0f || !FateVisuals.Particles) { if (mounts.TryGetValue(slot, out GameObject value) && Object.op_Implicit((Object)(object)value)) { value.GetComponent<FateGlow>()?.ClearVisual(); value.SetActive(false); } return; } if (!mounts.TryGetValue(slot, out GameObject value2) || !Object.op_Implicit((Object)(object)value2)) { Transform val = Bone(slot); value2 = new GameObject("Nornforged worn " + slot); value2.transform.SetParent(val, false); Vector3 val2 = (Vector3)((!((Object)(object)val == (Object)(object)((Component)this).transform)) ? (slot switch { "accessory" => Vector3.right * 0.2f, "shoulder" => -Vector3.forward * 0.18f, "leg" => Vector3.down * 0.3f, _ => Vector3.zero, }) : (slot switch { "shoulder" => Vector3.up * 1.25f - Vector3.forward * 0.2f, "leg" => Vector3.up * 0.55f, "chest" => Vect