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 CaptainValheim v1.0.0
CaptainValheim.dll
Decompiled 2 days 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.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("CaptainValheim")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("CaptainValheim")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [CompilerGenerated] internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { int ICollection.Count => _items.Length; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => _items.Length; T IReadOnlyList<T>.this[int index] => _items[index]; int ICollection<T>.Count => _items.Length; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyArray(T[] items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { ((ICollection)_items).CopyTo(array, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return ((IList)_items).Contains(value); } int IList.IndexOf(object value) { return ((IList)_items).IndexOf(value); } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return ((ICollection<T>)_items).Contains(item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { ((ICollection<T>)_items).CopyTo(array, arrayIndex); } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { return ((IList<T>)_items).IndexOf(item); } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } } [CompilerGenerated] internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T> { object IEnumerator.Current => _item; T IEnumerator<T>.Current => _item; public Enumerator(T item) { _item = item; } bool IEnumerator.MoveNext() { if (!_moveNextCalled) { return _moveNextCalled = true; } return false; } void IEnumerator.Reset() { _moveNextCalled = false; } void IDisposable.Dispose() { } } int ICollection.Count => 1; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => 1; T IReadOnlyList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } } int ICollection<T>.Count => 1; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } public <>z__ReadOnlySingleElementList(T item) { _item = item; } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_item); } void ICollection.CopyTo(Array array, int index) { array.SetValue(_item, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return EqualityComparer<T>.Default.Equals(_item, (T)value); } int IList.IndexOf(object value) { if (!EqualityComparer<T>.Default.Equals(_item, (T)value)) { return -1; } return 0; } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return new Enumerator(_item); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return EqualityComparer<T>.Default.Equals(_item, item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { array[arrayIndex] = _item; } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { if (!EqualityComparer<T>.Default.Equals(_item, item)) { return -1; } return 0; } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } } 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; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ExtensionMarkerAttribute : Attribute { private readonly string <Name>k__BackingField; public string Name => <Name>k__BackingField; public ExtensionMarkerAttribute(string name) { <Name>k__BackingField = name; } } } namespace CaptainValheim { internal sealed class KeyHintCell { private readonly List<TMP_Text> _keys = new List<TMP_Text>(); private readonly List<GameObject> _keyParents = new List<GameObject>(); private readonly List<TMP_Text> _extraTexts = new List<TMP_Text>(); private readonly List<TMP_Text> _generatedSeparatorTexts = new List<TMP_Text>(); private TMP_Text? _label; internal GameObject Root { get; } private KeyHintCell(GameObject root) { Root = root; RefreshChildren(); } internal static KeyHintCell? CloneFrom(GameObject? template, string name) { if (!IsUsableTemplate(template) || (Object)(object)template.transform.parent == (Object)null) { return null; } GameObject obj = Object.Instantiate<GameObject>(template, template.transform.parent, false); ((Object)obj).name = name; obj.SetActive(false); return new KeyHintCell(obj); } internal static bool IsUsableTemplate(GameObject? template) { if ((Object)(object)template != (Object)null && (Object)(object)template.transform.parent != (Object)null && !((Object)template).name.StartsWith("CaptainValheim_")) { return template.GetComponentsInChildren<TMP_Text>(true).Length != 0; } return false; } internal static Transform? FindParentWithTemplates(GameObject root, string name) { Transform val = root.transform.Find(name); if ((Object)(object)val == (Object)null) { return null; } if (!((IEnumerable)val).Cast<Transform>().Any((Transform child) => IsUsableTemplate(((Component)child).gameObject))) { return null; } return val; } internal void Set(string label, IReadOnlyList<string> keys, float preferredTextWidth = 0f, bool hideExtraTexts = false) { EnsureKeyCount(keys.Count); Root.SetActive(true); if ((Object)(object)_label != (Object)null) { SetText(_label, label); LayoutElement val = default(LayoutElement); if (preferredTextWidth > 0f && ((Component)_label).TryGetComponent<LayoutElement>(ref val)) { val.preferredWidth = preferredTextWidth; } } for (int i = 0; i < _keys.Count; i++) { bool flag = i < keys.Count; if (i < _keyParents.Count && (Object)(object)_keyParents[i] != (Object)null) { _keyParents[i].SetActive(flag); } if (flag) { SetText(_keys[i], keys[i]); } } if (hideExtraTexts) { foreach (TMP_Text extraText in _extraTexts) { if ((Object)(object)extraText != (Object)null) { ((Component)extraText).gameObject.SetActive(false); } } return; } EnsureSeparatorCount(Mathf.Max(0, keys.Count - 1)); for (int j = 0; j < _generatedSeparatorTexts.Count; j++) { TMP_Text val2 = _generatedSeparatorTexts[j]; if (!((Object)(object)val2 == (Object)null)) { bool flag2 = j < keys.Count - 1; ((Component)val2).gameObject.SetActive(flag2); if (flag2) { SetText(val2, "+"); } } } foreach (TMP_Text extraText2 in _extraTexts) { if ((Object)(object)extraText2 != (Object)null) { ((Component)extraText2).gameObject.SetActive(keys.Count > 1); } } } internal void SetActive(bool active) { if ((Object)(object)Root != (Object)null) { Root.SetActive(active); } } internal void MoveBefore(GameObject? template) { if (!((Object)(object)template == (Object)null) && !((Object)(object)Root == (Object)null) && !((Object)(object)Root == (Object)(object)template) && !((Object)(object)Root.transform.parent != (Object)(object)template.transform.parent)) { int siblingIndex = Root.transform.GetSiblingIndex(); int siblingIndex2 = template.transform.GetSiblingIndex(); int num = ((siblingIndex < siblingIndex2) ? (siblingIndex2 - 1) : siblingIndex2); if (siblingIndex != num) { Root.transform.SetSiblingIndex(Mathf.Max(0, num)); } } } internal void RebuildParentLayout() { if ((Object)(object)Root != (Object)null) { Transform parent = Root.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val != null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val); } } } private void EnsureKeyCount(int count) { RefreshChildren(); if (count <= _keys.Count || _keyParents.Count == 0) { return; } GameObject val = _keyParents[0]; Transform parent = val.transform.parent; while (_keys.Count < count) { ((Object)Object.Instantiate<GameObject>(val, parent, false)).name = ((_keys.Count == 1) ? "key_bkg (1)" : $"key_bkg ({_keys.Count})"); RefreshChildren(); if (_keys.Count == 0) { break; } } } private void EnsureSeparatorCount(int count) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0087: 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_00a1: Unknown result type (might be due to invalid IL or missing references) if (count <= 0 || _keyParents.Count == 0 || _extraTexts.Count > 0) { return; } Transform parent = _keyParents[0].transform.parent; TMP_Text val = ResolveSeparatorTemplateText(); if ((Object)(object)parent == (Object)null || (Object)(object)val == (Object)null) { return; } while (_generatedSeparatorTexts.Count < count) { GameObject val2 = new GameObject($"CaptainValheim_KeyHintSeparator_{_generatedSeparatorTexts.Count}"); val2.SetActive(false); val2.transform.SetParent(parent, false); val2.AddComponent<RectTransform>().sizeDelta = new Vector2(18f, 24f); TextMeshProUGUI val3 = val2.AddComponent<TextMeshProUGUI>(); CopyTextStyle(val, (TMP_Text)(object)val3); ((TMP_Text)val3).alignment = (TextAlignmentOptions)514; ((Graphic)val3).raycastTarget = false; ((TMP_Text)val3).text = "+"; LayoutElement obj = val2.AddComponent<LayoutElement>(); obj.preferredWidth = 18f; obj.minWidth = 12f; _generatedSeparatorTexts.Add((TMP_Text)(object)val3); } for (int i = 0; i < _generatedSeparatorTexts.Count; i++) { TMP_Text val4 = _generatedSeparatorTexts[i]; if (!((Object)(object)val4 == (Object)null)) { int num = Mathf.Min(i + 1, _keyParents.Count - 1); if (num >= 0 && num < _keyParents.Count && (Object)(object)_keyParents[num] != (Object)null) { val4.transform.SetSiblingIndex(_keyParents[num].transform.GetSiblingIndex()); } } } } private TMP_Text? ResolveSeparatorTemplateText() { return GetStyleTemplate(_label) ?? _keys.Select(GetStyleTemplate).FirstOrDefault<TMP_Text>((Func<TMP_Text, bool>)((TMP_Text text) => (Object)(object)text != (Object)null)) ?? GetStyleTemplate(((IEnumerable<TMP_Text>)Root.GetComponentsInChildren<TMP_Text>(true)).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => (Object)(object)text != (Object)null && (Object)(object)text.font != (Object)null))) ?? _label ?? _keys.FirstOrDefault(); } private static TMP_Text? GetStyleTemplate(TMP_Text? text) { if (!((Object)(object)text != (Object)null) || !((Object)(object)text.font != (Object)null)) { return null; } return text; } private static void CopyTextStyle(TMP_Text source, TMP_Text target) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) target.font = source.font; target.fontSharedMaterial = source.fontSharedMaterial; target.fontSize = source.fontSize; target.fontSizeMin = source.fontSizeMin; target.fontSizeMax = source.fontSizeMax; target.enableAutoSizing = source.enableAutoSizing; target.fontStyle = source.fontStyle; target.fontWeight = source.fontWeight; ((Graphic)target).color = ((Graphic)source).color; target.characterSpacing = source.characterSpacing; target.wordSpacing = source.wordSpacing; target.lineSpacing = source.lineSpacing; target.paragraphSpacing = source.paragraphSpacing; target.textWrappingMode = source.textWrappingMode; target.overflowMode = source.overflowMode; } private void RefreshChildren() { _keys.Clear(); _keyParents.Clear(); _extraTexts.Clear(); _label = null; TMP_Text[] array = (from text in Root.GetComponentsInChildren<TMP_Text>(true) where (Object)(object)text != (Object)null select text).ToArray(); TMP_Text[] array2 = array; foreach (TMP_Text val in array2) { Localization instance = Localization.instance; if (instance != null) { instance.RemoveTextFromCache(val); } TextMeshProUGUI val2 = (TextMeshProUGUI)(object)((val is TextMeshProUGUI) ? val : null); if (val2 != null) { ((Graphic)val2).raycastTarget = false; } } _keys.AddRange(array.Where((TMP_Text text) => string.Equals(((Object)text).name, "Key", StringComparison.OrdinalIgnoreCase))); if (_keys.Count == 0) { TMP_Text val3 = ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => LooksLikeKeyBindingText(text.text))) ?? array.OrderBy((TMP_Text text) => text.transform.position.x).LastOrDefault(); if ((Object)(object)val3 != (Object)null && array.Length > 1) { _keys.Add(val3); } } _label = ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => string.Equals(((Object)text).name, "Text", StringComparison.OrdinalIgnoreCase) && !_keys.Contains(text))) ?? ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => !_keys.Contains(text) && !LooksLikeKeyBindingText(text.text))) ?? ((IEnumerable<TMP_Text>)array).FirstOrDefault((Func<TMP_Text, bool>)((TMP_Text text) => !_keys.Contains(text))); foreach (TMP_Text key in _keys) { _keyParents.Add(((Object)(object)key.transform.parent != (Object)null) ? ((Component)key.transform.parent).gameObject : ((Component)key).gameObject); } _extraTexts.AddRange(array.Where((TMP_Text text) => (Object)(object)text != (Object)(object)_label && !_keys.Contains(text))); SortKeysBySiblingIndex(); } private void SortKeysBySiblingIndex() { List<int> list = (from i in Enumerable.Range(0, _keys.Count) orderby (!((Object)(object)_keyParents[i] != (Object)null)) ? i : _keyParents[i].transform.GetSiblingIndex() select i).ToList(); if (list.Count <= 1) { return; } List<TMP_Text> list2 = new List<TMP_Text>(); List<GameObject> list3 = new List<GameObject>(); foreach (int item in list) { list2.Add(_keys[item]); list3.Add(_keyParents[item]); } _keys.Clear(); _keys.AddRange(list2); _keyParents.Clear(); _keyParents.AddRange(list3); } private static void SetText(TMP_Text? text, string value) { if (!((Object)(object)text == (Object)null)) { Localization instance = Localization.instance; if (instance != null) { instance.RemoveTextFromCache(text); } ((Component)text).gameObject.SetActive(true); text.text = value; } } private static bool LooksLikeKeyBindingText(string? text) { if (string.IsNullOrWhiteSpace(text)) { return false; } string text2 = new string(text.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray()); if (!text2.Contains("mouse") && !text2.Contains("ctrl") && !text2.Contains("shift") && !text2.Contains("alt") && !text2.Contains("button") && !text2.Contains("key") && !text2.Contains("sprite")) { return text2.Length <= 2; } return true; } } internal static class ShieldChargeCooldownStatusSystem { private const string StatusEffectName = "CaptainValheim_Cooldown_shieldCharge"; private const string DisplayName = "Shield Charge Cooldown"; private const string Tooltip = "Shield Charge is recharging."; private const string FallbackIconPrefabName = "ShieldWood"; internal static void RegisterStatusEffect(ObjectDB objectDb) { if (!((Object)(object)objectDb == (Object)null) && !objectDb.m_StatusEffects.Exists((StatusEffect statusEffect) => (Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name == "CaptainValheim_Cooldown_shieldCharge")) { ShieldChargeCooldownStatusEffect shieldChargeCooldownStatusEffect = ScriptableObject.CreateInstance<ShieldChargeCooldownStatusEffect>(); shieldChargeCooldownStatusEffect.Initialize("CaptainValheim_Cooldown_shieldCharge", "Shield Charge Cooldown", "Shield Charge is recharging.", ResolveIcon(objectDb, "ShieldWood")); objectDb.m_StatusEffects.Add((StatusEffect)(object)shieldChargeCooldownStatusEffect); } } internal static void Apply(Character character, ItemData? shield, float cooldown) { if ((Object)(object)character == (Object)null || cooldown <= 0f) { return; } SEMan sEMan = character.GetSEMan(); if (sEMan != null) { int stableHashCode = StringExtensionMethods.GetStableHashCode("CaptainValheim_Cooldown_shieldCharge"); sEMan.AddStatusEffect(stableHashCode, true, 0, 0f); StatusEffect statusEffect = sEMan.GetStatusEffect(stableHashCode); if (statusEffect != null) { statusEffect.m_ttl = cooldown; statusEffect.m_icon = ResolveShieldIcon(shield) ?? statusEffect.m_icon; } } } private static Sprite? ResolveShieldIcon(ItemData? shield) { Sprite[] array = shield?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static Sprite? ResolveIcon(ObjectDB objectDb, string itemPrefabName) { GameObject itemPrefab = objectDb.GetItemPrefab(itemPrefabName); Sprite[] array = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } } internal sealed class ShieldChargeCooldownStatusEffect : StatusEffect { internal void Initialize(string prefabName, string displayName, string tooltip, Sprite? icon) { ((Object)this).name = prefabName; base.m_name = displayName; base.m_tooltip = tooltip; base.m_icon = icon; } } internal static class ShieldOnlyKeyHintSystem { private readonly struct ShieldHintState { internal static readonly ShieldHintState Hidden = new ShieldHintState("", gamepad: false, hasCharge: false, hasPrimaryAttack: false, hasThrow: false); private readonly string _weaponPrefabName; private readonly bool _gamepad; internal readonly bool HasCharge; internal readonly bool HasPrimaryAttack; internal readonly bool HasThrow; internal bool HasRows { get { if (!HasCharge && !HasPrimaryAttack) { return HasThrow; } return true; } } public ShieldHintState(string weaponPrefabName, bool gamepad, bool hasCharge, bool hasPrimaryAttack, bool hasThrow) { _weaponPrefabName = weaponPrefabName; _gamepad = gamepad; HasCharge = hasCharge; HasPrimaryAttack = hasPrimaryAttack; HasThrow = hasThrow; } internal bool Equals(ShieldHintState other) { if (_gamepad == other._gamepad && HasCharge == other.HasCharge && HasPrimaryAttack == other.HasPrimaryAttack && HasThrow == other.HasThrow) { return string.Equals(_weaponPrefabName, other._weaponPrefabName, StringComparison.Ordinal); } return false; } } private readonly struct ShieldHintRow { internal readonly string Label; internal readonly IReadOnlyList<string> Keys; public ShieldHintRow(string label, IReadOnlyList<string> keys) { Label = label; Keys = keys; } } private static KeyHints? _activeKeyHints; private static readonly List<KeyHintCell> HintCells = new List<KeyHintCell>(); private static readonly List<ShieldHintRow> ReusableRows = new List<ShieldHintRow>(); private static ShieldHintState _lastHintState = ShieldHintState.Hidden; private static bool _hasLastHintState; private static bool _showingHints; internal static void InitializeKeyHints(KeyHints hints) { _activeKeyHints = hints; DestroyHints(); _hasLastHintState = false; _lastHintState = ShieldHintState.Hidden; _showingHints = false; UpdateKeyHint(hints, force: true); } internal static void RefreshKeyHintUi() { if ((Object)(object)_activeKeyHints != (Object)null) { UpdateKeyHint(_activeKeyHints); } } internal static void UpdateKeyHint(KeyHints hints, bool force = false) { if ((Object)(object)hints == (Object)null) { return; } _activeKeyHints = hints; if (!ShouldAllowCustomCombatHints(hints)) { HideHints(); if ((Object)(object)hints.m_combatHints != (Object)null) { hints.m_combatHints.SetActive(false); } return; } if (!TryBuildHintState(out var state) || !state.HasRows) { HideHints(); RememberHintState(ShieldHintState.Hidden); return; } if (!force && _showingHints && _hasLastHintState && _lastHintState.Equals(state)) { PrepareCombatHintGroup(hints); return; } BuildHintRows(state, ReusableRows); EnsureHints(hints, ReusableRows.Count); if (HintCells.Count == 0) { return; } PrepareCombatHintGroup(hints); for (int i = 0; i < HintCells.Count; i++) { KeyHintCell keyHintCell = HintCells[i]; if (i >= ReusableRows.Count) { keyHintCell.SetActive(active: false); continue; } ShieldHintRow shieldHintRow = ReusableRows[i]; keyHintCell.Set(shieldHintRow.Label, shieldHintRow.Keys, 0f, shieldHintRow.Keys.Count <= 1); keyHintCell.RebuildParentLayout(); } RememberHintState(state); _showingHints = true; } private static bool TryBuildHintState(out ShieldHintState state) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Invalid comparison between Unknown and I4 state = ShieldHintState.Hidden; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !ShouldShowCombatHints(localPlayer)) { return false; } ItemData leftItem = ((Humanoid)localPlayer).GetLeftItem(); if (((Humanoid)localPlayer).GetRightItem() != null || leftItem == null || (int)(leftItem.m_shared?.m_itemType).GetValueOrDefault() != 5 || !SecondaryAttackRuntimeFacade.TryGetDefinition(leftItem, out SecondaryAttackDefinition definition) || !(definition.Behavior is ShieldSpecialSecondaryBehavior shieldSpecialSecondaryBehavior)) { return false; } state = new ShieldHintState(((Object)(object)leftItem.m_dropPrefab != (Object)null) ? ((Object)leftItem.m_dropPrefab).name : leftItem.m_shared.m_name, ZInput.IsGamepadActive(), shieldSpecialSecondaryBehavior.HasShieldCharge && shieldSpecialSecondaryBehavior.ShieldChargeDistance > 0f, shieldSpecialSecondaryBehavior.HasShieldPrimaryAttack, shieldSpecialSecondaryBehavior.HasShieldThrow); return state.HasRows; } private static void BuildHintRows(ShieldHintState state, List<ShieldHintRow> rows) { rows.Clear(); if (state.HasCharge) { rows.Add(new ShieldHintRow("Charge", new <>z__ReadOnlyArray<string>(new string[2] { ResolveButtonLabel("Block"), ResolveButtonLabel("SecondaryAttack") }))); } if (state.HasPrimaryAttack) { rows.Add(new ShieldHintRow("Attack", new <>z__ReadOnlySingleElementList<string>(ResolveButtonLabel("Attack")))); } if (state.HasThrow) { rows.Add(new ShieldHintRow("Throw", new <>z__ReadOnlySingleElementList<string>(ResolveButtonLabel("SecondaryAttack")))); } } private static bool ShouldAllowCustomCombatHints(KeyHints hints) { if (hints.m_keyHintsEnabled && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !Game.IsPaused() && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.HasFocus())) { if (!((Object)(object)InventoryGui.instance == (Object)null)) { if (!InventoryGui.instance.IsSkillsPanelOpen && !InventoryGui.instance.IsTrophisPanelOpen) { return !InventoryGui.instance.IsTextPanelOpen; } return false; } return true; } return false; } private static bool ShouldShowCombatHints(Player? player) { if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && !Hud.IsPieceSelectionVisible() && !Hud.InRadial() && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !Game.IsPaused() && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.HasFocus()) && ((Object)(object)InventoryGui.instance == (Object)null || (!InventoryGui.instance.IsSkillsPanelOpen && !InventoryGui.instance.IsTrophisPanelOpen && !InventoryGui.instance.IsTextPanelOpen)) && !PlayerCustomizaton.IsBarberGuiVisible()) { return player.GetDoodadController() == null; } return false; } private static void PrepareCombatHintGroup(KeyHints hints) { if ((Object)(object)hints.m_combatHints != (Object)null) { hints.m_combatHints.SetActive(true); } SetVanillaCombatHintActive(hints.m_bowDrawGP, active: false); SetVanillaCombatHintActive(hints.m_bowDrawKB, active: false); SetVanillaCombatHintActive(hints.m_primaryAttackGP, active: false); SetVanillaCombatHintActive(hints.m_primaryAttackKB, active: false); SetVanillaCombatHintActive(hints.m_secondaryAttackGP, active: false); SetVanillaCombatHintActive(hints.m_secondaryAttackKB, active: false); } private static void SetVanillaCombatHintActive(GameObject? hint, bool active) { if ((Object)(object)hint != (Object)null) { hint.SetActive(active); } } private static void EnsureHints(KeyHints hints, int count) { GameObject val = ResolveCombatHintTemplate(hints); if ((Object)(object)val == (Object)null || (Object)(object)val.transform.parent == (Object)null) { return; } if (HintCells.Count > 0 && (Object)(object)HintCells[0].Root.transform.parent != (Object)(object)val.transform.parent) { DestroyHints(); } while (HintCells.Count < count) { KeyHintCell keyHintCell = KeyHintCell.CloneFrom(val, $"CaptainValheim_ShieldOnlyHint_{HintCells.Count}"); if (keyHintCell == null) { break; } keyHintCell.MoveBefore(val); HintCells.Add(keyHintCell); } foreach (KeyHintCell hintCell in HintCells) { hintCell.MoveBefore(val); } } private static GameObject? ResolveCombatHintTemplate(KeyHints hints) { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown GameObject val = (ZInput.IsGamepadActive() ? hints.m_primaryAttackGP : hints.m_primaryAttackKB); if (KeyHintCell.IsUsableTemplate(val)) { return val; } GameObject val2 = (ZInput.IsGamepadActive() ? hints.m_primaryAttackKB : hints.m_primaryAttackGP); if (KeyHintCell.IsUsableTemplate(val2)) { return val2; } GameObject val3 = (ZInput.IsGamepadActive() ? hints.m_secondaryAttackGP : hints.m_secondaryAttackKB); if (KeyHintCell.IsUsableTemplate(val3)) { return val3; } GameObject val4 = (ZInput.IsGamepadActive() ? hints.m_secondaryAttackKB : hints.m_secondaryAttackGP); if (KeyHintCell.IsUsableTemplate(val4)) { return val4; } if ((Object)(object)hints.m_combatHints == (Object)null) { return null; } foreach (Transform item in KeyHintCell.FindParentWithTemplates(hints.m_combatHints, ZInput.IsGamepadActive() ? "Gamepad" : "Keyboard") ?? KeyHintCell.FindParentWithTemplates(hints.m_combatHints, "Keyboard") ?? KeyHintCell.FindParentWithTemplates(hints.m_combatHints, "Gamepad") ?? hints.m_combatHints.transform) { Transform val5 = item; if (KeyHintCell.IsUsableTemplate(((Component)val5).gameObject)) { return ((Component)val5).gameObject; } } return null; } private static string ResolveButtonLabel(string button) { bool flag = ZInput.IsGamepadActive(); string text = button; if (flag && button.Equals("Attack", StringComparison.OrdinalIgnoreCase)) { text = "JoyAttack"; } else if (flag && button.Equals("Block", StringComparison.OrdinalIgnoreCase)) { text = "JoyBlock"; } else if (flag && button.Equals("SecondaryAttack", StringComparison.OrdinalIgnoreCase)) { text = "JoySecondaryAttack"; } ZInput instance = ZInput.instance; string text2 = ((instance != null) ? instance.GetBoundKeyString(text, true) : null) ?? ""; if (!string.IsNullOrWhiteSpace(text2)) { if (Localization.instance == null) { return text2; } return Localization.instance.Localize(text2); } if (!(button == "Attack")) { if (button == "Block") { return flag ? "LB" : "RMB"; } return flag ? "RB" : "MMB"; } return flag ? "RT" : "LMB"; } private static void HideHints() { if (!_showingHints) { return; } foreach (KeyHintCell hintCell in HintCells) { hintCell.SetActive(active: false); hintCell.RebuildParentLayout(); } _showingHints = false; } private static void DestroyHints() { foreach (KeyHintCell hintCell in HintCells) { if ((Object)(object)hintCell.Root != (Object)null) { Object.Destroy((Object)(object)hintCell.Root); } } HintCells.Clear(); } private static void RememberHintState(ShieldHintState state) { _lastHintState = state; _hasLastHintState = true; } } [HarmonyPatch(typeof(KeyHints), "Awake")] internal static class KeyHintsAwakeShieldOnlyPatch { private static void Postfix(KeyHints __instance) { ShieldOnlyKeyHintSystem.InitializeKeyHints(__instance); } } [HarmonyPatch(typeof(KeyHints), "UpdateHints")] internal static class KeyHintsUpdateShieldOnlyPatch { private static void Postfix(KeyHints __instance) { ShieldOnlyKeyHintSystem.UpdateKeyHint(__instance, force: true); } } internal sealed class ThrowProjectileVisualSpin : MonoBehaviour { internal enum AxisMode { None, HorizontalSide, WorldUp } private const float DegreesPerSecond = 720f; private const float ForwardEpsilonSqr = 0.0001f; private AxisMode _axisMode = AxisMode.HorizontalSide; private Vector3 _horizontalForward; private bool _hasHorizontalForward; private void LateUpdate() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (_axisMode != AxisMode.None) { Vector3 val = ((_axisMode == AxisMode.WorldUp) ? Vector3.up : ResolveHorizontalSideAxis()); ((Component)this).transform.Rotate(val, 720f * Time.deltaTime, (Space)0); } } private Vector3 ResolveHorizontalSideAxis() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) if (_hasHorizontalForward) { return ResolveSideAxis(_horizontalForward); } Transform val = (((Object)(object)((Component)this).transform.parent != (Object)null) ? ((Component)this).transform.parent : ((Component)this).transform); Vector3 forward = Vector3.ProjectOnPlane(val.forward, Vector3.up); if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { forward = Vector3.ProjectOnPlane(val.right, Vector3.up); } if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { return Vector3.right; } return ResolveSideAxis(forward); } private static Vector3 ResolveSideAxis(Vector3 forward) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0041: Unknown result type (might be due to invalid IL or missing references) forward = Vector3.ProjectOnPlane(forward, Vector3.up); if (((Vector3)(ref forward)).sqrMagnitude < 0.001f) { return Vector3.right; } Vector3 val = Vector3.Cross(Vector3.up, ((Vector3)(ref forward)).normalized); if (!(((Vector3)(ref val)).sqrMagnitude > 0.001f)) { return Vector3.right; } return ((Vector3)(ref val)).normalized; } internal static void Ensure(GameObject? visual, AxisMode axisMode = AxisMode.HorizontalSide, Vector3 horizontalForward = default(Vector3)) { //IL_000c: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)visual == (Object)null || IsConfigured(visual, axisMode, horizontalForward)) { return; } if (axisMode == AxisMode.None) { ThrowProjectileVisualSpin component = visual.GetComponent<ThrowProjectileVisualSpin>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } else { ThrowProjectileVisualSpin throwProjectileVisualSpin = visual.GetComponent<ThrowProjectileVisualSpin>() ?? visual.AddComponent<ThrowProjectileVisualSpin>(); throwProjectileVisualSpin._axisMode = axisMode; throwProjectileVisualSpin._horizontalForward = Vector3.ProjectOnPlane(horizontalForward, Vector3.up); throwProjectileVisualSpin._hasHorizontalForward = axisMode == AxisMode.HorizontalSide && ((Vector3)(ref throwProjectileVisualSpin._horizontalForward)).sqrMagnitude > 0.001f; } } internal static bool IsConfigured(GameObject? visual, AxisMode axisMode = AxisMode.HorizontalSide, Vector3 horizontalForward = default(Vector3)) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)visual == (Object)null) { return false; } ThrowProjectileVisualSpin component = visual.GetComponent<ThrowProjectileVisualSpin>(); if (axisMode == AxisMode.None) { return (Object)(object)component == (Object)null; } if ((Object)(object)component != (Object)null && ((Behaviour)component).enabled) { return component.Matches(axisMode, horizontalForward); } return false; } private bool Matches(AxisMode axisMode, Vector3 horizontalForward) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (_axisMode != axisMode) { return false; } Vector3 val = Vector3.ProjectOnPlane(horizontalForward, Vector3.up); bool flag = axisMode == AxisMode.HorizontalSide && ((Vector3)(ref val)).sqrMagnitude > 0.001f; if (_hasHorizontalForward != flag) { return false; } if (flag) { Vector3 val2 = _horizontalForward - val; return ((Vector3)(ref val2)).sqrMagnitude <= 0.0001f; } return true; } } internal static class ProjectileSpinAxis { internal const string None = "none"; internal const string Horizontal = "horizontal"; internal const string Vertical = "vertical"; internal static string Normalize(string? raw) { string text = raw?.Trim() ?? ""; if (text.Length == 0) { return ""; } if (text.Equals("none", StringComparison.OrdinalIgnoreCase)) { return "none"; } if (text.Equals("horizontal", StringComparison.OrdinalIgnoreCase)) { return "horizontal"; } if (!text.Equals("vertical", StringComparison.OrdinalIgnoreCase)) { return ""; } return "vertical"; } internal static bool TryResolveAxisMode(string? raw, out ThrowProjectileVisualSpin.AxisMode axisMode) { string text = Normalize(raw); axisMode = ThrowProjectileVisualSpin.AxisMode.None; return text switch { "none" => Set(ThrowProjectileVisualSpin.AxisMode.None, out axisMode), "horizontal" => Set(ThrowProjectileVisualSpin.AxisMode.HorizontalSide, out axisMode), "vertical" => Set(ThrowProjectileVisualSpin.AxisMode.WorldUp, out axisMode), _ => false, }; } private static bool Set(ThrowProjectileVisualSpin.AxisMode value, out ThrowProjectileVisualSpin.AxisMode axisMode) { axisMode = value; return true; } } internal sealed class ThrowProjectileVisualRotationOffset : MonoBehaviour { private const float OffsetEpsilonSqr = 0.0001f; private bool _hasBaseRotation; private Quaternion _baseLocalRotation; private Vector3 _offset; internal static void Ensure(GameObject? visual, Vector3 offset) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)visual == (Object)null)) { (visual.GetComponent<ThrowProjectileVisualRotationOffset>() ?? visual.AddComponent<ThrowProjectileVisualRotationOffset>()).Apply(offset); } } private void Apply(Vector3 offset) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_0046: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (_hasBaseRotation) { Vector3 val = offset - _offset; if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { return; } } if (!_hasBaseRotation) { _baseLocalRotation = ((Component)this).transform.localRotation; _hasBaseRotation = true; } _offset = offset; ((Component)this).transform.localRotation = _baseLocalRotation * Quaternion.Euler(offset); } } [BepInPlugin("sighsorry.CaptainValheim", "CaptainValheim", "1.0.0")] public class CaptainValheimPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } internal sealed class PluginSettings { internal GeneralSettings General { get; } = new GeneralSettings(); internal void Bind(CaptainValheimPlugin plugin) { General.Bind(plugin); } } internal sealed class GeneralSettings { internal ConfigEntry<Toggle> LockConfiguration; internal void Bind(CaptainValheimPlugin plugin) { LockConfiguration = plugin.config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only."); } } private class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order; [UsedImplicitly] public bool? Browsable; [UsedImplicitly] public string? Category; [UsedImplicitly] public Action<ConfigEntryBase>? CustomDrawer; } private class AcceptableShortcuts : AcceptableValueBase { public AcceptableShortcuts() : base(typeof(KeyboardShortcut)) { } public override object Clamp(object value) { return value; } public override bool IsValid(object value) { return true; } public override string ToDescriptionString() { return "# Acceptable values: " + string.Join(", ", UnityInput.Current.SupportedKeyCodes); } } internal const string ModName = "CaptainValheim"; internal const string ModVersion = "1.0.0"; internal const string Author = "sighsorry"; private const string ModGUID = "sighsorry.CaptainValheim"; private static string ConfigFileName = "sighsorry.CaptainValheim.cfg"; private static string ConfigFileFullPath = Paths.ConfigPath + Path.DirectorySeparatorChar + ConfigFileName; internal static string ConnectionError = ""; private readonly Harmony _harmony = new Harmony("sighsorry.CaptainValheim"); public static readonly ManualLogSource ModLogger = Logger.CreateLogSource("CaptainValheim"); internal static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.CaptainValheim") { DisplayName = "CaptainValheim", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0" }; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private string? _lastConfigFileText; private bool _suppressWorldApplySettingChange; private const long RELOAD_DELAY = 10000000L; private static ConfigEntry<Toggle> _serverConfigLocked = null; internal static PluginSettings Settings { get; } = new PluginSettings(); public void Awake() { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; Settings.Bind(this); RegisterWorldApplySettingHandlers(); _serverConfigLocked = Settings.General.LockConfiguration; ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); PatchCaptainValheimHooks(); SecondaryAttackFacade.Initialize(); SetupWatcher(); ((BaseUnityPlugin)this).Config.Save(); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void OnDestroy() { UnregisterWorldApplySettingHandlers(); SecondaryAttackFacade.Dispose(); SaveWithRespectToConfigSet(); _watcher?.Dispose(); } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = true; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000) { return; } lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { ModLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { string b = File.ReadAllText(ConfigFileFullPath); if (string.Equals(_lastConfigFileText, b, StringComparison.Ordinal)) { return; } _suppressWorldApplySettingChange = true; try { SaveWithRespectToConfigSet(reload: true); } finally { _suppressWorldApplySettingChange = false; } SecondaryAttackFacade.RequestCurrentWorldReapply(); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); ModLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { ModLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = now; } private static string? ReadFileTextIfExists(string path) { if (!File.Exists(path)) { return null; } return File.ReadAllText(path); } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void PatchCaptainValheimHooks() { Type[] array = new Type[15] { typeof(ProjectileUpdateVisualPatch), typeof(ProjectileOnHitPatch), typeof(PlayerUpdatePendingConfigPatch), typeof(ObjectDbAwakePatch), typeof(ObjectDbCopyOtherDbPatch), typeof(HumanoidGetCurrentWeaponPatch), typeof(HumanoidPickupThrownShieldPatch), typeof(HumanoidBlockAttackPatch), typeof(HumanoidStartAttackPatch), typeof(AttackOnAttackTriggerPatch), typeof(AttackDoMeleeAttackSecondaryDurabilityFactorPatch), typeof(AttackDoAreaAttackSecondaryDurabilityFactorPatch), typeof(AttackProjectileAttackTriggeredSecondaryDurabilityFactorPatch), typeof(KeyHintsAwakeShieldOnlyPatch), typeof(KeyHintsUpdateShieldOnlyPatch) }; foreach (Type type in array) { _harmony.CreateClassProcessor(type).Patch(); } } private void RegisterWorldApplySettingHandlers() { } private void UnregisterWorldApplySettingHandlers() { } private void OnWorldApplySettingChanged(object? sender, EventArgs e) { if (!_suppressWorldApplySettingChange) { SecondaryAttackFacade.RequestCurrentWorldReapply(); } } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting; return val2; } private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } } public static class KeyboardExtensions { [SpecialName] public sealed class <G>$8D1D3E80A18AA9715780B6CB7003B2F1 { [SpecialName] public static class <M>$895AB635D4D087636CF1C26BA650BA11 { } [ExtensionMarker("<M>$895AB635D4D087636CF1C26BA650BA11")] public bool IsKeyDown() { throw new NotSupportedException(); } [ExtensionMarker("<M>$895AB635D4D087636CF1C26BA650BA11")] public bool IsKeyHeld() { throw new NotSupportedException(); } } public static bool IsKeyDown(this KeyboardShortcut shortcut) { //IL_0002: 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) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } public static bool IsKeyHeld(this KeyboardShortcut shortcut) { //IL_0002: 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) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } } public static class ToggleExtentions { [SpecialName] public sealed class <G>$9E7C82BC55DED4AA4CD9E4F2D97F4E68 { [SpecialName] public static class <M>$D090B0687CCBD39B38D3C95725B16919 { } [ExtensionMarker("<M>$D090B0687CCBD39B38D3C95725B16919")] public bool IsOn() { throw new NotSupportedException(); } [ExtensionMarker("<M>$D090B0687CCBD39B38D3C95725B16919")] public bool IsOff() { throw new NotSupportedException(); } } public static bool IsOn(this CaptainValheimPlugin.Toggle value) { return value == CaptainValheimPlugin.Toggle.On; } public static bool IsOff(this CaptainValheimPlugin.Toggle value) { return value == CaptainValheimPlugin.Toggle.Off; } } internal static class ProjectileAccess { private static readonly FieldInfo? WeaponField = AccessTools.Field(typeof(Projectile), "m_weapon"); private static readonly FieldInfo? AmmoField = AccessTools.Field(typeof(Projectile), "m_ammo"); private static readonly FieldInfo? OwnerField = AccessTools.Field(typeof(Projectile), "m_owner"); private static readonly FieldInfo? OriginalHitDataField = AccessTools.Field(typeof(Projectile), "m_originalHitData"); private static readonly FieldInfo? StatusEffectHashField = AccessTools.Field(typeof(Projectile), "m_statusEffectHash"); private static readonly FieldInfo? VelocityField = AccessTools.Field(typeof(Projectile), "m_vel"); private static readonly FieldInfo? DidHitField = AccessTools.Field(typeof(Projectile), "m_didHit"); internal static ItemData? GetWeapon(Projectile projectile) { object? obj = WeaponField?.GetValue(projectile); return (ItemData?)((obj is ItemData) ? obj : null); } internal static ItemData? GetAmmo(Projectile projectile) { object? obj = AmmoField?.GetValue(projectile); return (ItemData?)((obj is ItemData) ? obj : null); } internal static Character? GetOwner(Projectile projectile) { object? obj = OwnerField?.GetValue(projectile); return (Character?)((obj is Character) ? obj : null); } internal static HitData? GetOriginalHitData(Projectile projectile) { object? obj = OriginalHitDataField?.GetValue(projectile); return (HitData?)((obj is HitData) ? obj : null); } internal static int GetStatusEffectHash(Projectile projectile) { object obj = StatusEffectHashField?.GetValue(projectile); if (obj is int) { return (int)obj; } return 0; } internal static Vector3 GetVelocity(Projectile projectile) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) object obj = VelocityField?.GetValue(projectile); if (obj is Vector3) { return (Vector3)obj; } return Vector3.zero; } internal static void SetVelocity(Projectile projectile, Vector3 velocity) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) VelocityField?.SetValue(projectile, velocity); } internal static void SetDidHit(Projectile projectile, bool didHit) { DidHitField?.SetValue(projectile, didHit); } } internal static class ProjectileRuntimeSystem { internal readonly struct ProjectileLaunchData { public static readonly ProjectileLaunchData Invalid = new ProjectileLaunchData(null, null, 0f, 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, useRandomVelocity: false); public GameObject? ProjectilePrefab { get; } public ItemData? AmmoItem { get; } public float ProjectileVelocity { get; } public float ProjectileVelocityMin { get; } public float ProjectileAccuracy { get; } public float ProjectileAccuracyMin { get; } public float AttackHitNoise { get; } public float DamageFactor { get; } public float ConfiguredDamageFactor { get; } public float ConfiguredSkillRaiseFactor { get; } public float ConfiguredAdrenalineFactor { get; } public bool UseRandomVelocity { get; } public bool IsValid => (Object)(object)ProjectilePrefab != (Object)null; public ProjectileLaunchData(GameObject? projectilePrefab, ItemData? ammoItem, float projectileVelocity, float projectileVelocityMin, float projectileAccuracy, float projectileAccuracyMin, float attackHitNoise, float damageFactor, float configuredDamageFactor, float configuredSkillRaiseFactor, float configuredAdrenalineFactor, bool useRandomVelocity) { ProjectilePrefab = projectilePrefab; AmmoItem = ammoItem; ProjectileVelocity = projectileVelocity; ProjectileVelocityMin = projectileVelocityMin; ProjectileAccuracy = projectileAccuracy; ProjectileAccuracyMin = projectileAccuracyMin; AttackHitNoise = attackHitNoise; DamageFactor = damageFactor; ConfiguredDamageFactor = configuredDamageFactor; ConfiguredSkillRaiseFactor = configuredSkillRaiseFactor; ConfiguredAdrenalineFactor = configuredAdrenalineFactor; UseRandomVelocity = useRandomVelocity; } } internal static Character? GetHitCharacter(Collider collider) { GameObject obj = Projectile.FindHitObject(collider); if (obj == null) { return null; } return obj.GetComponent<Character>(); } } internal sealed class SecondaryAttackCompiledSnapshot { public static readonly SecondaryAttackCompiledSnapshot Empty = new SecondaryAttackCompiledSnapshot(0, new NormalizedSecondaryAttackConfigFile()); public int SnapshotId { get; } public NormalizedSecondaryAttackConfigFile Config { get; } public IReadOnlyDictionary<string, NormalizedWeaponConfig> Weapons => Config.Weapons; public NormalizedWeaponConfig? GlobalShieldFallback => Config.GlobalShieldFallback; public SecondaryAttackCompiledSnapshot(int snapshotId, NormalizedSecondaryAttackConfigFile config) { SnapshotId = snapshotId; Config = config ?? throw new ArgumentNullException("config"); } } internal sealed class SecondaryAttackAppliedWorldSnapshot { public static readonly SecondaryAttackAppliedWorldSnapshot Empty = new SecondaryAttackAppliedWorldSnapshot(SecondaryAttackCompiledSnapshot.Empty, new Dictionary<string, SecondaryAttackDefinition>(StringComparer.OrdinalIgnoreCase), 0); public SecondaryAttackCompiledSnapshot CompiledSnapshot { get; } public int SnapshotId => CompiledSnapshot.SnapshotId; public int ApplyRevision { get; } public IReadOnlyDictionary<string, SecondaryAttackDefinition> DefinitionsByPrefabName { get; } public SecondaryAttackAppliedWorldSnapshot(SecondaryAttackCompiledSnapshot compiledSnapshot, IReadOnlyDictionary<string, SecondaryAttackDefinition> definitionsByPrefabName, int applyRevision) { CompiledSnapshot = compiledSnapshot ?? throw new ArgumentNullException("compiledSnapshot"); DefinitionsByPrefabName = definitionsByPrefabName ?? throw new ArgumentNullException("definitionsByPrefabName"); ApplyRevision = applyRevision; } } internal enum SecondaryAttackYamlDomainId { Shields } internal sealed class SecondaryAttackYamlDomain { public SecondaryAttackYamlDomainId Id { get; } public string FileName { get; } public string FilePath { get; } public string SyncedIdentifier { get; } public Func<string> GetDefaultContents { get; } internal SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId id, string fileName, string filePath, string syncedIdentifier, Func<string> getDefaultContents) { Id = id; FileName = fileName; FilePath = filePath; SyncedIdentifier = syncedIdentifier; GetDefaultContents = getDefaultContents; } } internal static class SecondaryAttackYamlDomainRegistry { internal const string ShieldsYamlFileName = "CaptainValheim.yml"; private const string SyncedShieldsYamlIdentifier = "captain_valheim_yaml"; internal const long ReloadDelayTicks = 10000000L; internal static readonly string ConfigDirectoryPath = Paths.ConfigPath; internal static readonly string ShieldsYamlFilePath = Path.Combine(ConfigDirectoryPath, "CaptainValheim.yml"); private static readonly SecondaryAttackYamlDomain[] OrderedDomains = new SecondaryAttackYamlDomain[1] { new SecondaryAttackYamlDomain(SecondaryAttackYamlDomainId.Shields, "CaptainValheim.yml", ShieldsYamlFilePath, "captain_valheim_yaml", () => SecondaryAttackDefaultYamlResources.Load("CaptainValheim.yml")) }; private static readonly Dictionary<SecondaryAttackYamlDomainId, SecondaryAttackYamlDomain> DomainsById = OrderedDomains.ToDictionary((SecondaryAttackYamlDomain domain) => domain.Id); public static IReadOnlyList<SecondaryAttackYamlDomain> Domains => OrderedDomains; public static SecondaryAttackYamlDomain Get(SecondaryAttackYamlDomainId id) { return DomainsById[id]; } } internal sealed class SecondaryAttackYamlTexts { private readonly Dictionary<SecondaryAttackYamlDomainId, string> _texts; public IReadOnlyDictionary<SecondaryAttackYamlDomainId, string> All => _texts; public SecondaryAttackYamlTexts(IReadOnlyDictionary<SecondaryAttackYamlDomainId, string> texts) { _texts = new Dictionary<SecondaryAttackYamlDomainId, string>(texts); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { _texts.TryAdd(domain.Id, string.Empty); } } public string Get(SecondaryAttackYamlDomainId id) { if (!_texts.TryGetValue(id, out string value)) { return string.Empty; } return value; } public string GetContentFingerprint() { StringBuilder stringBuilder = new StringBuilder(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { string text = Get(domain.Id); stringBuilder.Append((int)domain.Id).Append(':').Append(text.Length) .Append(':') .Append(text) .Append('\n'); } return stringBuilder.ToString(); } } internal sealed class SecondaryAttackParsedYaml { public IReadOnlyDictionary<string, ShieldWeaponConfig> Shields { get; set; } = new Dictionary<string, ShieldWeaponConfig>(StringComparer.OrdinalIgnoreCase); } internal static class SecondaryAttackConfigLoader { private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); public static void EnsureLocalFilesExist() { Directory.CreateDirectory(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { if (!File.Exists(domain.FilePath)) { File.WriteAllText(domain.FilePath, domain.GetDefaultContents()); } } } public static SecondaryAttackYamlTexts ReadLocalYamlTexts() { Dictionary<SecondaryAttackYamlDomainId, string> dictionary = new Dictionary<SecondaryAttackYamlDomainId, string>(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { dictionary[domain.Id] = File.ReadAllText(domain.FilePath); } return new SecondaryAttackYamlTexts(dictionary); } public static bool TryCompileSnapshot(int snapshotId, SecondaryAttackYamlTexts yamlTexts, out SecondaryAttackCompiledSnapshot? snapshot) { snapshot = null; if (!TryParseYamlTexts(yamlTexts, out SecondaryAttackParsedYaml parsedYaml)) { return false; } snapshot = SecondaryAttackConfigCompiler.Compile(snapshotId, parsedYaml); return true; } private static bool TryParseYamlTexts(SecondaryAttackYamlTexts yamlTexts, out SecondaryAttackParsedYaml? parsedYaml) { parsedYaml = null; if (!TryParseDictionary(SecondaryAttackYamlDomainId.Shields, yamlTexts.Get(SecondaryAttackYamlDomainId.Shields), out Dictionary<string, ShieldWeaponConfig> parsed)) { return false; } parsedYaml = new SecondaryAttackParsedYaml { Shields = parsed }; return true; } private static bool TryParseDictionary<T>(SecondaryAttackYamlDomainId domainId, string yamlText, out Dictionary<string, T>? parsed) { parsed = null; if (string.IsNullOrWhiteSpace(yamlText)) { parsed = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); return true; } try { parsed = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); YamlStream yamlStream = new YamlStream(); yamlStream.Load(new StringReader(yamlText)); if (yamlStream.Documents.Count == 0 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { return true; } SecondaryAttackYamlDomain secondaryAttackYamlDomain = SecondaryAttackYamlDomainRegistry.Get(domainId); foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { string text = (child.Key as YamlScalarNode)?.Value?.Trim() ?? ""; if (!string.IsNullOrWhiteSpace(text)) { try { Dictionary<string, T>? obj = parsed; T val = DeserializeYamlNode<T>(child.Value); obj[text] = ((val != null) ? val : Activator.CreateInstance<T>()); } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogWarning((object)("Skipping " + secondaryAttackYamlDomain.FileName + " block '" + text + "': " + ex.Message)); } } } return true; } catch (Exception ex2) { SecondaryAttackYamlDomain secondaryAttackYamlDomain2 = SecondaryAttackYamlDomainRegistry.Get(domainId); CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse " + secondaryAttackYamlDomain2.FileName + ": " + ex2.Message)); return false; } } private static T? DeserializeYamlNode<T>(YamlNode node) { using StringWriter stringWriter = new StringWriter(); new YamlStream(new YamlDocument(node)).Save(stringWriter, assignAnchors: false); return Deserializer.Deserialize<T>(stringWriter.ToString()); } } internal static class SecondaryAttackAdrenalineSystem { private sealed class AttackAdrenalineState { internal readonly HashSet<string> GrantedKeys = new HashSet<string>(); } private static readonly ConditionalWeakTable<Attack, AttackAdrenalineState> AttackStates = new ConditionalWeakTable<Attack, AttackAdrenalineState>(); internal static void Reset(Attack attack) { if (attack != null) { AttackStates.Remove(attack); } } internal static float ResolveFactor(ActiveSecondaryAttack activeAttack) { if (!(activeAttack.Definition.Behavior is ShieldSpecialSecondaryBehavior shieldSpecialSecondaryBehavior)) { return 1f; } return activeAttack.ShieldMode switch { ShieldSpecialMode.PrimaryAttack => shieldSpecialSecondaryBehavior.ShieldPrimaryAttackAdrenalineFactor, ShieldSpecialMode.Charge => shieldSpecialSecondaryBehavior.ShieldChargeAdrenalineFactor, _ => shieldSpecialSecondaryBehavior.ShieldThrowAdrenalineFactor, }; } internal static bool TryGrantOnceRaw(Attack attack, Character target, float baseAdrenaline, float factor, string key) { if ((Object)(object)attack?.m_character == (Object)null || (Object)(object)target == (Object)null || target.m_enemyAdrenalineMultiplier <= 0f || baseAdrenaline <= 0f || factor <= 0f || !TryMarkGranted(attack, key)) { return false; } ((Character)attack.m_character).AddAdrenaline(baseAdrenaline * Mathf.Max(0f, factor) * target.m_enemyAdrenalineMultiplier); return true; } private static bool TryMarkGranted(Attack attack, string key) { return AttackStates.GetValue(attack, (Attack _) => new AttackAdrenalineState()).GrantedKeys.Add(key); } } internal sealed class SecondaryAttackWeaponNormalizationResult { public Dictionary<string, NormalizedWeaponConfig> Weapons { get; set; } = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase); public NormalizedWeaponConfig? GlobalShieldFallback { get; set; } } internal static class SecondaryAttackWeaponConfigNormalizer { private const string GlobalFallbackKey = "Global"; internal static SecondaryAttackWeaponNormalizationResult Normalize(IReadOnlyDictionary<string, ShieldWeaponConfig> shields) { Dictionary<string, NormalizedWeaponConfig> dictionary = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase); NormalizedWeaponConfig normalizedWeaponConfig = CreateGlobalDefaultShieldFallback(); ShieldWeaponConfig shieldWeaponConfig = null; string key; ShieldWeaponConfig value; foreach (KeyValuePair<string, ShieldWeaponConfig> shield in shields) { shield.Deconstruct(out key, out value); string text = key; ShieldWeaponConfig shieldWeaponConfig2 = value; if (!string.IsNullOrWhiteSpace(text) && shieldWeaponConfig2 != null && text.Trim().Equals("Global", StringComparison.OrdinalIgnoreCase)) { shieldWeaponConfig = shieldWeaponConfig2; break; } } NormalizedWeaponConfig normalizedWeaponConfig2 = ((shieldWeaponConfig != null) ? FromShieldRaw(shieldWeaponConfig, normalizedWeaponConfig) : null); NormalizedWeaponConfig fallback = normalizedWeaponConfig2 ?? normalizedWeaponConfig; foreach (KeyValuePair<string, ShieldWeaponConfig> shield2 in shields) { shield2.Deconstruct(out key, out value); string text2 = key; ShieldWeaponConfig shieldWeaponConfig3 = value; if (!string.IsNullOrWhiteSpace(text2) && shieldWeaponConfig3 != null) { string text3 = text2.Trim(); if (!text3.Equals("Global", StringComparison.OrdinalIgnoreCase)) { dictionary[text3] = FromShieldRaw(shieldWeaponConfig3, fallback); } } } return new SecondaryAttackWeaponNormalizationResult { Weapons = dictionary, GlobalShieldFallback = normalizedWeaponConfig2 }; } public static NormalizedWeaponConfig FromShieldRaw(ShieldWeaponConfig raw, NormalizedWeaponConfig? fallback = null) { if (fallback == null) { fallback = CreateGlobalDefaultShieldFallback(); } return new NormalizedWeaponConfig { Shield = NormalizeShield(raw, fallback.Shield ?? new NormalizedShieldModeConfig()) }; } public static NormalizedWeaponConfig CreateGlobalDefaultShieldFallback() { return new NormalizedWeaponConfig { Shield = new NormalizedShieldModeConfig { PrimaryAttack = new NormalizedShieldPrimaryAttackConfig(), Throw = new NormalizedShieldThrowConfig(), Charge = new NormalizedShieldChargeConfig(), Reflect = new NormalizedShieldReflectConfig(), BlockCharge = new NormalizedShieldBlockChargeConfig() } }; } private static NormalizedShieldModeConfig NormalizeShield(ShieldWeaponConfig rawShield, NormalizedShieldModeConfig fallback) { return new NormalizedShieldModeConfig { PrimaryAttack = NormalizeShieldPrimaryAttack(rawShield.PrimaryAttack, fallback.PrimaryAttack), Throw = NormalizeShieldThrow(rawShield.Throw, fallback.Throw), Charge = NormalizeShieldCharge(rawShield.Charge, fallback.Charge), Reflect = NormalizeShieldReflect(rawShield.Reflect, fallback.Reflect), BlockCharge = NormalizeShieldBlockCharge(rawShield.BlockCharge, fallback.BlockCharge) }; } private static NormalizedShieldPrimaryAttackConfig? NormalizeShieldPrimaryAttack(ShieldPrimaryAttackConfig? rawPrimaryAttack, NormalizedShieldPrimaryAttackConfig? fallback) { if (rawPrimaryAttack == null) { return fallback; } if (rawPrimaryAttack.Enabled == false) { return null; } NormalizedShieldPrimaryAttackConfig normalizedShieldPrimaryAttackConfig = fallback ?? new NormalizedShieldPrimaryAttackConfig(); return new NormalizedShieldPrimaryAttackConfig { DamageFactor = (rawPrimaryAttack.DamageFactor ?? normalizedShieldPrimaryAttackConfig.DamageFactor), PushFactor = (rawPrimaryAttack.PushFactor ?? normalizedShieldPrimaryAttackConfig.PushFactor), StaminaFactor = (rawPrimaryAttack.StaminaFactor ?? normalizedShieldPrimaryAttackConfig.StaminaFactor), DurabilityFactor = (rawPrimaryAttack.DurabilityFactor ?? normalizedShieldPrimaryAttackConfig.DurabilityFactor), AdrenalineFactor = 1f }; } private static NormalizedShieldThrowConfig? NormalizeShieldThrow(ShieldThrowConfig? rawThrow, NormalizedShieldThrowConfig? fallback) { if (rawThrow == null) { return fallback; } if (rawThrow.Enabled == false) { return null; } NormalizedShieldThrowConfig normalizedShieldThrowConfig = fallback ?? new NormalizedShieldThrowConfig(); return new NormalizedShieldThrowConfig { Animation = ((!string.IsNullOrWhiteSpace(rawThrow.Animation)) ? rawThrow.Animation.Trim() : normalizedShieldThrowConfig.Animation), DamageFactor = (rawThrow.DamageFactor ?? normalizedShieldThrowConfig.DamageFactor), PushFactor = (rawThrow.PushFactor ?? normalizedShieldThrowConfig.PushFactor), StaminaFactor = (rawThrow.StaminaFactor ?? normalizedShieldThrowConfig.StaminaFactor), DurabilityFactor = (rawThrow.DurabilityFactor ?? normalizedShieldThrowConfig.DurabilityFactor), Targets = (rawThrow.Targets ?? normalizedShieldThrowConfig.Targets), DamageDecay = (rawThrow.DamageDecay ?? normalizedShieldThrowConfig.DamageDecay), RadiusFactor = (rawThrow.RadiusFactor ?? normalizedShieldThrowConfig.RadiusFactor), TtlFactor = (rawThrow.TtlFactor ?? normalizedShieldThrowConfig.TtlFactor), AdrenalineFactor = 1f }; } private static NormalizedShieldChargeConfig? NormalizeShieldCharge(ShieldChargeConfig? rawCharge, NormalizedShieldChargeConfig? fallback) { if (rawCharge == null) { return fallback; } if (rawCharge.Enabled == false) { return null; } NormalizedShieldChargeConfig normalizedShieldChargeConfig = fallback ?? new NormalizedShieldChargeConfig(); return new NormalizedShieldChargeConfig { DamageFactor = (rawCharge.DamageFactor ?? normalizedShieldChargeConfig.DamageFactor), PushFactor = (rawCharge.PushFactor ?? normalizedShieldChargeConfig.PushFactor), StaminaFactor = (rawCharge.StaminaFactor ?? normalizedShieldChargeConfig.StaminaFactor), Distance = (rawCharge.Distance ?? normalizedShieldChargeConfig.Distance), Speed = (rawCharge.Speed ?? normalizedShieldChargeConfig.Speed), Cooldown = (rawCharge.Cooldown ?? normalizedShieldChargeConfig.Cooldown), CooldownReductionFactor = (rawCharge.CooldownReductionFactor ?? normalizedShieldChargeConfig.CooldownReductionFactor), DurabilityFactor = (rawCharge.DurabilityFactor ?? normalizedShieldChargeConfig.DurabilityFactor), HitRadiusFactor = (rawCharge.HitRadiusFactor ?? normalizedShieldChargeConfig.HitRadiusFactor), AdrenalineFactor = 5f }; } private static NormalizedShieldReflectConfig? NormalizeShieldReflect(ShieldReflectConfig? rawReflect, NormalizedShieldReflectConfig? fallback) { if (rawReflect == null) { return fallback; } if (rawReflect.Enabled == false) { return null; } NormalizedShieldReflectConfig normalizedShieldReflectConfig = fallback ?? new NormalizedShieldReflectConfig(); return new NormalizedShieldReflectConfig { StaminaFactor = (rawReflect.StaminaFactor ?? normalizedShieldReflectConfig.StaminaFactor), ReflectionFactor = (rawReflect.ReflectionFactor ?? normalizedShieldReflectConfig.ReflectionFactor) }; } private static NormalizedShieldBlockChargeConfig? NormalizeShieldBlockCharge(ShieldBlockChargeConfig? rawBlockCharge, NormalizedShieldBlockChargeConfig? fallback) { if (rawBlockCharge == null) { return fallback; } if (rawBlockCharge.Enabled == false) { return null; } return new NormalizedShieldBlockChargeConfig { ChargeCount = (rawBlockCharge.ChargeCount ?? fallback?.ChargeCount), DecayTime = (rawBlockCharge.DecayTime ?? fallback?.DecayTime), BlockingDecayFactor = (rawBlockCharge.BlockingDecayFactor ?? fallback?.BlockingDecayFactor) }; } } internal static class SecondaryAttackFacade { private enum YamlAuthorityMode { LocalFiles, SyncedOnly } private static readonly object ReloadLock = new object(); private static FileSystemWatcher? _watcher; private static readonly Dictionary<SecondaryAttackYamlDomainId, CustomSyncedValue<string>> SyncedYamlValues = new Dictionary<SecondaryAttackYamlDomainId, CustomSyncedValue<string>>(); private static SecondaryAttackCompiledSnapshot _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; private static SecondaryAttackCompiledSnapshot? _pendingCompiledSnapshot; private static SecondaryAttackAppliedWorldSnapshot _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; private static DateTime _lastYamlReloadTime; private static bool _hasPendingConfig; private static bool _hasPendingWorldReapply; private static int _nextSnapshotId = 1; private static bool _suppressSyncedYamlChanged; private static YamlAuthorityMode _yamlAuthorityMode; private static string _currentYamlFingerprint = string.Empty; private static string? _pendingYamlFingerprint; internal static SecondaryAttackCompiledSnapshot CurrentCompiledSnapshot => _currentCompiledSnapshot; internal static SecondaryAttackAppliedWorldSnapshot CurrentAppliedWorldSnapshot => _currentAppliedWorldSnapshot; public static void Initialize() { SecondaryAttackConfigLoader.EnsureLocalFilesExist(); InitializeSyncedYamlValues(); RefreshYamlAuthorityMode(force: true); } public static void Dispose() { DisposeSyncedYamlValues(); _watcher?.Dispose(); _watcher = null; } public static void ApplyToObjectDb(ObjectDB objectDb, bool emitMissingWarnings) { RefreshYamlAuthorityMode(); ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings); } internal static void TryApplyPendingConfig() { RefreshYamlAuthorityMode(); if (!CommitPendingConfig(force: false, applyToObjectDbImmediately: true)) { CommitPendingWorldReapply(force: false); } } internal static void RequestCurrentWorldReapply() { lock (ReloadLock) { StageWorldReapply(); } } internal static void ApplyPendingConfigToObjectDb(ObjectDB objectDb, bool emitMissingWarnings) { RefreshYamlAuthorityMode(); bool num = CommitPendingConfig(force: true, applyToObjectDbImmediately: false); ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings); if (num) { CaptainValheimPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); } } internal static void ApplyPendingConfigToZNetScene(ZNetScene scene, bool emitMissingWarnings) { RefreshYamlAuthorityMode(); bool num = CommitPendingConfig(force: true, applyToObjectDbImmediately: false); ApplyCompiledSnapshotToZNetScene(scene, _currentCompiledSnapshot, emitMissingWarnings); if ((Object)(object)ObjectDB.instance != (Object)null) { ApplyCompiledSnapshotToObjectDb(ObjectDB.instance, _currentCompiledSnapshot, emitMissingWarnings, applyZNetScene: false); } if (num) { CaptainValheimPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); } } private static void SetupWatcher() { if (_watcher == null) { Directory.CreateDirectory(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath); _watcher = new FileSystemWatcher(SecondaryAttackYamlDomainRegistry.ConfigDirectoryPath, "CaptainValheim.yml"); _watcher.Changed += OnYamlFileChanged; _watcher.Created += OnYamlFileChanged; _watcher.Renamed += OnYamlFileChanged; _watcher.IncludeSubdirectories = false; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } } private static void OnYamlFileChanged(object sender, FileSystemEventArgs e) { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles) { return; } DateTime now = DateTime.Now; if (now.Ticks - _lastYamlReloadTime.Ticks < 10000000) { return; } lock (ReloadLock) { ReloadLocalYaml(); _lastYamlReloadTime = now; } } private static void ReloadLocalYaml() { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles) { return; } SecondaryAttackConfigLoader.EnsureLocalFilesExist(); SecondaryAttackYamlTexts secondaryAttackYamlTexts = SecondaryAttackConfigLoader.ReadLocalYamlTexts(); if (SyncedYamlValues.Count == SecondaryAttackYamlDomainRegistry.Domains.Count) { _suppressSyncedYamlChanged = true; try { foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { SyncedYamlValues[domain.Id].AssignLocalValue(secondaryAttackYamlTexts.Get(domain.Id)); } } finally { _suppressSyncedYamlChanged = false; } } ApplyYamlTexts(secondaryAttackYamlTexts); } private static void OnSyncedYamlChanged() { if (!_suppressSyncedYamlChanged) { ApplyYamlTexts(ReadSyncedYamlTexts()); } } private static void RefreshYamlAuthorityMode(bool force = false) { YamlAuthorityMode yamlAuthorityMode = DetermineYamlAuthorityMode(); if (!force && yamlAuthorityMode == _yamlAuthorityMode) { return; } _yamlAuthorityMode = yamlAuthorityMode; switch (yamlAuthorityMode) { case YamlAuthorityMode.LocalFiles: SetupWatcher(); ReloadLocalYaml(); CaptainValheimPlugin.ModLogger.LogInfo((object)"CaptainValheim YAML authority mode: LocalFiles."); break; case YamlAuthorityMode.SyncedOnly: DisposeWatcher(); if (AnySyncedYamlHasValue()) { ApplyYamlTexts(ReadSyncedYamlTexts()); } else { _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; _hasPendingConfig = false; _hasPendingWorldReapply = false; _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; _currentYamlFingerprint = string.Empty; _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; if ((Object)(object)ZNetScene.instance != (Object)null) { ApplyCompiledSnapshotToZNetScene(ZNetScene.instance, _currentCompiledSnapshot, emitMissingWarnings: true); } } CaptainValheimPlugin.ModLogger.LogInfo((object)"CaptainValheim YAML authority mode: SyncedOnly."); break; } } private static YamlAuthorityMode DetermineYamlAuthorityMode() { if (!((Object)(object)ZNet.instance != (Object)null) || ZNet.instance.IsServer()) { return YamlAuthorityMode.LocalFiles; } return YamlAuthorityMode.SyncedOnly; } private static void InitializeSyncedYamlValues() { DisposeSyncedYamlValues(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { CustomSyncedValue<string> customSyncedValue = new CustomSyncedValue<string>(CaptainValheimPlugin.ConfigSync, domain.SyncedIdentifier, ""); customSyncedValue.ValueChanged += OnSyncedYamlChanged; SyncedYamlValues[domain.Id] = customSyncedValue; } } private static void DisposeSyncedYamlValues() { foreach (CustomSyncedValue<string> value in SyncedYamlValues.Values) { value.ValueChanged -= OnSyncedYamlChanged; } SyncedYamlValues.Clear(); } private static SecondaryAttackYamlTexts ReadSyncedYamlTexts() { Dictionary<SecondaryAttackYamlDomainId, string> dictionary = new Dictionary<SecondaryAttackYamlDomainId, string>(); foreach (SecondaryAttackYamlDomain domain in SecondaryAttackYamlDomainRegistry.Domains) { dictionary[domain.Id] = (SyncedYamlValues.TryGetValue(domain.Id, out CustomSyncedValue<string> value) ? value.Value : string.Empty); } return new SecondaryAttackYamlTexts(dictionary); } private static bool AnySyncedYamlHasValue() { return SyncedYamlValues.Values.Any((CustomSyncedValue<string> syncedValue) => !string.IsNullOrEmpty(syncedValue.Value)); } private static void DisposeWatcher() { if (_watcher != null) { _watcher.Dispose(); _watcher = null; } } private static void ApplyYamlTexts(SecondaryAttackYamlTexts yamlTexts) { string contentFingerprint = yamlTexts.GetContentFingerprint(); if (!string.Equals(_currentYamlFingerprint, contentFingerprint, StringComparison.Ordinal) && (!_hasPendingConfig || !string.Equals(_pendingYamlFingerprint, contentFingerprint, StringComparison.Ordinal)) && SecondaryAttackConfigLoader.TryCompileSnapshot(_nextSnapshotId++, yamlTexts, out SecondaryAttackCompiledSnapshot snapshot)) { StageConfig(snapshot, contentFingerprint); } } private static void StageConfig(SecondaryAttackCompiledSnapshot snapshot, string fingerprint) { _pendingCompiledSnapshot = snapshot; _pendingYamlFingerprint = fingerprint; _hasPendingConfig = true; CommitPendingConfig(force: true, applyToObjectDbImmediately: true); } private static void StageWorldReapply() { _hasPendingWorldReapply = true; CommitPendingWorldReapply(force: true); } private static bool CommitPendingConfig(bool force, bool applyToObjectDbImmediately) { if (!_hasPendingConfig || _pendingCompiledSnapshot == null) { return false; } if (!force && !CanApplyPendingConfigNow()) { return false; } _currentCompiledSnapshot = _pendingCompiledSnapshot; _currentYamlFingerprint = _pendingYamlFingerprint ?? _currentYamlFingerprint; _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; _hasPendingConfig = false; if (applyToObjectDbImmediately && (Object)(object)ObjectDB.instance != (Object)null) { ApplyCompiledSnapshotToObjectDb(ObjectDB.instance, _currentCompiledSnapshot, emitMissingWarnings: true); } CaptainValheimPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); return true; } private static bool CommitPendingWorldReapply(bool force) { if (!_hasPendingWorldReapply) { return false; } if (!force && !CanApplyPendingConfigNow()) { return false; } if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } ApplyCompiledSnapshotToObjectDb(ObjectDB.instance, _currentCompiledSnapshot, emitMissingWarnings: true); CaptainValheimPlugin.ModLogger.LogInfo((object)"Applied staged world-apply config changes."); return true; } private static void ApplyCompiledSnapshotToObjectDb(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings, bool applyZNetScene = true) { _hasPendingWorldReapply = false; if (applyZNetScene && (Object)(object)ZNetScene.instance != (Object)null) { ApplyCompiledSnapshotToZNetScene(ZNetScene.instance, compiledSnapshot, emitMissingWarnings); } _currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, compiledSnapshot, emitMissingWarnings); } private static void ApplyCompiledSnapshotToZNetScene(ZNetScene scene, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { SecondaryAttackWorldApplyContributors.ApplyToZNetScene(scene, compiledSnapshot, emitMissingWarnings); } private static bool CanApplyPendingConfigNow() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return true; } if (((Humanoid)localPlayer).m_currentAttack != null) { return false; } if (!ShieldRuntimeSystem.IsShieldChargeActiveForDebug((Humanoid)(object)localPlayer)) { return !SecondaryAttackManager.HasActiveAsyncSecondaryWorkForFacade((Character?)(object)localPlayer); } return false; } } internal static class SecondaryAttackConfigCompiler { public static SecondaryAttackCompiledSnapshot Compile(int snapshotId, SecondaryAttackParsedYaml parsedYaml) { return Compile(snapshotId, parsedYaml.Shields); } public static SecondaryAttackCompiledSnapshot Compile(int snapshotId, IReadOnlyDictionary<string, ShieldWeaponConfig> parsedShields) { return new SecondaryAttackCompiledSnapshot(snapshotId, SecondaryAttackNormalizedConfigFacade.FromParsed(parsedShields)); } } internal readonly struct SecondaryAttackDefinitionBuildContext { public ObjectDB ObjectDb { get; } public bool EmitMissingWarnings { get; } public SecondaryAttackDefinitionBuildContext(ObjectDB objectDb, bool emitMissingWarnings) { ObjectDb = objectDb; EmitMissingWarnings = emitMissingWarnings; } } internal static class SecondaryAttackDefinitionCompiler { private readonly struct DefinitionFeatures { public bool HasShieldConfig { get; } public bool UsesShieldSpecials { get; } public bool UsesShieldReflect { get; } public bool UsesShieldBlockCharge { get; } public bool WantsSecondaryOverride => UsesShieldSpecials; public DefinitionFeatures(bool hasShieldConfig, bool usesShieldSpecials, bool usesShieldReflect, bool usesShieldBlockCharge) { HasShieldConfig = hasShieldConfig; UsesShieldSpecials = usesShieldSpecials; UsesShieldReflect = usesShieldReflect; UsesShieldBlockCharge = usesShieldBlockCharge; } } private enum DefinitionValidationDisposition { Continue, Skip, EffectOnly } private readonly struct DefinitionValidationResult { public DefinitionValidationDisposition Disposition { get; } public Attack? PrimaryAttack { get; } public DefinitionValidationResult(DefinitionValidationDisposition disposition, Attack? primaryAttack = null) { Disposition = disposition; PrimaryAttack = primaryAttack; } } internal static bool TryCreateDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, ItemDrop itemDrop, NormalizedWeaponConfig weaponConfig, out SecondaryAttackDefinition? definition) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) definition = null; SharedData val = itemDrop.m_itemData?.m_shared; if (val == null) { return false; } DefinitionFeatures features = AnalyzeDefinitionFeatures(weaponConfig); DefinitionValidationResult definitionValidationResult = ValidateDefinitionRequest(prefabName, val, weaponConfig, features); switch (definitionValidationResult.Disposition) { case DefinitionValidationDisposition.EffectOnly: definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; case DefinitionValidationDisposition.Skip: return false; default: return TryCreateValidatedDefinition(buildContext, prefabName, val, (Attack)(((object)definitionValidationResult.PrimaryAttack) ?? ((object)new Attack())), weaponConfig, features, out definition); } } private static DefinitionFeatures AnalyzeDefinitionFeatures(NormalizedWeaponConfig weaponConfig) { bool hasShieldConfig = weaponConfig.Shield != null; bool usesShieldSpecials = weaponConfig.Shield?.PrimaryAttack != null || weaponConfig.Shield?.Throw != null || weaponConfig.Shield?.Charge != null; bool usesShieldReflect = weaponConfig.Shield?.Reflect != null; bool usesShieldBlockCharge = weaponConfig.Shield?.BlockCharge != null; return new DefinitionFeatures(hasShieldConfig, usesShieldSpecials, usesShieldReflect, usesShieldBlockCharge); } private static bool TryCreateValidatedDefinition(SecondaryAttackDefinitionBuildContext buildContext, string prefabName, SharedData sharedData, Attack primaryAttack, NormalizedWeaponConfig weaponConfig, DefinitionFeatures features, out SecondaryAttackDefinition? definition) { definition = null; if (features.UsesShieldSpecials) { return SecondaryAttackManager.TryCreateShieldSpecialDefinition(prefabName, sharedData, weaponConfig, out definition); } if (features.UsesShieldReflect || features.UsesShieldBlockCharge) { definition = SecondaryAttackManager.CreateEffectOnlyDefinition(prefabName, weaponConfig); return true; } return false; } private static DefinitionValidationResult ValidateDefinitionRequest(string prefabName, SharedData sharedData, NormalizedWeaponConfig weaponConfig, DefinitionFeatures features) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (!features.HasShieldConfig) { return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } if ((int)sharedData.m_itemType != 5) { CaptainValheimPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": CaptainValheim shield features can only be used on shield prefabs.")); return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } if (!features.WantsSecondaryOverride) { if (!features.UsesShieldReflect && !features.UsesShieldBlockCharge) { return new DefinitionValidationResult(DefinitionValidationDisposition.Skip); } return new DefinitionValidationResult(DefinitionValidationDisposition.EffectOnly); } return new DefinitionValidationResult(DefinitionValidationDisposition.Continue, (Attack?)(((object)sharedData.m_attack) ?? ((object)new Attack()))); } } internal sealed class NormalizedSecondaryAttackConfigFile { public Dictionary<string, NormalizedWeaponConfig> Weapons { get; set; } = new Dictionary<string, NormalizedWeaponConfig>(StringComparer.OrdinalIgnoreCase); public NormalizedWeaponConfig? GlobalShieldFallback { get; set; } } internal static class SecondaryAttackNormalizedConfigFacade { internal static NormalizedSecondaryAttackConfigFile FromParsed(IReadOnlyDictionary<string, ShieldWeaponConfig> shields) { SecondaryAttackWeaponNormalizationResult secondaryAttackWeaponNormalizationResult = SecondaryAttackWeaponConfigNormalizer.Normalize(shields); return new NormalizedSecondaryAttackConfigFile { Weapons = secondaryAttackWeaponNormalizationResult.Weapons, GlobalShieldFallback = secondaryAttackWeaponNormalizationResult.GlobalShieldFallback }; } } internal sealed class NormalizedShieldModeConfig { public NormalizedShieldPrimaryAttackConfig? PrimaryAttack { get; set; } public NormalizedShieldThrowConfig? Throw { get; set; } public NormalizedShieldChargeConfig? Charge { get; set; } public NormalizedShieldReflectConfig? Reflect { get; set; } public NormalizedShieldBlockChargeConfig? BlockCharge { get; set; } } internal sealed class NormalizedShieldPrimaryAttackConfig { public float DamageFactor { get; set; } = 0.4f; public float PushFactor { get; set; } = 1f; public float StaminaFactor { get; set; } = 0.8f; public float DurabilityFactor { get; set; } = 1f; public float AdrenalineFactor { get; set; } = 1f; } internal sealed class NormalizedShieldThrowConfig { public string Animation { get; set; } = "battleaxe_attack1"; public float DamageFactor { get; set; } = 0.8f; public float PushFactor { get; set; } = 1f; public float StaminaFactor { get; set; } = 2f; public float DurabilityFactor { get; set; } = 1f; public int Targets { get; set; } = 3; public float DamageDecay { get; set; } = 0.5f; public float RadiusFactor { get; set; } = 6f; public float TtlFactor { get; set; } = 1f; public float AdrenalineFactor { get; set; } = 1f; } internal sealed class NormalizedShieldChargeConfig { public float DamageFactor { get; set; } = 1.2f; public float PushFactor { get; set; } = 2f; public float StaminaFactor { get; set; } = 3f; public float Distance { get; set; } = 4f; public float Speed { get; set; } = 12f; public float Cooldown { get; set; } = 10f; public float CooldownReductionFactor { get; set; } = 0.5f; public float DurabilityFactor { get; set; } = 2f; public float HitRadiusFactor { get; set; } = 0.4f; public float AdrenalineFactor { get; set; } = 5f; } internal static class ShieldAdrenalineFactors { public const float PrimaryAttack = 1f; public const float Throw = 1f; public const float Charge = 5f; } internal sealed class NormalizedShieldReflectConfig { public float StaminaFactor { get; set; } = 2f; public float ReflectionFactor { get; set; } = 0.01f; } internal sealed class NormalizedShieldBlockChargeConfig { public int? ChargeCount { get; set; } public float? DecayTime { get; set; } public float? BlockingDecayFactor { get; set; } } internal sealed class NormalizedWeaponConfig { public NormalizedShieldModeConfig? Shield { get; set; } } internal sealed class ShieldWeaponConfig { public ShieldPrimaryAttackConfig? PrimaryAttack { get; set; } public ShieldThrowConfig? Throw { get; set; } public ShieldChargeConfig? Charge { get; set; } public ShieldReflectConfig? Reflect { get; set; } public ShieldBlockChargeConfig? BlockCharge { get; set; } } internal sealed class ShieldPrimaryAttackConfig { public bool? Enabled { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } public float? StaminaFactor { get; set; } public float? DurabilityFactor { get; set; } } internal sealed class ShieldThrowConfig { public bool? Enabled { get; set; } public string? Animation { get; set; } public float? DamageFactor { get; set; } public float? PushFactor { get; set; } public float? StaminaFactor { get; set; } public float? DurabilityFactor { get; set; } public int? Targets { get; set; } public float? DamageDecay { get; set; } public float? RadiusFactor { get; set; } public float? TtlFactor { get; set; } } internal sealed class ShieldChargeConfig { public bool? Enabled { get; set; } public float? Damage