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.8
CaptainValheim.dll
Decompiled a month 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.8")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.8.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; } } } namespace CaptainValheim { internal static class CaptainValheimLocalization { private const string EnglishLanguage = "English"; private const string ResourcePrefix = "CaptainValheim.translations."; private const string ExternalFilePrefix = "CaptainValheim."; private const string TranslationFileExtension = ".yml"; private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreFields().Build(); private static readonly Dictionary<string, Dictionary<string, string>?> TranslationCache = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); private static bool _reportedMissingEnglish; internal static void Load() { if (ReadEmbeddedTranslations("English") == null) { ReportMissingEnglish(); } if (Localization.instance != null) { ApplyLanguage(Localization.instance); } } internal static string Text(string key) { return Localize("$" + key); } internal static string Localize(string token) { if (string.IsNullOrWhiteSpace(token)) { return token ?? string.Empty; } Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(token) : null) ?? token; if (!IsMissingTranslation(text, token)) { return text; } string translationKey = GetTranslationKey(token); Dictionary<string, string> dictionary = ReadEmbeddedTranslations("English"); if (dictionary != null && dictionary.TryGetValue(translationKey, out var value)) { return value; } return text; } internal static void ApplyLanguage(Localization localization) { if (localization == null) { return; } string selectedLanguage = localization.GetSelectedLanguage(); string text = (string.IsNullOrWhiteSpace(selectedLanguage) ? "English" : selectedLanguage); try { Dictionary<string, string> dictionary = ReadEmbeddedTranslations("English"); if (dictionary == null) { ReportMissingEnglish(); return; } Dictionary<string, string> dictionary2 = new Dictionary<string, string>(dictionary, StringComparer.Ordinal); if (!string.Equals(text, "English", StringComparison.OrdinalIgnoreCase)) { Dictionary<string, string> dictionary3 = ReadEmbeddedTranslations(text); if (dictionary3 != null) { OverlayTranslations(dictionary2, dictionary3); } } Dictionary<string, string> dictionary4 = ReadExternalTranslations(text); if (dictionary4 != null) { OverlayTranslations(dictionary2, dictionary4); } foreach (KeyValuePair<string, string> item in dictionary2) { localization.AddWord(item.Key, item.Value); } } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to load localization for '" + text + "': " + ex.Message)); } } private static Dictionary<string, string>? ReadExternalTranslations(string language) { if (!IsSafeLanguageFileComponent(language)) { CaptainValheimPlugin.ModLogger.LogWarning((object)("Skipped external localization for unsafe language name '" + language + "'.")); return null; } string logicalFileName = "CaptainValheim." + language + ".yml"; string directoryName; try { directoryName = Path.GetDirectoryName(typeof(CaptainValheimPlugin).Assembly.Location); } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to resolve the plugin directory for external localization: " + ex.Message)); return null; } if (string.IsNullOrWhiteSpace(directoryName) || !Directory.Exists(directoryName)) { return null; } string text2; try { string text = Path.Combine(directoryName, logicalFileName); text2 = (File.Exists(text) ? text : (from path in Directory.EnumerateFiles(directoryName, "*", SearchOption.TopDirectoryOnly) where string.Equals(Path.GetFileName(path), logicalFileName, StringComparison.OrdinalIgnoreCase) select path).OrderBy<string, string>(Path.GetFileName, StringComparer.Ordinal).FirstOrDefault()); } catch (Exception ex2) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to search for external localization '" + logicalFileName + "': " + ex2.Message)); return null; } if (text2 == null) { return null; } try { using StreamReader streamReader = new StreamReader(text2, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); Dictionary<string, string> dictionary = Deserializer.Deserialize<Dictionary<string, string>>(streamReader.ReadToEnd()); if (dictionary == null) { throw new InvalidDataException("the document root must be a translation mapping."); } foreach (KeyValuePair<string, string> item in dictionary) { if (string.IsNullOrWhiteSpace(item.Key) || item.Value == null) { throw new InvalidDataException("translation keys must be non-empty and values cannot be null."); } } return dictionary; } catch (Exception ex3) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to read external localization '" + logicalFileName + "': " + ex3.Message)); return null; } } private static bool IsSafeLanguageFileComponent(string language) { if (!string.IsNullOrWhiteSpace(language) && language.IndexOf("..", StringComparison.Ordinal) < 0 && language.IndexOf('/') < 0 && language.IndexOf('\\') < 0) { return language.IndexOfAny(Path.GetInvalidFileNameChars()) < 0; } return false; } private static void OverlayTranslations(Dictionary<string, string> target, Dictionary<string, string> overlay) { foreach (KeyValuePair<string, string> item in overlay) { if (target.ContainsKey(item.Key)) { target[item.Key] = item.Value; } else { CaptainValheimPlugin.ModLogger.LogWarning((object)("Ignored unknown localization key '" + item.Key + "'.")); } } } private static Dictionary<string, string>? ReadEmbeddedTranslations(string language) { if (TranslationCache.TryGetValue(language, out Dictionary<string, string> value)) { return value; } Assembly assembly = typeof(CaptainValheimPlugin).Assembly; string resourceSuffix = "CaptainValheim.translations." + language + ".yml"; string text = assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(resourceSuffix, StringComparison.OrdinalIgnoreCase)); if (text == null) { TranslationCache[language] = null; return null; } using Stream stream = assembly.GetManifestResourceStream(text); if (stream == null) { TranslationCache[language] = null; return null; } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); Dictionary<string, string> dictionary = Deserializer.Deserialize<Dictionary<string, string>>(streamReader.ReadToEnd()); TranslationCache[language] = dictionary; return dictionary; } private static bool IsMissingTranslation(string localized, string token) { if (string.IsNullOrWhiteSpace(localized) || string.Equals(localized, token, StringComparison.Ordinal)) { return true; } if (token[0] == '$') { return string.Equals(localized, "[" + GetTranslationKey(token) + "]", StringComparison.Ordinal); } return false; } private static string GetTranslationKey(string token) { if (token.Length <= 0 || token[0] != '$') { return token; } return token.Substring(1); } private static void ReportMissingEnglish() { if (!_reportedMissingEnglish) { _reportedMissingEnglish = true; CaptainValheimPlugin.ModLogger.LogError((object)"Embedded English localization resource was not found."); } } } [HarmonyPatch(typeof(Localization), "SetupLanguage")] internal static class LocalizationSetupLanguageCaptainValheimPatch { private static void Postfix(Localization __instance) { CaptainValheimLocalization.ApplyLanguage(__instance); } } 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, bool hideExtraTexts = false) { EnsureKeyCount(keys.Count); Root.SetActive(true); if ((Object)(object)_label != (Object)null) { SetText(_label, label); } 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 val = _generatedSeparatorTexts[j]; if (!((Object)(object)val == (Object)null)) { bool flag2 = j < keys.Count - 1; ((Component)val).gameObject.SetActive(flag2); if (flag2) { SetText(val, "+"); } } } 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) { int count2 = _keys.Count; GameObject val2 = Object.Instantiate<GameObject>(val, parent, false); ((Object)val2).name = ((_keys.Count == 1) ? "key_bkg (1)" : $"key_bkg ({_keys.Count})"); RefreshChildren(); if (_keys.Count <= count2) { val2.SetActive(false); val2.transform.SetParent((Transform)null, false); Object.Destroy((Object)(object)val2); RefreshChildren(); 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 = "$captainvalheim_shield_charge_cooldown_name"; private const string Tooltip = "$captainvalheim_shield_charge_cooldown_tooltip"; private const string FallbackIconPrefabName = "ShieldWood"; private static readonly ConditionalWeakTable<StatusEffect, object> OwnedStatusEffects = new ConditionalWeakTable<StatusEffect, object>(); 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")) { StatusEffect val = ScriptableObject.CreateInstance<StatusEffect>(); ((Object)val).name = "CaptainValheim_Cooldown_shieldCharge"; val.m_name = "$captainvalheim_shield_charge_cooldown_name"; val.m_tooltip = "$captainvalheim_shield_charge_cooldown_tooltip"; val.m_icon = ResolveIcon(objectDb, "ShieldWood"); objectDb.m_StatusEffects.Add(val); OwnedStatusEffects.Add(val, new object()); } } internal static void UnregisterStatusEffect(ObjectDB objectDb) { if ((Object)(object)objectDb == (Object)null) { return; } for (int num = objectDb.m_StatusEffects.Count - 1; num >= 0; num--) { StatusEffect val = objectDb.m_StatusEffects[num]; if (val != null && OwnedStatusEffects.TryGetValue(val, out object _)) { objectDb.m_StatusEffects.RemoveAt(num); OwnedStatusEffects.Remove(val); Object.Destroy((Object)(object)val); } } } 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 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 _language; private readonly bool _gamepad; internal readonly bool HasCharge; internal readonly bool HasPrimaryAttack; internal readonly bool HasThrow; internal readonly string AttackKey; internal readonly string BlockKey; internal readonly string SecondaryAttackKey; internal bool HasRows { get { if (!HasCharge && !HasPrimaryAttack) { return HasThrow; } return true; } } public ShieldHintState(string language, bool gamepad, bool hasCharge, bool hasPrimaryAttack, bool hasThrow, string attackKey, string blockKey, string secondaryAttackKey) { _language = language; _gamepad = gamepad; HasCharge = hasCharge; HasPrimaryAttack = hasPrimaryAttack; HasThrow = hasThrow; AttackKey = attackKey; BlockKey = blockKey; SecondaryAttackKey = secondaryAttackKey; } internal bool Equals(ShieldHintState other) { if (_gamepad == other._gamepad && HasCharge == other.HasCharge && HasPrimaryAttack == other.HasPrimaryAttack && HasThrow == other.HasThrow && string.Equals(AttackKey, other.AttackKey, StringComparison.Ordinal) && string.Equals(BlockKey, other.BlockKey, StringComparison.Ordinal) && string.Equals(SecondaryAttackKey, other.SecondaryAttackKey, StringComparison.Ordinal)) { return string.Equals(_language, other._language, 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 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) { DestroyHints(); _hasLastHintState = false; _lastHintState = ShieldHintState.Hidden; _showingHints = false; UpdateKeyHint(hints); } internal static void UpdateKeyHint(KeyHints hints) { if ((Object)(object)hints == (Object)null) { return; } Player localPlayer = Player.m_localPlayer; if (!ShouldShowCustomCombatHints(hints, localPlayer)) { HideHints(); if ((Object)(object)hints.m_combatHints != (Object)null) { hints.m_combatHints.SetActive(false); } return; } if (!TryBuildHintState(localPlayer, out var state) || !state.HasRows) { HideHints(); RememberHintState(ShieldHintState.Hidden); return; } if (_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, shieldHintRow.Keys.Count <= 1); } HintCells[0].RebuildParentLayout(); RememberHintState(state); _showingHints = true; } private static bool TryBuildHintState(Player player, out ShieldHintState state) { //IL_0035: 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_0048: Invalid comparison between Unknown and I4 state = ShieldHintState.Hidden; ItemData leftItem = ((Humanoid)player).GetLeftItem(); if (((Humanoid)player).GetRightItem() == null && leftItem != null && (int)(leftItem.m_shared?.m_itemType).GetValueOrDefault() == 5 && ShieldRuntimeSystem.TryGetDefinition(leftItem, out SecondaryAttackDefinition definition)) { ShieldSpecialSecondaryBehavior shieldSpecial = definition.ShieldSpecial; if (shieldSpecial != null) { Localization instance = Localization.instance; state = new ShieldHintState(((instance != null) ? instance.GetSelectedLanguage() : null) ?? "", ZInput.IsGamepadActive(), shieldSpecial.HasShieldCharge && shieldSpecial.ShieldChargeDistance > 0f, shieldSpecial.HasShieldPrimaryAttack, shieldSpecial.HasShieldThrow, ResolveButtonLabel("Attack"), ResolveButtonLabel("Block"), ResolveButtonLabel("SecondaryAttack")); return state.HasRows; } } return false; } private static void BuildHintRows(ShieldHintState state, List<ShieldHintRow> rows) { rows.Clear(); if (state.HasCharge) { rows.Add(new ShieldHintRow(CaptainValheimLocalization.Text("captainvalheim_hint_charge"), new <>z__ReadOnlyArray<string>(new string[2] { state.BlockKey, state.SecondaryAttackKey }))); } if (state.HasPrimaryAttack) { rows.Add(new ShieldHintRow(CaptainValheimLocalization.Text("captainvalheim_hint_attack"), new <>z__ReadOnlySingleElementList<string>(state.AttackKey))); } if (state.HasThrow) { rows.Add(new ShieldHintRow(CaptainValheimLocalization.Text("captainvalheim_hint_throw"), new <>z__ReadOnlySingleElementList<string>(state.SecondaryAttackKey))); } } private static bool ShouldShowCustomCombatHints(KeyHints hints, Player? player) { if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && hints.m_keyHintsEnabled && !((Character)player).InPlaceMode() && !Hud.IsPieceSelectionVisible() && !Hud.InRadial() && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Console.IsVisible() && !Game.IsPaused() && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.IsChatDialogWindowVisible()) && ((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); } if (HintCells.Count > 0) { HintCells[0].RebuildParentLayout(); } _showingHints = false; } internal static void Dispose() { DestroyHints(); ReusableRows.Clear(); _lastHintState = ShieldHintState.Hidden; _hasLastHintState = false; _showingHints = false; } private static void DestroyHints() { foreach (KeyHintCell hintCell in HintCells) { if ((Object)(object)hintCell.Root != (Object)null) { hintCell.Root.SetActive(false); hintCell.Root.transform.SetParent((Transform)null, false); 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); } } internal static class ShieldTechniqueCompendiumManager { private enum ShieldTechniqueGroup { ActiveAttacks, DefensiveTechniques } private sealed class ShieldTechniqueInfo(ShieldTechniqueGroup group, string iconPrefabName, string nameToken, string descriptionToken) { internal ShieldTechniqueGroup Group { get; } = group; internal string IconPrefabName { get; } = iconPrefabName; internal string NameToken { get; } = nameToken; internal string DescriptionToken { get; } = descriptionToken; } private const string PageTopic = "$captainvalheim_compendium_title"; private const string BodyIconPrefix = "CaptainValheim_CompendiumTechniqueIcon_"; private const char IconMarker = '*'; private const float IconSize = 18f; private const float IconTextGap = 5f; private static readonly IReadOnlyList<ShieldTechniqueInfo> Entries = new <>z__ReadOnlyArray<ShieldTechniqueInfo>(new ShieldTechniqueInfo[5] { new ShieldTechniqueInfo(ShieldTechniqueGroup.ActiveAttacks, "ShieldWood", "$captainvalheim_compendium_primary_attack_name", "$captainvalheim_compendium_primary_attack_description"), new ShieldTechniqueInfo(ShieldTechniqueGroup.ActiveAttacks, "ShieldBanded", "$captainvalheim_compendium_throw_name", "$captainvalheim_compendium_throw_description"), new ShieldTechniqueInfo(ShieldTechniqueGroup.ActiveAttacks, "ShieldIronTower", "$captainvalheim_compendium_charge_name", "$captainvalheim_compendium_charge_description"), new ShieldTechniqueInfo(ShieldTechniqueGroup.DefensiveTechniques, "ShieldSerpentscale", "$captainvalheim_compendium_reflect_name", "$captainvalheim_compendium_reflect_description"), new ShieldTechniqueInfo(ShieldTechniqueGroup.DefensiveTechniques, "ShieldCarapaceBuckler", "$captainvalheim_compendium_block_charge_name", "$captainvalheim_compendium_block_charge_description") }); private static readonly List<GameObject> BodyIcons = new List<GameObject>(); private static readonly List<TextsDialog> PageDialogs = new List<TextsDialog>(); internal static void AddTechniquePage(TextsDialog dialog) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown if (dialog?.m_texts != null) { TrackDialog(dialog); dialog.m_texts.RemoveAll((TextInfo text) => IsTechniquePage(text?.m_topic)); dialog.m_texts.Add(new TextInfo("$captainvalheim_compendium_title", BuildPageText())); } } internal static void RefreshPageContentIcons(TextsDialog dialog, TextInfo info) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dialog?.m_textArea == (Object)null || info == null) { return; } ClearPageContentIcons(dialog); if (!IsTechniquePage(info.m_topic)) { return; } TMP_Text textArea = dialog.m_textArea; Transform parent = textArea.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val == (Object)null) { return; } textArea.ForceMeshUpdate(false, false); LayoutRebuilder.ForceRebuildLayoutImmediate(val); textArea.ForceMeshUpdate(false, false); TMP_TextInfo textInfo = textArea.textInfo; int num = 0; for (int i = 0; i < textInfo.characterCount; i++) { if (num >= Entries.Count) { break; } TMP_CharacterInfo val2 = textInfo.characterInfo[i]; if (val2.character == '*') { ShieldTechniqueInfo entry = Entries[num]; AttachBodyIcon(val, textArea.rectTransform, val2, ResolveIcon(entry), num); num++; } } } internal static void ClearPageContentIcons(TextsDialog dialog) { DestroyTrackedIcons(); if (!((Object)(object)dialog?.m_textArea == (Object)null)) { ClearIconChildren(dialog.m_textArea.transform); if ((Object)(object)dialog.m_textArea.transform.parent != (Object)null) { ClearIconChildren(dialog.m_textArea.transform.parent); } } } internal static void Dispose() { DestroyTrackedIcons(); foreach (TextsDialog pageDialog in PageDialogs) { if ((Object)(object)pageDialog == (Object)null) { continue; } pageDialog.m_texts?.RemoveAll((TextInfo text) => IsTechniquePage(text?.m_topic)); if (!((Object)(object)pageDialog.m_textArea == (Object)null)) { ClearIconChildren(pageDialog.m_textArea.transform); if ((Object)(object)pageDialog.m_textArea.transform.parent != (Object)null) { ClearIconChildren(pageDialog.m_textArea.transform.parent); } } } PageDialogs.Clear(); } private static string BuildPageText() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<color=#D6D6D6>").Append(CaptainValheimLocalization.Localize("$captainvalheim_compendium_intro")).Append("</color>\n\n"); ShieldTechniqueGroup? shieldTechniqueGroup = null; foreach (ShieldTechniqueInfo entry in Entries) { if (shieldTechniqueGroup != entry.Group) { if (shieldTechniqueGroup.HasValue) { stringBuilder.Append('\n'); } stringBuilder.Append("<color=#FFD27A><b>").Append(LocalizeGroupHeading(entry.Group)).Append("</b></color>\n\n"); shieldTechniqueGroup = entry.Group; } stringBuilder.Append("<color=#00000000>").Append('*').Append("</color> ") .Append("<color=orange><b>") .Append(CaptainValheimLocalization.Localize(entry.NameToken)) .Append("</b></color>\n ") .Append(CaptainValheimLocalization.Localize(entry.DescriptionToken)) .Append("\n\n"); } return stringBuilder.ToString().TrimEnd(); } private static string LocalizeGroupHeading(ShieldTechniqueGroup group) { return group switch { ShieldTechniqueGroup.ActiveAttacks => CaptainValheimLocalization.Localize("$captainvalheim_compendium_group_active"), ShieldTechniqueGroup.DefensiveTechniques => CaptainValheimLocalization.Localize("$captainvalheim_compendium_group_defensive"), _ => "", }; } private static void AttachBodyIcon(RectTransform content, RectTransform textArea, TMP_CharacterInfo marker, Sprite? sprite, int index) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)content == (Object)null) && !((Object)(object)textArea == (Object)null) && !((Object)(object)sprite == (Object)null)) { GameObject val = new GameObject(string.Format("{0}{1}", "CaptainValheim_CompendiumTechniqueIcon_", index), new Type[3] { typeof(RectTransform), typeof(Image), typeof(LayoutElement) }) { layer = ((Component)textArea).gameObject.layer }; RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)content, false); val2.anchorMin = content.pivot; val2.anchorMax = content.pivot; val2.pivot = new Vector2(0.5f, 0.5f); val2.sizeDelta = new Vector2(18f, 18f); Vector3 val3 = (marker.bottomLeft + marker.topLeft) * 0.5f; val3.x += 14f; Vector3 val4 = ((Transform)textArea).TransformPoint(val3); Vector3 val5 = ((Transform)content).InverseTransformPoint(val4); val2.anchoredPosition = new Vector2(val5.x, val5.y); Image component = val.GetComponent<Image>(); component.sprite = sprite; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; val.GetComponent<LayoutElement>().ignoreLayout = true; BodyIcons.Add(val); } } private static void DestroyTrackedIcons() { foreach (GameObject bodyIcon in BodyIcons) { if (!((Object)(object)bodyIcon == (Object)null)) { bodyIcon.SetActive(false); bodyIcon.transform.SetParent((Transform)null, false); Object.Destroy((Object)(object)bodyIcon); } } BodyIcons.Clear(); } private static void ClearIconChildren(Transform parent) { for (int num = parent.childCount - 1; num >= 0; num--) { Transform child = parent.GetChild(num); if (((Object)child).name.StartsWith("CaptainValheim_CompendiumTechniqueIcon_", StringComparison.Ordinal)) { ((Component)child).gameObject.SetActive(false); child.SetParent((Transform)null, false); Object.Destroy((Object)(object)((Component)child).gameObject); } } } private static void TrackDialog(TextsDialog dialog) { PageDialogs.RemoveAll((TextsDialog trackedDialog) => (Object)(object)trackedDialog == (Object)null); if (!PageDialogs.Contains(dialog)) { PageDialogs.Add(dialog); } } private static Sprite? ResolveIcon(ShieldTechniqueInfo entry) { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject obj = ObjectDB.instance.GetItemPrefab(entry.IconPrefabName) ?? ObjectDB.instance.GetItemPrefab("ShieldWood"); Sprite[] array = ((obj != null) ? obj.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } private static bool IsTechniquePage(string? topic) { return string.Equals(topic, "$captainvalheim_compendium_title", StringComparison.Ordinal); } } [HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")] internal static class TextsDialogUpdateTextsListShieldTechniquePatch { private static void Postfix(TextsDialog __instance) { ShieldTechniqueCompendiumManager.AddTechniquePage(__instance); } } [HarmonyPatch(typeof(TextsDialog), "ShowText", new Type[] { typeof(TextInfo) })] internal static class TextsDialogShowTextShieldTechniquePatch { private static void Postfix(TextsDialog __instance, TextInfo text) { ShieldTechniqueCompendiumManager.RefreshPageContentIcons(__instance, text); } } [HarmonyPatch(typeof(TextsDialog), "OnClose")] internal static class TextsDialogOnCloseShieldTechniquePatch { private static void Postfix(TextsDialog __instance) { ShieldTechniqueCompendiumManager.ClearPageContentIcons(__instance); } } internal static class ShieldTooltipSystem { internal static void AppendShieldGuidance(ItemData? item, ref string tooltip) { if (CaptainValheimPlugin.Settings.General.ShowShieldTooltip.Value == CaptainValheimPlugin.Toggle.On && HasEnabledShieldAttack(item)) { string text = CaptainValheimLocalization.Text("captainvalheim_shield_tooltip"); string text2 = CaptainValheimLocalization.Text("captainvalheim_shield_tooltip_note"); string text3 = text + "\n" + text2; tooltip = (string.IsNullOrWhiteSpace(tooltip) ? text3 : (tooltip + "\n\n" + text3)); } } private static bool HasEnabledShieldAttack(ItemData? item) { //IL_001b: 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_002e: Invalid comparison between Unknown and I4 if (item == null || (int)(item.m_shared?.m_itemType).GetValueOrDefault() != 5 || !ShieldRuntimeSystem.TryGetDefinition(item, out SecondaryAttackDefinition definition)) { return false; } ShieldSpecialSecondaryBehavior shieldSpecial = definition.ShieldSpecial; if ((shieldSpecial == null || !shieldSpecial.HasShieldPrimaryAttack) && (shieldSpecial == null || !shieldSpecial.HasShieldThrow)) { if (shieldSpecial != null && shieldSpecial.HasShieldCharge) { return shieldSpecial.ShieldChargeDistance > 0f; } return false; } return true; } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] internal static class ItemDataGetTooltipShieldGuidancePatch { private static void Postfix(ItemData item, ref string __result) { ShieldTooltipSystem.AppendShieldGuidance(item, ref __result); } } [BepInPlugin("sighsorry.CaptainValheim", "CaptainValheim", "1.0.8")] 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 ConfigEntry<Toggle> ShowShieldTooltip; 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."); ShowShieldTooltip = plugin.config("1 - General", "Show Shield Tooltip", Toggle.On, "Shows CaptainValheim guidance on shield item tooltips.", synchronizedSetting: false); } } internal const string ModName = "CaptainValheim"; internal const string ModVersion = "1.0.8"; 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; 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.8", MinimumRequiredVersion = "1.0.8" }; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _configReloadDueUtc; private bool _hasPendingConfigReload; private string? _lastConfigFileText; private static readonly TimeSpan ConfigReloadDelay = TimeSpan.FromSeconds(1.0); 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); _serverConfigLocked = Settings.General.LockConfiguration; ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); PatchCaptainValheimHooks(); CaptainValheimLocalization.Load(); SecondaryAttackFacade.Initialize(); SetupWatcher(); ((BaseUnityPlugin)this).Config.Save(); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void Update() { TryReloadConfigValues(); SecondaryAttackFacade.TryApplyPendingConfig(); } private void OnDestroy() { RunCleanup("secondary attack state", SecondaryAttackFacade.Dispose); RunCleanup("configuration save", delegate { SaveWithRespectToConfigSet(); }); RunCleanup("configuration watcher", DisposeWatcher); RunCleanup("shield key hints", ShieldOnlyKeyHintSystem.Dispose); RunCleanup("shield compendium", ShieldTechniqueCompendiumManager.Dispose); RunCleanup("Harmony patches", (Action)_harmony.UnpatchSelf); } private static void RunCleanup(string operation, Action cleanup) { try { cleanup(); } catch (Exception ex) { ModLogger.LogError((object)("Failed to clean up " + operation + ": " + ex.Message)); } } 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 DisposeWatcher() { FileSystemWatcher watcher = _watcher; _watcher = null; if (watcher == null) { return; } watcher.EnableRaisingEvents = false; watcher.Changed -= ReadConfigValues; watcher.Created -= ReadConfigValues; watcher.Renamed -= ReadConfigValues; watcher.Dispose(); lock (_reloadLock) { _hasPendingConfigReload = false; _configReloadDueUtc = default(DateTime); } } private void ReadConfigValues(object sender, FileSystemEventArgs e) { lock (_reloadLock) { _hasPendingConfigReload = true; _configReloadDueUtc = DateTime.UtcNow + ConfigReloadDelay; } } private void TryReloadConfigValues() { lock (_reloadLock) { if (!_hasPendingConfigReload || DateTime.UtcNow < _configReloadDueUtc) { return; } _hasPendingConfigReload = false; 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)) { SaveWithRespectToConfigSet(reload: true); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); ModLogger.LogInfo((object)"Configuration reload complete."); } } catch (Exception ex) { ModLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } } 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; try { if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void PatchCaptainValheimHooks() { _harmony.PatchAll(typeof(CaptainValheimPlugin).Assembly); } 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); } } internal static class ProjectileAccess { private static readonly FieldInfo? AmmoField = AccessTools.Field(typeof(Projectile), "m_ammo"); 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"); internal static ItemData? GetAmmo(Projectile projectile) { object? obj = AmmoField?.GetValue(projectile); return (ItemData?)((obj is ItemData) ? 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 Character? GetHitCharacter(Collider collider) { if (!((Object)(object)collider != (Object)null)) { return null; } 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(new Dictionary<string, NormalizedShieldModeConfig>(StringComparer.OrdinalIgnoreCase), null); public IReadOnlyDictionary<string, NormalizedShieldModeConfig> Shields { get; } public NormalizedShieldModeConfig? GlobalShieldFallback { get; } public SecondaryAttackCompiledSnapshot(IReadOnlyDictionary<string, NormalizedShieldModeConfig> shields, NormalizedShieldModeConfig? globalShieldFallback) { Shields = shields ?? throw new ArgumentNullException("shields"); GlobalShieldFallback = globalShieldFallback; } } internal sealed class SecondaryAttackAppliedWorldSnapshot { public static readonly SecondaryAttackAppliedWorldSnapshot Empty = new SecondaryAttackAppliedWorldSnapshot(new Dictionary<string, SecondaryAttackDefinition>(StringComparer.OrdinalIgnoreCase)); public IReadOnlyDictionary<string, SecondaryAttackDefinition> DefinitionsByPrefabName { get; } public SecondaryAttackAppliedWorldSnapshot(IReadOnlyDictionary<string, SecondaryAttackDefinition> definitionsByPrefabName) { DefinitionsByPrefabName = definitionsByPrefabName ?? throw new ArgumentNullException("definitionsByPrefabName"); } } internal static class SecondaryAttackConfigLoader { private const string DefaultResourcePrefix = "CaptainValheim.Resources.Defaults."; private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); public static void EnsureLocalFileExists() { Directory.CreateDirectory(SecondaryAttackYamlConfig.DirectoryPath); if (!File.Exists(SecondaryAttackYamlConfig.FilePath)) { File.WriteAllText(SecondaryAttackYamlConfig.FilePath, LoadEmbeddedDefault("CaptainValheim.yml")); } } public static string ReadLocalYamlText() { return File.ReadAllText(SecondaryAttackYamlConfig.FilePath); } public static bool TryCompileSnapshot(string yamlText, out SecondaryAttackCompiledSnapshot? snapshot) { snapshot = null; if (!TryParseDictionary(yamlText, out Dictionary<string, ShieldWeaponConfig> parsed)) { return false; } if (!TryValidateFiniteValues(parsed, out string invalidValuePath)) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml: '" + invalidValuePath + "' must be a finite number.")); return false; } try { snapshot = SecondaryAttackWeaponConfigNormalizer.Normalize(parsed); return true; } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to compile CaptainValheim.yml: " + ex.Message)); return false; } } private static bool TryParseDictionary(string yamlText, out Dictionary<string, ShieldWeaponConfig>? parsed) { parsed = null; if (string.IsNullOrWhiteSpace(yamlText)) { CaptainValheimPlugin.ModLogger.LogError((object)"Failed to parse CaptainValheim.yml: the document cannot be empty."); return false; } try { parsed = new Dictionary<string, ShieldWeaponConfig>(StringComparer.OrdinalIgnoreCase); YamlStream yamlStream = new YamlStream(); yamlStream.Load(new StringReader(yamlText)); if (yamlStream.Documents.Count != 1 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { CaptainValheimPlugin.ModLogger.LogError((object)"Failed to parse CaptainValheim.yml: the document root must be a single mapping."); return false; } foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { string text = (child.Key as YamlScalarNode)?.Value?.Trim() ?? ""; if (string.IsNullOrWhiteSpace(text)) { CaptainValheimPlugin.ModLogger.LogError((object)"Failed to parse CaptainValheim.yml: every root block must have a non-empty scalar key."); return false; } if (!(child.Value is YamlMappingNode)) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml block '" + text + "': the block must be a mapping.")); return false; } if (parsed.ContainsKey(text)) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml: duplicate root block '" + text + "'.")); return false; } try { ShieldWeaponConfig shieldWeaponConfig = DeserializeYamlNode(child.Value); if (shieldWeaponConfig == null) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml block '" + text + "': the block is empty or invalid.")); return false; } parsed.Add(text, shieldWeaponConfig); } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml block '" + text + "': " + ex.Message)); return false; } } return true; } catch (Exception ex2) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to parse CaptainValheim.yml: " + ex2.Message)); return false; } } private static ShieldWeaponConfig? DeserializeYamlNode(YamlNode node) { using StringWriter stringWriter = new StringWriter(); new YamlStream(new YamlDocument(node)).Save(stringWriter, assignAnchors: false); return Deserializer.Deserialize<ShieldWeaponConfig>(stringWriter.ToString()); } private static bool TryValidateFiniteValues(IReadOnlyDictionary<string, ShieldWeaponConfig> shields, out string invalidValuePath) { foreach (var (text2, shieldWeaponConfig2) in shields) { if (TryFindNonFiniteValue(out invalidValuePath, (text2 + ".primaryAttack.damageFactor", shieldWeaponConfig2.PrimaryAttack?.DamageFactor), (text2 + ".primaryAttack.pushFactor", shieldWeaponConfig2.PrimaryAttack?.PushFactor), (text2 + ".primaryAttack.staminaFactor", shieldWeaponConfig2.PrimaryAttack?.StaminaFactor), (text2 + ".primaryAttack.durabilityFactor", shieldWeaponConfig2.PrimaryAttack?.DurabilityFactor), (text2 + ".throw.damageFactor", shieldWeaponConfig2.Throw?.DamageFactor), (text2 + ".throw.pushFactor", shieldWeaponConfig2.Throw?.PushFactor), (text2 + ".throw.staminaFactor", shieldWeaponConfig2.Throw?.StaminaFactor), (text2 + ".throw.durabilityFactor", shieldWeaponConfig2.Throw?.DurabilityFactor), (text2 + ".throw.damageDecay", shieldWeaponConfig2.Throw?.DamageDecay), (text2 + ".throw.radiusFactor", shieldWeaponConfig2.Throw?.RadiusFactor), (text2 + ".throw.ttlFactor", shieldWeaponConfig2.Throw?.TtlFactor), (text2 + ".charge.damageFactor", shieldWeaponConfig2.Charge?.DamageFactor), (text2 + ".charge.pushFactor", shieldWeaponConfig2.Charge?.PushFactor), (text2 + ".charge.staminaFactor", shieldWeaponConfig2.Charge?.StaminaFactor), (text2 + ".charge.distance", shieldWeaponConfig2.Charge?.Distance), (text2 + ".charge.speed", shieldWeaponConfig2.Charge?.Speed), (text2 + ".charge.cooldown", shieldWeaponConfig2.Charge?.Cooldown), (text2 + ".charge.cooldownReductionFactor", shieldWeaponConfig2.Charge?.CooldownReductionFactor), (text2 + ".charge.durabilityFactor", shieldWeaponConfig2.Charge?.DurabilityFactor), (text2 + ".charge.hitRadiusFactor", shieldWeaponConfig2.Charge?.HitRadiusFactor), (text2 + ".reflect.staminaFactor", shieldWeaponConfig2.Reflect?.StaminaFactor), (text2 + ".reflect.reflectionFactor", shieldWeaponConfig2.Reflect?.ReflectionFactor), (text2 + ".blockCharge.decayTime", shieldWeaponConfig2.BlockCharge?.DecayTime), (text2 + ".blockCharge.blockingDecayFactor", shieldWeaponConfig2.BlockCharge?.BlockingDecayFactor))) { return false; } } invalidValuePath = string.Empty; return true; } private static bool TryFindNonFiniteValue(out string invalidValuePath, params (string Path, float? Value)[] values) { for (int i = 0; i < values.Length; i++) { var (text, num) = values[i]; if (num.HasValue && (float.IsNaN(num.Value) || float.IsInfinity(num.Value))) { invalidValuePath = text; return true; } } invalidValuePath = string.Empty; return false; } private static string LoadEmbeddedDefault(string fileName) { string text = "CaptainValheim.Resources.Defaults." + fileName; using Stream stream = typeof(SecondaryAttackConfigLoader).Assembly.GetManifestResourceStream(text); if (stream == null) { throw new InvalidOperationException("Embedded default YAML resource '" + text + "' was not found."); } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); return streamReader.ReadToEnd(); } } internal static class SecondaryAttackYamlConfig { internal const string FileName = "CaptainValheim.yml"; internal const string SyncedIdentifier = "captain_valheim_yaml"; internal const long ReloadDelayTicks = 10000000L; internal static readonly string DirectoryPath = Paths.ConfigPath; internal static readonly string FilePath = Path.Combine(DirectoryPath, "CaptainValheim.yml"); } internal static class SecondaryAttackWeaponConfigNormalizer { private const string GlobalFallbackKey = "Global"; internal static SecondaryAttackCompiledSnapshot Normalize(IReadOnlyDictionary<string, ShieldWeaponConfig> shields) { Dictionary<string, NormalizedShieldModeConfig> dictionary = new Dictionary<string, NormalizedShieldModeConfig>(StringComparer.OrdinalIgnoreCase); NormalizedShieldModeConfig normalizedShieldModeConfig = 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; } } NormalizedShieldModeConfig normalizedShieldModeConfig2 = ((shieldWeaponConfig != null) ? FromShieldRaw(shieldWeaponConfig, normalizedShieldModeConfig) : normalizedShieldModeConfig); 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, normalizedShieldModeConfig2); } } } return new SecondaryAttackCompiledSnapshot(dictionary, normalizedShieldModeConfig2); } internal static NormalizedShieldModeConfig FromShieldRaw(ShieldWeaponConfig raw, NormalizedShieldModeConfig? fallback = null) { if (fallback == null) { fallback = CreateGlobalDefaultShieldFallback(); } return NormalizeShield(raw, fallback); } internal static NormalizedShieldModeConfig CreateGlobalDefaultShieldFallback() { return 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) }; } 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) }; } 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) }; } 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 enum PendingCommitResult { None, Deferred, Committed, Failed } private static readonly object ReloadLock = new object(); private static FileSystemWatcher? _watcher; private static CustomSyncedValue<string>? _syncedYamlValue; private static SecondaryAttackCompiledSnapshot _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; private static SecondaryAttackCompiledSnapshot? _pendingCompiledSnapshot; private static SecondaryAttackAppliedWorldSnapshot _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; private static DateTime _localYamlReloadDueUtc; private static bool _hasPendingLocalYamlReload; private static bool _suppressSyncedYamlChanged; private static YamlAuthorityMode _yamlAuthorityMode; private static string _currentYamlFingerprint = string.Empty; private static string? _pendingYamlFingerprint; private static string? _rejectedYamlFingerprint; private static string _lastObservedSyncedYamlFingerprint = string.Empty; private static bool _hasObservedSyncedYaml; internal static SecondaryAttackAppliedWorldSnapshot CurrentAppliedWorldSnapshot => _currentAppliedWorldSnapshot; public static void Initialize() { SecondaryAttackConfigLoader.EnsureLocalFileExists(); InitializeSyncedYamlValue(); RefreshYamlAuthorityMode(force: true); } public static void Dispose() { DisposeSyncedYamlValue(); DisposeWatcher(); ClearConfigState(); } internal static void TryApplyPendingConfig() { RefreshYamlAuthorityMode(); StageSyncedYamlIfReady(); TryReloadDebouncedLocalYaml(); CommitPendingConfig(force: false, ObjectDB.instance, emitMissingWarnings: true); } internal static void ApplyPendingConfigToObjectDb(ObjectDB objectDb, bool emitMissingWarnings) { try { RefreshYamlAuthorityMode(); StageSyncedYamlIfReady(); TryReloadDebouncedLocalYaml(); PendingCommitResult pendingCommitResult = CommitPendingConfig(force: true, objectDb, emitMissingWarnings); if ((uint)pendingCommitResult <= 1u) { ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings); } } catch (Exception ex) { _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; StageCurrentConfigForRetry(); CaptainValheimPlugin.ModLogger.LogError((object)("Failed to apply CaptainValheim config during ObjectDB initialization: " + ex.Message)); } } private static void SetupWatcher() { if (_watcher == null) { Directory.CreateDirectory(SecondaryAttackYamlConfig.DirectoryPath); _watcher = new FileSystemWatcher(SecondaryAttackYamlConfig.DirectoryPath, "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; } lock (ReloadLock) { _hasPendingLocalYamlReload = true; _localYamlReloadDueUtc = DateTime.UtcNow.AddTicks(10000000L); } } private static void TryReloadDebouncedLocalYaml() { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles) { return; } lock (ReloadLock) { if (_hasPendingLocalYamlReload && !(DateTime.UtcNow < _localYamlReloadDueUtc)) { _hasPendingLocalYamlReload = false; if (!ReloadLocalYaml()) { _hasPendingLocalYamlReload = true; _localYamlReloadDueUtc = DateTime.UtcNow.AddTicks(10000000L); } } } } private static bool ReloadLocalYaml() { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles) { return true; } string yamlText; try { SecondaryAttackConfigLoader.EnsureLocalFileExists(); yamlText = SecondaryAttackConfigLoader.ReadLocalYamlText(); } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to read CaptainValheim.yml: " + ex.Message)); return false; } ApplyYamlText(yamlText); return true; } private static void OnSyncedYamlChanged() { if (!_suppressSyncedYamlChanged) { StageSyncedYamlIfReady(); } } private static void RefreshYamlAuthorityMode(bool force = false) { YamlAuthorityMode yamlAuthorityMode = DetermineYamlAuthorityMode(); if (force || yamlAuthorityMode != _yamlAuthorityMode) { _yamlAuthorityMode = yamlAuthorityMode; switch (yamlAuthorityMode) { case YamlAuthorityMode.LocalFiles: _hasObservedSyncedYaml = false; _lastObservedSyncedYamlFingerprint = string.Empty; SetupWatcher(); ReloadLocalYaml(); CaptainValheimPlugin.ModLogger.LogInfo((object)"CaptainValheim YAML authority mode: LocalFiles."); break; case YamlAuthorityMode.SyncedOnly: _hasObservedSyncedYaml = false; _lastObservedSyncedYamlFingerprint = string.Empty; DisposeWatcher(); ClearConfigState(); StageSyncedYamlIfReady(); CaptainValheimPlugin.ModLogger.LogInfo((object)"CaptainValheim YAML authority mode: SyncedOnly."); break; } } } private static void StageSyncedYamlIfReady() { if (_yamlAuthorityMode != YamlAuthorityMode.SyncedOnly || !CaptainValheimPlugin.ConfigSync.InitialSyncDone) { return; } string text = _syncedYamlValue?.Value ?? string.Empty; if (_hasObservedSyncedYaml && string.Equals(_lastObservedSyncedYamlFingerprint, text, StringComparison.Ordinal)) { return; } _hasObservedSyncedYaml = true; _lastObservedSyncedYamlFingerprint = text; if (string.IsNullOrWhiteSpace(text)) { if (_currentCompiledSnapshot != SecondaryAttackCompiledSnapshot.Empty || _pendingCompiledSnapshot != null) { ClearConfigState(); } } else { ApplyYamlText(text); } } private static void ClearConfigState() { _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; _currentCompiledSnapshot = SecondaryAttackCompiledSnapshot.Empty; _currentYamlFingerprint = string.Empty; _rejectedYamlFingerprint = null; if (_yamlAuthorityMode != YamlAuthorityMode.SyncedOnly) { _hasObservedSyncedYaml = false; _lastObservedSyncedYamlFingerprint = string.Empty; } _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; if ((Object)(object)ObjectDB.instance != (Object)null) { SecondaryAttackObjectDbStateStore.Restore(ObjectDB.instance); ShieldChargeCooldownStatusSystem.UnregisterStatusEffect(ObjectDB.instance); } ShieldRuntimeSystem.ResetTransientState(); } private static YamlAuthorityMode DetermineYamlAuthorityMode() { if (!((Object)(object)ZNet.instance != (Object)null) || ZNet.instance.IsServer()) { return YamlAuthorityMode.LocalFiles; } return YamlAuthorityMode.SyncedOnly; } private static void InitializeSyncedYamlValue() { DisposeSyncedYamlValue(); _syncedYamlValue = new CustomSyncedValue<string>(CaptainValheimPlugin.ConfigSync, "captain_valheim_yaml", string.Empty); _syncedYamlValue.ValueChanged += OnSyncedYamlChanged; } private static void DisposeSyncedYamlValue() { if (_syncedYamlValue != null) { _syncedYamlValue.ValueChanged -= OnSyncedYamlChanged; _syncedYamlValue = null; } } private static void DisposeWatcher() { _watcher?.Dispose(); _watcher = null; lock (ReloadLock) { _hasPendingLocalYamlReload = false; _localYamlReloadDueUtc = default(DateTime); } } private static bool ApplyYamlText(string yamlText) { string text = yamlText ?? string.Empty; if (string.Equals(_rejectedYamlFingerprint, text, StringComparison.Ordinal)) { DiscardPendingConfig(); return false; } if (_currentCompiledSnapshot != SecondaryAttackCompiledSnapshot.Empty && _currentAppliedWorldSnapshot != SecondaryAttackAppliedWorldSnapshot.Empty && string.Equals(_currentYamlFingerprint, text, StringComparison.Ordinal)) { _rejectedYamlFingerprint = null; DiscardPendingConfig(); return true; } if (_pendingCompiledSnapshot != null && string.Equals(_pendingYamlFingerprint, text, StringComparison.Ordinal)) { return true; } if (!SecondaryAttackConfigLoader.TryCompileSnapshot(text, out SecondaryAttackCompiledSnapshot snapshot)) { _rejectedYamlFingerprint = text; DiscardPendingConfig(); return false; } _rejectedYamlFingerprint = null; StageConfig(snapshot, text); return true; } private static void StageConfig(SecondaryAttackCompiledSnapshot snapshot, string fingerprint) { _pendingCompiledSnapshot = snapshot; _pendingYamlFingerprint = fingerprint; } private static void StageCurrentConfigForRetry() { if (_pendingCompiledSnapshot == null) { SecondaryAttackCompiledSnapshot currentCompiledSnapshot = _currentCompiledSnapshot; string currentYamlFingerprint = _currentYamlFingerprint; _currentYamlFingerprint = string.Empty; if (currentCompiledSnapshot != SecondaryAttackCompiledSnapshot.Empty && !string.IsNullOrWhiteSpace(currentYamlFingerprint)) { StageConfig(currentCompiledSnapshot, currentYamlFingerprint); } } } private static PendingCommitResult CommitPendingConfig(bool force, ObjectDB? objectDb, bool emitMissingWarnings) { if (_pendingCompiledSnapshot == null) { return PendingCommitResult.None; } if (HasPendingLocalYamlReload() || (Object)(object)objectDb == (Object)null || (!force && !CanApplyPendingConfigNow())) { return PendingCommitResult.Deferred; } SecondaryAttackCompiledSnapshot pendingCompiledSnapshot = _pendingCompiledSnapshot; string text = _pendingYamlFingerprint ?? string.Empty; SecondaryAttackAppliedWorldSnapshot currentAppliedWorldSnapshot; try { currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, pendingCompiledSnapshot, emitMissingWarnings); } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to apply staged YAML config; keeping the last working config: " + ex.Message)); DiscardPendingConfig(); try { ApplyCompiledSnapshotToObjectDb(objectDb, _currentCompiledSnapshot, emitMissingWarnings: false); } catch (Exception ex2) { _currentAppliedWorldSnapshot = SecondaryAttackAppliedWorldSnapshot.Empty; StageCurrentConfigForRetry(); CaptainValheimPlugin.ModLogger.LogError((object)("Failed to restore the last working YAML config: " + ex2.Message)); } return PendingCommitResult.Failed; } _currentCompiledSnapshot = pendingCompiledSnapshot; _currentYamlFingerprint = text; _currentAppliedWorldSnapshot = currentAppliedWorldSnapshot; DiscardPendingConfig(); PublishCommittedLocalYaml(text); CaptainValheimPlugin.ModLogger.LogInfo((object)"Applied staged YAML config changes."); return PendingCommitResult.Committed; } private static bool HasPendingLocalYamlReload() { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles) { return false; } lock (ReloadLock) { return _hasPendingLocalYamlReload; } } private static void PublishCommittedLocalYaml(string yamlText) { if (_yamlAuthorityMode != YamlAuthorityMode.LocalFiles || _syncedYamlValue == null) { return; } _suppressSyncedYamlChanged = true; try { _syncedYamlValue.AssignLocalValue(yamlText); } catch (Exception ex) { CaptainValheimPlugin.ModLogger.LogError((object)("Failed to synchronize the committed YAML config: " + ex.Message)); } finally { _suppressSyncedYamlChanged = false; } } private static void DiscardPendingConfig() { _pendingCompiledSnapshot = null; _pendingYamlFingerprint = null; } private static void ApplyCompiledSnapshotToObjectDb(ObjectDB objectDb, SecondaryAttackCompiledSnapshot compiledSnapshot, bool emitMissingWarnings) { _currentAppliedWorldSnapshot = SecondaryAttackWorldApplySystem.Apply(objectDb, 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.IsShieldChargeActive((Humanoid)(object)localPlayer)) { return !SecondaryAttackManager.HasActiveAsyncSecondaryWorkForFacade((Character?)(object)localPlayer); } return false; } } internal static class SecondaryAttackDefinitionCompiler { internal static bool TryCreateDefinition(string prefabName, ItemDrop itemDrop, NormalizedShieldModeConfig shieldConfig, bool emitWarnings, out SecondaryAttackDefinition? definition) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 definition = null; SharedData val = itemDrop.m_itemData?.m_shared; if (val == null) { return false; } if ((int)val.m_itemType != 5) { if (emitWarnings && SecondaryAttackManager.TryMarkCompatibilityWarningReported("non_shield_prefab:" + prefabName)) { CaptainValheimPlugin.ModLogger.LogWarning((object)("Skipping " + prefabName + ": CaptainValheim shield features can only be used on shield prefabs.")); } return false; } int num; if (shieldConfig.PrimaryAttack == null && shieldConfig.Throw == null) { NormalizedShieldChargeConfig charge = shieldConfig.Charge; num = ((charge != null && charge.Distance > 0f) ? 1 : 0); } else { num = 1; } bool flag = (byte)num != 0; if (!flag && shieldConfig.Reflect == null && shieldConfig.BlockCharge == null) { return false; } definition = CreateDefinition(shieldConfig); if (flag) { definition.ShieldSpecial = CreateShieldSpecialBehavior(shieldConfig); } return true; } private static SecondaryAttackDefinition CreateDefinition(NormalizedShieldModeConfig shieldConfig) { return new SecondaryAttackDefinition { ShieldProjectileReflect = (shieldConfig.Reflect != null), ShieldProjectileReflectStaminaFactor = Mathf.Max(0f, shieldConfig.Reflect?.StaminaFactor ?? 1f), ShieldProjectileReflectionFactor = (shieldConfig.Reflect?.ReflectionFactor ?? 0f), ShieldBlockCharge = (shieldConfig.BlockCharge != null), ShieldBlockChargeCount = shieldConfig.BlockCharge?.ChargeCount, ShieldBlockChargeDecayTime = shieldConfig.BlockCharge?.DecayTime, ShieldBlockChargeBlockingDecayFactor = shieldConfig.BlockCharge?.BlockingDecayFactor }; } private static ShieldSpecialSecondaryBehavior CreateShieldSpecialBehavior(NormalizedShieldModeConfig shieldConfig) { NormalizedShieldPrimaryAttackConfig primaryAttack = shieldConfig.PrimaryAttack; NormalizedShieldThrowConfig normalizedShieldThrowConfig = shieldConfig.Throw; NormalizedShieldChargeConfig charge = shieldConfig.Charge; bool flag = primaryAttack != null; bool flag2 = normalizedShieldThrowConfig != null; bool flag3 = charge != null && charge.Distance > 0f; return new ShieldSpecialSecondaryBehavior { HasShieldPrimaryAttack = flag, ShieldPrimaryAttackDamageFactor = (flag ? Mathf.Max(0f, primaryAttack.DamageFactor) : 0f), ShieldPrimaryAttackPushFactor = (flag ? Mathf.Max(0f, primaryAttack.PushFactor) : 0f), ShieldPrimaryAttackStaminaFactor = (flag ? Mathf.Max(0f, primaryAttack.StaminaFactor) : 0f), ShieldPrimaryAttackDurabilityFactor = (flag ? Mathf.Max(0f, primaryAttack.DurabilityFactor) : 1f), HasShieldThrow = flag2, ShieldThrowAnimation = (flag2 ? normalizedShieldThrowConfig.Animation : string.Empty), ShieldThrowTargets = (flag2 ? Mathf.Max(0, normalizedShieldThrowConfig.Targets) : 0), ShieldThrowDamageFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.DamageFactor) : 0f), ShieldThrowPushFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.PushFactor) : 0f), ShieldThrowStaminaFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.StaminaFactor) : 0f), ShieldThrowDurabilityFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.DurabilityFactor) : 1f), ShieldThrowDamageDecay = (flag2 ? Mathf.Clamp01(normalizedShieldThrowConfig.DamageDecay) : 0f), ShieldThrowRadiusFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.RadiusFactor) : 0f), ShieldThrowTtlFactor = (flag2 ? Mathf.Max(0f, normalizedShieldThrowConfig.TtlFactor) : 0f), HasShieldCharge = flag3, ShieldChargeDamageFactor = (flag3 ? Mathf.Max(0f, charge.DamageFactor) : 0f), ShieldChargePushFactor = (flag3 ? Mathf.Max(0f, charge.PushFactor) : 0f), ShieldChargeStaminaFactor = (flag3 ? Mathf.Max(0f, charge.StaminaFactor) : 0f), ShieldChargeDistance = (flag3 ? Mathf.Max(0f, charge.Distance) : 0f), ShieldChargeSpeed = (flag3 ? Mathf.Max(0f, charge.Speed) : 0f), ShieldChargeCooldown = (flag3 ? Mathf.Max(0f, charge.Cooldown) : 0f), ShieldChargeCooldownReductionFactor = (flag3 ? Mathf.Clamp01(charge.CooldownReductionFactor) : 0f), ShieldChargeDurabilityFactor = (flag3