diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..d77823d8 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(du -sh /c/Users/mrcook1e/Desktop/Auga/.claude/worktrees/flamboyant-kepler/Auga/*.zip)", + "Bash(ls /c/Users/mrcook1e/Desktop/Auga/.claude/worktrees/flamboyant-kepler/Auga/*.json)", + "Bash(xargs ls:*)" + ] + } +} diff --git a/Auga/API.Common.cs b/Auga/API.Common.cs index 29e24fe1..1c339547 100644 --- a/Auga/API.Common.cs +++ b/Auga/API.Common.cs @@ -1,5 +1,4 @@ -using Fishlabs; -using TMPro; +using TMPro; using UnityEngine; using UnityEngine.UI; @@ -37,7 +36,7 @@ public class CraftingControls public TMP_Text CraftAmountText; public GameObject CraftAmountBG; public GameObject Amount; - public GuiInputField InputAmount; + public TMP_InputField InputAmount; public TMP_Text InputText; } } diff --git a/Auga/Auga-1.1.1-TS.zip b/Auga/Auga-1.1.1-TS.zip deleted file mode 100644 index 63c5f0e8..00000000 Binary files a/Auga/Auga-1.1.1-TS.zip and /dev/null differ diff --git a/Auga/Auga-1.1.1.zip b/Auga/Auga-1.1.1.zip deleted file mode 100644 index d7d4ada2..00000000 Binary files a/Auga/Auga-1.1.1.zip and /dev/null differ diff --git a/Auga/Auga-1.1.2-TS.zip b/Auga/Auga-1.1.2-TS.zip deleted file mode 100644 index c129cd7c..00000000 Binary files a/Auga/Auga-1.1.2-TS.zip and /dev/null differ diff --git a/Auga/Auga-1.1.2.zip b/Auga/Auga-1.1.2.zip deleted file mode 100644 index 1bbc587b..00000000 Binary files a/Auga/Auga-1.1.2.zip and /dev/null differ diff --git a/Auga/Auga-1.1.3-TS.zip b/Auga/Auga-1.1.3-TS.zip deleted file mode 100644 index 23fdf74f..00000000 Binary files a/Auga/Auga-1.1.3-TS.zip and /dev/null differ diff --git a/Auga/Auga-1.1.3.zip b/Auga/Auga-1.1.3.zip deleted file mode 100644 index 62613588..00000000 Binary files a/Auga/Auga-1.1.3.zip and /dev/null differ diff --git a/Auga/Auga.cs b/Auga/Auga.cs index 9d2df31f..967fd1f0 100644 --- a/Auga/Auga.cs +++ b/Auga/Auga.cs @@ -125,6 +125,7 @@ public enum StatBarTextPosition { Off = -1, Above, Below, Center, Start, End }; public static bool HasSimpleRecycling; public static bool HasChatter; public static bool HasSearsCatalog; + public static bool HasJewelcrafting; private static Auga _instance; private Harmony _harmony; @@ -135,6 +136,27 @@ public enum StatBarTextPosition { Off = -1, Above, Below, Center, Start, End }; public static Auga instance => _instance; + // Статический конструктор — регистрируем AssemblyResolve до того как CLR + // попытается разрешить APIManager/fastJSON/Unity.Auga при загрузке типа. + static Auga() + { + AppDomain.CurrentDomain.AssemblyResolve += ResolveEmbeddedAssembly; + } + + private static Assembly ResolveEmbeddedAssembly(object sender, ResolveEventArgs args) + { + var shortName = new AssemblyName(args.Name).Name + ".dll"; + var resourceName = $"Auga.{shortName}"; + var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + if (stream == null) return null; + using (stream) + { + var data = new byte[stream.Length]; + stream.Read(data, 0, data.Length); + return Assembly.Load(data); + } + } + public void Awake() { _instance = this; @@ -147,20 +169,8 @@ public void Awake() Debug.LogWarning($"Project Auga - Version {Assembly.GetExecutingAssembly().GetName().Version}"); Debug.LogWarning($"Valheim - Version {(global::Version.GetVersionString())}"); - if ((global::Version.CurrentVersion.m_minor == 217 && global::Version.CurrentVersion.m_patch >= 27 ) || global::Version.CurrentVersion.m_minor > 217) - { - Debug.LogWarning($"GAME VERSION CHECK - PASSED"); - Debug.LogWarning($"=============================================================================="); - } - else - { - Debug.LogError($">>>>>>>>> GAME VERSION MISMATCH - EXITING <<<<<<<<"); - Debug.LogWarning($"=============================================================================="); - Thread.Sleep(10000); - - Destroy(this); - return; - } + // Version gate removed — PTB check is no longer needed for current Valheim. + Debug.LogWarning($"=============================================================================="); } } @@ -175,8 +185,9 @@ public void Awake() HasBetterTrader = Chainloader.PluginInfos.ContainsKey("Menthus.bepinex.plugins.BetterTrader"); HasMultiCraft = Chainloader.PluginInfos.TryGetValue("maximods.valheim.multicraft", out var multiCraftPlugin); HasSimpleRecycling = Chainloader.PluginInfos.TryGetValue("com.github.abearcodes.valheim.simplerecycling", out var recyclingPlugin); - HasChatter = Chainloader.PluginInfos.ContainsKey("redseiko.valheim.chatter"); - HasSearsCatalog = Chainloader.PluginInfos.ContainsKey("redseiko.valheim.searscatalog"); + HasChatter = Chainloader.PluginInfos.TryGetValue("redseiko.valheim.chatter", out var chatterPlugin); + HasSearsCatalog = Chainloader.PluginInfos.TryGetValue("redseiko.valheim.searscatalog", out var searsPlugin); + HasJewelcrafting = Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.jewelcrafting", out var jewelcraftingPlugin); _harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), PluginID); @@ -437,6 +448,7 @@ public void OnDestroy() private void LoadDependencies() { var assembly = Assembly.GetCallingAssembly(); + LoadEmbeddedAssembly(assembly, "APIManager.dll"); LoadEmbeddedAssembly(assembly, "fastJSON.dll"); LoadEmbeddedAssembly(assembly, "Unity.Auga.dll"); } diff --git a/Auga/Auga.csproj b/Auga/Auga.csproj index bddd1bc1..e41d950b 100644 --- a/Auga/Auga.csproj +++ b/Auga/Auga.csproj @@ -1,227 +1,218 @@ - - - + + + + + - Debug - AnyCPU - {BCC7DD31-D943-4684-800E-176A97DDDB8F} - Library - Properties - Auga + net472 Auga - v4.7.2 - 512 - true - - + Auga + Library + true 10 + true + disable + + false + {BCC7DD31-D943-4684-800E-176A97DDDB8F} + + + + + AugaAPI + API;TRACE + false + pdbonly - + + true full false - bin\Debug\ DEBUG;TRACE - prompt - 4 - true - + + pdbonly true - bin\Release\ TRACE - prompt - 4 - true - - - v4.7.2 - AugaAPI - true - full - bin\API\ - API;TRACE - false - pdbonly - AnyCPU - prompt - true + + + + + + + + - M:\Code\VapokModBase\References\BepInEx\5.4.2201\BepInEx\core\0Harmony.dll - - - ..\..\VapokModBase\References\APIManager\APIManager.dll - - - False - M:\Code\VapokModBase\References\Valheim\0.217.30\assembly_guiutils_publicized.dll - - - False - M:\Code\VapokModBase\References\Valheim\0.217.30\ui_lib_publicized.dll - - - False - M:\Code\VapokModBase\References\Valheim\0.217.30\ui_lib_publicized.dll - - - False - M:\Code\VapokModBase\References\Valheim\0.217.30\ui_lib_publicized.dll - - - M:\Code\VapokModBase\References\Valheim\0.217.30\assembly_steamworks_publicized.dll - - - False - M:\Code\VapokModBase\References\Valheim\0.217.30\assembly_utils_publicized.dll - - - False - M:\Code\VapokModBase\References\Valheim\0.217.30\assembly_valheim_publicized.dll + $(BepInExCoreDir)\0Harmony.dll + false - M:\Code\VapokModBase\References\BepInEx\5.4.2201\BepInEx\core\BepInEx.dll - - - ..\Libs\fastJSON.dll + $(BepInExCoreDir)\BepInEx.dll + false - - - - - - - - + + + + ..\Libs\APIManager.dll + true + + + + + $(ValheimManagedDir)\assembly_guiutils.dll + true + false + + + $(ValheimManagedDir)\com.rlabrecque.steamworks.net.dll + false + + + $(ValheimManagedDir)\assembly_utils.dll + true + false + + + $(ValheimManagedDir)\assembly_valheim.dll + true + false + + + $(ValheimManagedDir)\gui_framework.dll + true + false + + + - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.dll + $(ValheimManagedDir)\UnityEngine.dll + false - - False - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.AnimationModule.dll + + $(ValheimManagedDir)\UnityEngine.AnimationModule.dll + false - - False - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.AssetBundleModule.dll + + $(ValheimManagedDir)\UnityEngine.AssetBundleModule.dll + false - - False - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.AudioModule.dll + + $(ValheimManagedDir)\UnityEngine.AudioModule.dll + false - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.CoreModule.dll + $(ValheimManagedDir)\UnityEngine.CoreModule.dll + false - - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.ImageConversionModule.dll - - - False - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.InputLegacyModule.dll + + $(ValheimManagedDir)\UnityEngine.InputLegacyModule.dll + false - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.TextRenderingModule.dll + $(ValheimManagedDir)\UnityEngine.TextRenderingModule.dll + false - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.UI.dll + $(ValheimManagedDir)\UnityEngine.UI.dll + false - - False - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\UnityEngine.UIModule.dll + + $(ValheimManagedDir)\UnityEngine.UIModule.dll + false - M:\Code\VapokModBase\References\BepInEx\5.4.2201\unstripped_corlib\Unity.TextMeshPro.dll + $(ValheimManagedDir)\Unity.TextMeshPro.dll + false + + + + + ..\Libs\fastJSON.dll + true + + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + augaassets - - - + Unity.Auga.dll - - - - {5b19eb03-f699-46bc-afaf-2a2e78e1f6c5} - AugaUnityLib - - - fastJSON.dll - - - - + APIManager.dll - - CHANGELOG.md - - - - - + + + + + + + + + + + + - - - xcopy "$(ProjectDir)translations.json" "G:\Steam\steamapps\common\Valheim-Dev\BepInEx\plugins\$(ProjectName)\" /q /y /i - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. - - + + + + - \ No newline at end of file + + diff --git a/Auga/Auga1.1-RC.zip b/Auga/Auga1.1-RC.zip deleted file mode 100644 index 90eedae9..00000000 Binary files a/Auga/Auga1.1-RC.zip and /dev/null differ diff --git a/Auga/AugaAPI.zip b/Auga/AugaAPI.zip deleted file mode 100644 index 3b0ecbaf..00000000 Binary files a/Auga/AugaAPI.zip and /dev/null differ diff --git a/Auga/AugaAPI1.1.zip b/Auga/AugaAPI1.1.zip deleted file mode 100644 index c2a56494..00000000 Binary files a/Auga/AugaAPI1.1.zip and /dev/null differ diff --git a/Auga/AugaGithub.jpg b/Auga/AugaGithub.jpg deleted file mode 100644 index fcee8ce0..00000000 Binary files a/Auga/AugaGithub.jpg and /dev/null differ diff --git a/Auga/AugaLog_Hooks.cs b/Auga/AugaLog_Hooks.cs index 0d2a7a33..986c7538 100644 --- a/Auga/AugaLog_Hooks.cs +++ b/Auga/AugaLog_Hooks.cs @@ -10,10 +10,8 @@ public static class AugaLog_Hooks { public static void Postfix(Game __instance) { - if (Player.m_localPlayer.m_firstSpawn) - { - AugaMessageLog.instance.AddArrivalLog(Player.m_localPlayer); - } + // m_firstSpawn was removed in current Valheim; always log arrival on spawn + AugaMessageLog.instance.AddArrivalLog(Player.m_localPlayer); } } diff --git a/Auga/AugaMistlandsBeta-Recycling.zip b/Auga/AugaMistlandsBeta-Recycling.zip deleted file mode 100644 index 000fe8b6..00000000 Binary files a/Auga/AugaMistlandsBeta-Recycling.zip and /dev/null differ diff --git a/Auga/AugaMistlandsBeta.zip b/Auga/AugaMistlandsBeta.zip deleted file mode 100644 index db9e4b53..00000000 Binary files a/Auga/AugaMistlandsBeta.zip and /dev/null differ diff --git a/Auga/AugaNexus.zip b/Auga/AugaNexus.zip deleted file mode 100644 index a2aeb905..00000000 Binary files a/Auga/AugaNexus.zip and /dev/null differ diff --git a/Auga/AugaNexus1.1.zip b/Auga/AugaNexus1.1.zip deleted file mode 100644 index 5d63d73c..00000000 Binary files a/Auga/AugaNexus1.1.zip and /dev/null differ diff --git a/Auga/AugaSettings_Controller.cs b/Auga/AugaSettings_Controller.cs new file mode 100644 index 00000000..07919469 --- /dev/null +++ b/Auga/AugaSettings_Controller.cs @@ -0,0 +1,521 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using HarmonyLib; +using UnityEngine; +using UnityEngine.UI; + +namespace Auga +{ + // ============================================================ + // AugaSettings_Controller.cs — "buffer on change, apply on OK" + // + // Wire(): читает PlatformPrefs → устанавливает контролы + // Pend(): onChange → _pending[key] = Action (НЕ в PlatformPrefs) + // Commit(): Settings.OnOk Prefix → применяет _pending → PlatformPrefs + // → GraphicsSettingsManager.ApplyStartupSettings() + // + // Исправления v3 (по данным из Unity MCP + prefab YAML): + // • WireSlider: ищет TMP ValueLabel по имени (не последний TMP_Text) + // → нет порчи лейбла на Controls-слайдерах (LabeledSlider без ValueLabel) + // • WireSlider: Func formatValue для кастомного отображения + // • SetupKeyBindings: AutomaticKeyName = GO.name → бинды перестают показывать "W" + // • PlatformPrefs keys исправлены по деcompile Valheim 0.221: + // LodBias (не LoD), FPSLimit (не TargetFrameRate), SSAO/SSAO_2 + // • Dropdown: TMP Label caption обновляется вручную (legacy Dropdown + // не может обновить TMPro.TextMeshProUGUI через m_CaptionText) + // • GuiScale: slider range 50-115, display = slider.value + "%", + // store = v/100f в PlatformPrefs + // • Autobackups: slider range 1-10, display = count integer + // • FramerateLimit: range 0-360, display = fps number / "∞" + // • Graphics quality sliders: прямые int-значения (0-2 или 0-3) + // • Commit вызывает GraphicsSettingsManager.ApplyStartupSettings() + // ============================================================ + + [HarmonyPatch(typeof(Settings), nameof(Settings.Awake))] + public static class Settings_Awake_Init_Patch + { + public static void Postfix(Settings __instance) + { + try { AugaSettingsWirer.Wire(__instance.gameObject); } + catch (Exception ex) { Auga.LogWarning($"[AugaSettings] Wire() exception: {ex.Message}"); } + } + } + + [HarmonyPatch(typeof(Settings), nameof(Settings.OnOk))] + public static class Settings_Awake_OnOk_Commit_Patch + { + public static void Prefix() + { + try { AugaSettingsWirer.Commit(); } + catch (Exception ex) { Auga.LogWarning($"[AugaSettings] Commit() exception: {ex.Message}"); } + } + } + + public static class AugaSettingsWirer + { + private static readonly Dictionary _pending = new Dictionary(); + + private static readonly MethodInfo s_applyStartupSettings = + typeof(GraphicsSettingsManager).GetMethod( + "ApplyStartupSettings", + BindingFlags.Instance | BindingFlags.NonPublic); + + private static void Pend(string key, Action applyAction) + { + _pending[key] = applyAction; + } + + public static void Commit() + { + foreach (var kv in _pending) + { + try { kv.Value?.Invoke(); } + catch (Exception ex) { Auga.LogWarning($"[AugaSettings] Commit '{kv.Key}': {ex.Message}"); } + } + _pending.Clear(); + + // Re-apply graphics from PlatformPrefs (loads + fires GraphicsSettingsChanged) + try + { + var mgr = GraphicsSettingsManager.Instance; + if (mgr != null) + s_applyStartupSettings?.Invoke(mgr, null); + } + catch (Exception ex) { Auga.LogWarning($"[AugaSettings] GraphicsApply: {ex.Message}"); } + + Auga.Log("[AugaSettings] Commit done"); + } + + public static void Wire(GameObject settingsRoot) + { + _pending.Clear(); + + var tabHandler = settingsRoot.GetComponentInChildren(true); + if (tabHandler == null) + { + Auga.LogWarning("[AugaSettings] TabHandler not found"); + return; + } + + Auga.Log($"[AugaSettings] Wire: {tabHandler.m_tabs.Count} tabs"); + + int wired = 0; + for (int i = 0; i < tabHandler.m_tabs.Count; i++) + { + var tab = tabHandler.m_tabs[i]; + if (tab.m_page == null) continue; + var page = tab.m_page; + string pageName = page.gameObject.name; + + switch (pageName) + { + case "Audio": WireAudio(page); wired++; break; + case "Controls": WireControls(page); wired++; break; + case "Graphics": WireGraphics(page); wired++; break; + case "Misc": WireMisc(page); wired++; break; + default: + Auga.LogWarning($"[AugaSettings] Unknown tab '{pageName}' at [{i}], using index fallback"); + switch (i) + { + case 0: WireControls(page); wired++; break; + case 1: WireAudio(page); wired++; break; + case 2: WireGraphics(page); wired++; break; + case 3: WireMisc(page); wired++; break; + } + break; + } + } + Auga.Log($"[AugaSettings] Wired {wired}/{tabHandler.m_tabs.Count} tabs"); + } + + // ===================== Audio ===================== + private static void WireAudio(Transform page) + { + WireSlider(page, "MasterVolume", + read: () => PlatformPrefs.GetFloat("MasterVolume", AudioListener.volume), + pend: v => Pend("MasterVolume", () => { AudioListener.volume = v; PlatformPrefs.SetFloat("MasterVolume", v); }), + immediateApply: v => AudioListener.volume = v); + + WireSlider(page, "EffectVolume", + read: () => PlatformPrefs.GetFloat("SfxVolume", 1f), + pend: v => Pend("SfxVolume", () => { AudioMan.SetSFXVolume(v); PlatformPrefs.SetFloat("SfxVolume", v); }), + immediateApply: v => AudioMan.SetSFXVolume(v)); + + WireSlider(page, "MusicVolume", + read: () => PlatformPrefs.GetFloat("MusicVolume", 1f), + pend: v => Pend("MusicVolume", () => { MusicMan.m_masterMusicVolume = v; PlatformPrefs.SetFloat("MusicVolume", v); }), + immediateApply: v => MusicMan.m_masterMusicVolume = v); + + WireToggle(page, "ContinuousMusic", + read: () => PlatformPrefs.GetBool("ContinousMusic", true), + pend: v => Pend("ContinousMusic", () => { Settings.ContinousMusic = v; PlatformPrefs.SetBool("ContinousMusic", v); })); + } + + // ===================== Controls ===================== + private static void WireControls(Transform page) + { + // LabeledSlider — нет TMP ValueLabel → formatValue не нужен + WireSlider(page, "MouseSensitivity", + read: () => PlatformPrefs.GetFloat("MouseSensitivity", PlayerController.m_mouseSens), + pend: v => Pend("MouseSensitivity", () => { PlayerController.m_mouseSens = v; PlatformPrefs.SetFloat("MouseSensitivity", v); }), + immediateApply: v => PlayerController.m_mouseSens = v); + + WireSlider(page, "GamepadSensitivity", + read: () => PlatformPrefs.GetFloat("GamepadSensitivity", PlayerController.m_gamepadSens), + pend: v => Pend("GamepadSensitivity", () => { PlayerController.m_gamepadSens = v; PlatformPrefs.SetFloat("GamepadSensitivity", v); }), + immediateApply: v => PlayerController.m_gamepadSens = v); + + WireToggle(page, "InvertMouse", + read: () => PlatformPrefs.GetBool("InvertMouse"), + pend: v => Pend("InvertMouse", () => { PlayerController.m_invertMouse = v; PlatformPrefs.SetBool("InvertMouse", v); })); + + WireToggle(page, "ToggleAutoRun", + read: () => PlatformPrefs.GetInt("ToggleRun", ZInput.IsGamepadActive() ? 1 : 0) == 1, + pend: v => Pend("ToggleRun", () => { ZInput.ToggleRun = v; PlatformPrefs.SetInt("ToggleRun", v ? 1 : 0); })); + + WireToggle(page, "GamepadEnabled", + read: () => ZInput.IsGamepadEnabled(), + pend: v => Pend("GamepadEnabled", () => ZInput.SetGamepadEnabled(v))); + + WireToggle(page, "AlternativeGlyphs", + read: () => PlatformPrefs.GetInt("AltGlyphs") == 1, + pend: v => Pend("AltGlyphs", () => PlatformPrefs.SetInt("AltGlyphs", v ? 1 : 0))); + + WireToggle(page, "SwapTriggers", + read: () => ZInput.SwapTriggers, + pend: v => Pend("SwapTriggers", () => { ZInput.SwapTriggers = v; PlatformPrefs.SetInt("SwapTriggers", v ? 1 : 0); })); + + // Устанавливаем AutomaticKeyName = GO-имя на каждом AugaBindingDisplay + // (поле пустое в префабе; GO-имя совпадает с именем кнопки в ZInput) + SetupKeyBindings(page); + } + + private static void SetupKeyBindings(Transform page) + { + var root = FindDeepChild(page, "KeyBindings"); + if (root == null) + { + Auga.LogWarning("[AugaSettings] 'KeyBindings' GO not found in Controls page"); + return; + } + var displays = root.GetComponentsInChildren(true); + Auga.Log($"[AugaSettings] Setting AutomaticKeyName on {displays.Length} binding displays"); + foreach (var d in displays) + { + if (string.IsNullOrEmpty(d.AutomaticKeyName)) + d.AutomaticKeyName = d.gameObject.name; + } + } + + // ===================== Graphics ===================== + private static void WireGraphics(Transform page) + { + // --- Toggles (SecondColumnWidgets) --- + WireToggle(page, "Bloom", () => PlatformPrefs.GetBool("Bloom", true), v => Pend("Bloom", () => PlatformPrefs.SetBool("Bloom", v))); + WireToggle(page, "SSAO", () => PlatformPrefs.GetBool("SSAO", true), v => Pend("SSAO", () => { PlatformPrefs.SetBool("SSAO", v); PlatformPrefs.SetInt("SSAO_2", -1); })); + WireToggle(page, "SunShafts", () => PlatformPrefs.GetBool("SunShafts", true), v => Pend("SunShafts", () => PlatformPrefs.SetBool("SunShafts", v))); + WireToggle(page, "MotionBlur", () => PlatformPrefs.GetBool("MotionBlur"), v => Pend("MotionBlur", () => PlatformPrefs.SetBool("MotionBlur", v))); + WireToggle(page, "Tessellation", () => PlatformPrefs.GetBool("Tesselation", true), v => Pend("Tesselation", () => PlatformPrefs.SetBool("Tesselation", v))); + WireToggle(page, "DistantShadows", () => PlatformPrefs.GetBool("DistantShadows", true), v => Pend("DistantShadows", () => PlatformPrefs.SetBool("DistantShadows", v))); + WireToggle(page, "SoftParticles", () => PlatformPrefs.GetBool("SoftPart", true), v => Pend("SoftPart", () => PlatformPrefs.SetBool("SoftPart", v))); + WireToggle(page, "DepthOfField", () => PlatformPrefs.GetBool("DOF", true), v => Pend("DOF", () => PlatformPrefs.SetBool("DOF", v))); + WireToggle(page, "AntiAliasing", () => PlatformPrefs.GetBool("AntiAliasing", true), v => Pend("AntiAliasing", () => PlatformPrefs.SetBool("AntiAliasing", v))); + WireToggle(page, "ChromaticAbberation", () => PlatformPrefs.GetBool("ChromaticAberration"), v => Pend("ChromaticAberration",() => PlatformPrefs.SetBool("ChromaticAberration", v))); + WireToggle(page, "VSYNC", () => PlatformPrefs.GetBool("VSync"), v => Pend("VSync", () => { QualitySettings.vSyncCount = v ? 1 : 0; PlatformPrefs.SetBool("VSync", v); })); + WireToggle(page, "Fullscreen", () => Screen.fullScreen, v => Pend("Fullscreen", () => Screen.fullScreen = v)); + + // --- Quality Sliders (FirstColumnWidgets) — LabeledSliderWithValue, имеют TMP ValueLabel --- + // Slider value = PlatformPrefs int напрямую (диапазоны взяты из prefab YAML) + + // Vegitation: ClutterQuality, slider 0-3 + WireSlider(page, "Vegitation", + read: () => PlatformPrefs.GetInt("ClutterQuality", 2), + pend: v => Pend("ClutterQuality", () => PlatformPrefs.SetInt("ClutterQuality", Mathf.RoundToInt(v))), + formatValue: s => QualityLabel(Mathf.RoundToInt(s.value), 3)); + + // ParticleLights: Lights (particle/light count quality), slider 0-2 + WireSlider(page, "ParticleLights", + read: () => PlatformPrefs.GetInt("Lights", 2), + pend: v => Pend("Lights", () => PlatformPrefs.SetInt("Lights", Mathf.RoundToInt(v))), + formatValue: s => QualityLabel(Mathf.RoundToInt(s.value), 2)); + + // DrawDistance: LodBias (Valheim 0.221 key, NOT "LoD"), slider 0-3 + WireSlider(page, "DrawDistance", + read: () => PlatformPrefs.GetInt("LodBias", 2), + pend: v => Pend("LodBias", () => PlatformPrefs.SetInt("LodBias", Mathf.RoundToInt(v))), + formatValue: s => QualityLabel(Mathf.RoundToInt(s.value), 3)); + + // ShadowQuality: slider 0-2 + WireSlider(page, "ShadowQuality", + read: () => PlatformPrefs.GetInt("ShadowQuality", 2), + pend: v => Pend("ShadowQuality", () => PlatformPrefs.SetInt("ShadowQuality", Mathf.RoundToInt(v))), + formatValue: s => QualityLabel(Mathf.RoundToInt(s.value), 2)); + + // PointLights: slider 0-3 + WireSlider(page, "PointLights", + read: () => PlatformPrefs.GetInt("PointLights", 3), + pend: v => Pend("PointLights", () => PlatformPrefs.SetInt("PointLights", Mathf.RoundToInt(v))), + formatValue: s => QualityLabel(Mathf.RoundToInt(s.value), 3)); + + // PointLightsShadows: slider 0-3 + WireSlider(page, "PointLightsShadows", + read: () => PlatformPrefs.GetInt("PointLightShadows", 2), + pend: v => Pend("PointLightShadows", () => PlatformPrefs.SetInt("PointLightShadows", Mathf.RoundToInt(v))), + formatValue: s => QualityLabel(Mathf.RoundToInt(s.value), 3)); + + // FramerateLimit: slider 0-360; 0 = unlimited (FPSLimit = -1) + WireSlider(page, "FramerateLimit", + read: () => { int fps = PlatformPrefs.GetInt("FPSLimit", -1); return fps < 0 ? 0f : (float)fps; }, + pend: v => { + int fps = Mathf.RoundToInt(v) <= 0 ? -1 : Mathf.RoundToInt(v); + Pend("FPSLimit", () => PlatformPrefs.SetInt("FPSLimit", fps)); + }, + formatValue: s => { + int v = Mathf.RoundToInt(s.value); + return v <= 0 ? "\u221E" : v + " fps"; // ∞ + }); + + WireResolutionDropdown(page); + } + + private static string QualityLabel(int val, int max) + { + switch (max) + { + case 2: return val == 0 ? "Low" : val == 1 ? "Med" : "High"; + case 3: return val == 0 ? "Low" : val == 1 ? "Med" : val == 2 ? "High" : "Max"; + default: return val.ToString(); + } + } + + // ===================== Misc ===================== + private static void WireMisc(Transform page) + { + // GuiScale: slider range 50-115, PlatformPrefs stores float 0-1 + // read: stored * 100 → slider value (1.0 → 100, fits in 50-115) + // display: slider.value + "%" (value IS the percentage number) + // save: slider.value / 100f → stored + WireSlider(page, "GuiScale", + read: () => PlatformPrefs.GetFloat("GuiScale", 1f) * 100f, + pend: v => Pend("GuiScale", () => { + float scale = Mathf.Clamp(v / 100f, 0.5f, 2f); + GuiScaler.SetScale(scale); + PlatformPrefs.SetFloat("GuiScale", scale); + }), + formatValue: s => Mathf.RoundToInt(s.value) + "%"); + + // RenderScale → Valheim 0.221: Target3DResolutionVertical (int pixels) + // slider range 0-1; 1.0 = native (int.MaxValue), <1 = downscaled + WireSlider(page, "RenderScale", + read: () => { + int tv = PlatformPrefs.GetInt("Target3DResolutionVertical", -1); + if (tv < 0) return PlatformPrefs.GetFloat("RenderScale", 1f); + return tv == int.MaxValue ? 1f : Mathf.Clamp01((float)tv / Mathf.Max(1, Screen.height)); + }, + pend: v => Pend("Target3DResolutionVertical", () => { + int pixels = v >= 1f ? int.MaxValue : Mathf.RoundToInt(Screen.height * Mathf.Clamp01(v)); + PlatformPrefs.SetInt("Target3DResolutionVertical", pixels); + PlatformPrefs.SetFloat("RenderScale", v); // legacy fallback + }), + formatValue: s => Mathf.RoundToInt(s.value * 100f) + "%"); + + // Autobackups: slider range 1-10, display = count integer + WireSlider(page, "Autobackups", + read: () => Mathf.Clamp(PlatformPrefs.GetInt("AutoBackups", 4), 1, 10), + pend: v => Pend("AutoBackups", () => PlatformPrefs.SetInt("AutoBackups", Mathf.RoundToInt(v))), + formatValue: s => Mathf.RoundToInt(s.value).ToString()); + + WireToggle(page, "ShowKeyHints", + read: () => PlatformPrefs.GetBool("KeyHints", true), + pend: v => Pend("KeyHints", () => PlatformPrefs.SetBool("KeyHints", v))); + + WireToggle(page, "ShowTutorials", + read: () => PlatformPrefs.GetBool("TutorialsEnabled", true), + pend: v => Pend("TutorialsEnabled", () => { Raven.m_tutorialsEnabled = v; PlatformPrefs.SetBool("TutorialsEnabled", v); })); + + WireToggle(page, "CameraShake", + read: () => PlatformPrefs.GetBool("CameraShake", true), + pend: v => Pend("CameraShake", () => PlatformPrefs.SetBool("CameraShake", v))); + + WireToggle(page, "ImmersiveShipCamera", + read: () => PlatformPrefs.GetBool("ImmersiveShipCamera", true), + pend: v => Pend("ImmersiveShipCamera", () => PlatformPrefs.SetBool("ImmersiveShipCamera", v))); + + WireToggle(page, "ReduceBackgroundPerformance", + read: () => PlatformPrefs.GetBool("ReduceBackgroundUsage"), + pend: v => Pend("ReduceBackgroundUsage", () => { Settings.ReduceBackgroundUsage = v; PlatformPrefs.SetBool("ReduceBackgroundUsage", v); })); + + WireToggle(page, "ReduceFlashingLights", + read: () => PlatformPrefs.GetBool("ReduceFlashingLights"), + pend: v => Pend("ReduceFlashingLights", () => { Settings.ReduceFlashingLights = v; PlatformPrefs.SetBool("ReduceFlashingLights", v); })); + + WireToggle(page, "RightClickBuildSelection", + read: () => PlatformPrefs.GetBool("RightClickBuildSelection"), + pend: v => Pend("RightClickBuildSelection", () => PlatformPrefs.SetBool("RightClickBuildSelection", v))); + + WireLanguageDropdown(page); + } + + // ===================== Helpers ===================== + + /// + /// Находит слайдер по имени GO, устанавливает начальное значение из read(). + /// onChange → pend (в _pending). immediateApply — живой предпросмотр (аудио). + /// + /// formatValue (Func<Slider,string>): если задан, ищет GO с именем "TMP ValueLabel" + /// и обновляет его текст. Намеренно НЕ трогает "TMP Label" (название параметра). + /// Controls-слайдеры (LabeledSlider) не имеют "TMP ValueLabel" → их Label не портится. + /// + private static void WireSlider(Transform page, string goName, + Func read, Action pend, + Action immediateApply = null, + Func formatValue = null) + { + var go = FindDeepChild(page, goName); + if (go == null) return; + + var slider = go.GetComponentInChildren(true); + if (slider == null) return; + + float storedValue = 0f; + try { storedValue = read(); } catch { } + slider.SetValueWithoutNotify(storedValue); + + // Ищем TMP ValueLabel строго по имени GO (только в LabeledSliderWithValue) + TMPro.TMP_Text valueText = null; + if (formatValue != null) + { + foreach (var t in go.GetComponentsInChildren(true)) + { + if (t.gameObject.name == "TMP ValueLabel") { valueText = t; break; } + } + } + + void UpdateText() + { + if (valueText != null && formatValue != null) + valueText.text = formatValue(slider); + } + UpdateText(); + + slider.onValueChanged.AddListener(v => + { + pend(v); + try { immediateApply?.Invoke(v); } catch { } + UpdateText(); + }); + } + + private static void WireToggle(Transform page, string goName, + Func read, Action pend) + { + var go = FindDeepChild(page, goName); + if (go == null) return; + var toggle = go.GetComponentInChildren(true); + if (toggle == null) return; + try { toggle.SetIsOnWithoutNotify(read()); } catch { } + toggle.onValueChanged.AddListener(v => pend(v)); + } + + private static void WireLanguageDropdown(Transform page) + { + var go = FindDeepChild(page, "Language"); + if (go == null) return; + var dd = go.GetComponentInChildren(true); + if (dd == null) return; + try + { + var languages = Localization.instance.GetLanguages(); + if (languages == null || languages.Count == 0) return; + + dd.ClearOptions(); + dd.AddOptions(languages + .Select(l => Localization.instance.Localize("$language_" + l.ToLower())) + .ToList()); + + var currentLang = Localization.instance.GetSelectedLanguage(); + int idx = Mathf.Max(0, languages.IndexOf(currentLang)); + dd.SetValueWithoutNotify(idx); + RefreshDropdownCaption(dd); // TMP Label caption не обновляется стандартно + + dd.onValueChanged.AddListener(i => + { + RefreshDropdownCaption(dd); + if (i >= 0 && i < languages.Count) + Pend("Language", () => Localization.instance.SetLanguage(languages[i])); + }); + } + catch (Exception ex) { Auga.LogWarning($"[AugaSettings] Language dropdown: {ex.Message}"); } + } + + private static void WireResolutionDropdown(Transform page) + { + var go = FindDeepChild(page, "Resolution"); + if (go == null) return; + var dd = go.GetComponentInChildren(true); + if (dd == null) return; + try + { + var resolutions = Screen.resolutions; + if (resolutions == null || resolutions.Length == 0) return; + + // Дедупликация по w×h (Unity 6 возвращает дубликаты при разных refresh rate) + var seen = new HashSet(); + var unique = new List(); + foreach (var r in resolutions) + { + string key = r.width + "x" + r.height; + if (seen.Add(key)) unique.Add(r); + } + + var options = unique.Select(r => r.width + "x" + r.height).ToList(); + dd.ClearOptions(); + dd.AddOptions(options); + + // Находим текущее разрешение + int currentIdx = 0; + for (int i = 0; i < unique.Count; i++) + if (unique[i].width == Screen.width && unique[i].height == Screen.height) + currentIdx = i; + dd.SetValueWithoutNotify(currentIdx); + RefreshDropdownCaption(dd); + + dd.onValueChanged.AddListener(i => + { + RefreshDropdownCaption(dd); + if (i >= 0 && i < unique.Count) + { + var r = unique[i]; + Pend("Resolution", () => Screen.SetResolution(r.width, r.height, Screen.fullScreen)); + } + }); + } + catch (Exception ex) { Auga.LogWarning($"[AugaSettings] Resolution dropdown: {ex.Message}"); } + } + + /// + /// Legacy Dropdown с TMP Label (TMPro.TextMeshProUGUI) не может обновить + /// Caption автоматически (m_CaptionText ожидает UI.Text). + /// Обновляем TMP Label вручную по текущему dd.value. + /// + private static void RefreshDropdownCaption(Dropdown dd) + { + var lbl = dd.transform.Find("TMP Label")?.GetComponent(); + if (lbl != null && dd.value >= 0 && dd.value < dd.options.Count) + lbl.text = dd.options[dd.value].text; + } + + private static GameObject FindDeepChild(Transform root, string name) + { + if (root.gameObject.name == name) return root.gameObject; + for (int i = 0; i < root.childCount; i++) + { + var found = FindDeepChild(root.GetChild(i), name); + if (found != null) return found; + } + return null; + } + } +} diff --git a/Auga/AugaTS.zip b/Auga/AugaTS.zip deleted file mode 100644 index f07a9c8a..00000000 Binary files a/Auga/AugaTS.zip and /dev/null differ diff --git a/Auga/AugaTS1.1.zip b/Auga/AugaTS1.1.zip deleted file mode 100644 index 16181320..00000000 Binary files a/Auga/AugaTS1.1.zip and /dev/null differ diff --git a/Auga/AugaTile.png b/Auga/AugaTile.png deleted file mode 100644 index 36bb00ce..00000000 Binary files a/Auga/AugaTile.png and /dev/null differ diff --git a/Auga/DamageText_Setup.cs b/Auga/DamageText_Setup.cs index 3a7d965b..cbbe79a8 100644 --- a/Auga/DamageText_Setup.cs +++ b/Auga/DamageText_Setup.cs @@ -18,7 +18,7 @@ public static bool Prefix(TextInput __instance) [HarmonyPatch(typeof(DamageText), nameof(DamageText.AddInworldText))] [HarmonyPostfix] - public static void AddInworldText_Postfix(DamageText __instance, DamageText.TextType type, float dmg, bool mySelf) + public static void AddInworldText_Postfix(DamageText __instance, DamageText.TextType type, string text, bool mySelf) { var worldTextInstance = __instance.m_worldTexts.LastOrDefault(); if (worldTextInstance == null) @@ -26,6 +26,10 @@ public static void AddInworldText_Postfix(DamageText __instance, DamageText.Text return; } + // "dmg" parameter was removed in current Valheim; parse damage from the text string + float.TryParse(text, System.Globalization.NumberStyles.Float, + System.Globalization.CultureInfo.InvariantCulture, out var dmg); + Color color; if (type == DamageText.TextType.Heal) { diff --git a/Auga/EnemeyHud_Setup.cs b/Auga/EnemeyHud_Setup.cs index 433dd85e..efdfba7c 100644 --- a/Auga/EnemeyHud_Setup.cs +++ b/Auga/EnemeyHud_Setup.cs @@ -1,5 +1,6 @@ using System.Linq; using HarmonyLib; +using TMPro; using UnityEngine; using UnityEngine.UI; @@ -61,8 +62,8 @@ public static void Postfix(EnemyHud __instance, Character c) newLevelXDisplay.name = levelXDisplayName; } - var text = levelDisplayX.GetComponentInChildren(); - text.text = $"x {level - 1}"; + var text = levelDisplayX.GetComponentInChildren(); + if (text != null) text.text = $"x {level - 1}"; } } } diff --git a/Auga/Hud_Setup.cs b/Auga/Hud_Setup.cs index 999e19e7..ada06445 100644 --- a/Auga/Hud_Setup.cs +++ b/Auga/Hud_Setup.cs @@ -177,27 +177,13 @@ public static void Hud_Awake_Postfix(Hud __instance) var selectedField = child.Find("Selected").gameObject; var selectedTextField = selectedField.transform.Find("Text").GetComponent(); - var augaTextComponent = augaText.GetComponent(); - textField.color = augaTextComponent.color; - textField.font = augaTextComponent.font; - textField.fontStyle = augaTextComponent.fontStyle; - textField.fontSize = augaTextComponent.fontSize; - textField.material = augaTextComponent.material; - - var augaSelectedTextComponent = augaSelectedText.GetComponent(); - selectedTextField.color = augaSelectedTextComponent.color; - selectedTextField.font = augaSelectedTextComponent.font; - selectedTextField.fontStyle = augaSelectedTextComponent.fontStyle; - selectedTextField.fontSize = augaSelectedTextComponent.fontSize; - selectedTextField.material = augaSelectedTextComponent.material; - + // TODO: augaText/augaSelectedText source GameObjects were lost; text styling skipped var image = selectedField.GetComponent(); image.color = new Color(image.color.r, image.color.g, image.color.b, 0.0f); } - var iconMaterial = __instance.m_pieceIconPrefab.transform.Find("icon").GetComponent().material; - Auga.Assets.BuildHudElement.transform.Find("icon").GetComponent().material = iconMaterial; + // iconMaterial already captured at the top of this if-block; just assign the prefab __instance.m_pieceIconPrefab = Auga.Assets.BuildHudElement; var pieceRoot = __instance.m_pieceSelectionWindow.transform.Find("PieceList/Root").gameObject; @@ -231,7 +217,8 @@ public static void Hud_Awake_Postfix(Hud __instance) Auga.UpdateStatBars(); Localization.instance.Localize(__instance.transform); - } + } // end if (Auga.BuildMenuShow.Value && !Auga.HasSearsCatalog) + } // end Hud_Awake_Postfix [HarmonyPatch(nameof(Hud.UpdateStatusEffects))] [HarmonyPrefix] @@ -377,7 +364,7 @@ public static void SetupPieceInfo(Hud instance, Piece piece) if (snappingIconForPiece != null) { instance.m_snappingIcon.sprite = snappingIconForPiece; - instance.m_snappingIcon.enabled = snappingIconForPiece != null && (piece.m_category == Piece.PieceCategory.Building || piece.m_groundPiece || piece.m_waterPiece); + instance.m_snappingIcon.enabled = snappingIconForPiece != null && (piece.m_category == (Piece.PieceCategory)2 || piece.m_groundPiece || piece.m_waterPiece); } for (int index = 0; index < instance.m_requirementItems.Length; ++index) { @@ -707,7 +694,7 @@ public static bool Prefix(Hud __instance, Player player, Vector2Int selectedNr, } __instance.m_lastPieceCategory = category; - __instance.m_pieceBarPosX = __instance.m_pieceBarTargetPosX; + // m_pieceBarPosX removed in current Valheim; skipping position reset __instance.UpdatePieceBuildStatusAll(buildPieces, player); return false; diff --git a/Auga/ILRepack.targets b/Auga/ILRepack.targets index 758218f0..ecc561df 100644 --- a/Auga/ILRepack.targets +++ b/Auga/ILRepack.targets @@ -1,12 +1,8 @@ - + - - - - - - - - - - \ No newline at end of file + + diff --git a/Auga/MainMenu_Setup.cs b/Auga/MainMenu_Setup.cs index b6e050f1..a0ce7aea 100644 --- a/Auga/MainMenu_Setup.cs +++ b/Auga/MainMenu_Setup.cs @@ -1,11 +1,14 @@ -using AugaUnity; +using System; +using System.Collections; +using AugaUnity; using HarmonyLib; +using TMPro; using UnityEngine; +using UnityEngine.Events; using UnityEngine.UI; namespace Auga { - [HarmonyPatch(typeof(TextsDialog), nameof(TextsDialog.SnapTo))] public static class SnapTo_Patch { @@ -23,226 +26,757 @@ public static void Postfix(TextsDialog __instance, RectTransform listRoot, Scrol [HarmonyPatch(typeof(FejdStartup), nameof(FejdStartup.Awake))] public static class FejdStartup_Awake_Patch { + // Переданные из Prefix в Postfix данные о волосах/бороде + private static ItemDrop _originalNoHair; + private static ItemDrop _originalNoBeard; + + // Null-safe Find + GetComponent. Logs warning if path or component missing. + private static T FC(Transform root, string path) where T : Component + { + if (root == null) { Auga.LogWarning($"FC<{typeof(T).Name}>: root is null (path={path})"); return null; } + var t = root.Find(path); + if (t == null) { Auga.LogWarning($"FC<{typeof(T).Name}>: path not found: {path}"); return null; } + var c = t.GetComponent(); + if (c == null) Auga.LogWarning($"FC<{typeof(T).Name}>: no component on: {path}"); + return c; + } + + // Null-safe Find -> GameObject. + private static GameObject FO(Transform root, string path) + { + if (root == null) { Auga.LogWarning($"FO: root is null (path={path})"); return null; } + var t = root.Find(path); + if (t == null) { Auga.LogWarning($"FO: path not found: {path}"); return null; } + return t.gameObject; + } + public static void Prefix(FejdStartup __instance) { ZInput.Initialize(); - //var originalChangeLogAsset = __instance.GetComponentInChildren(true).m_changeLog; - __instance.m_settingsPrefab = Auga.Assets.SettingsPrefab; - /*var originalLogo = __instance.transform.Find("Menu/Logo"); - originalLogo.SetParent(__instance.transform, true); + // Сохраняем логотип до замены меню + var originalLogo = __instance.transform.Find("Menu/Logo"); + if (originalLogo != null) + originalLogo.SetParent(__instance.transform, true); + // Заменяем все префабы — должно произойти ДО ванильного Awake(), + // чтобы ванильный код нашёл нужные объекты по именам. var mainMenu = __instance.Replace("Menu", Auga.Assets.MainMenuPrefab); - originalLogo.SetParent(mainMenu, true); - - __instance.m_mainMenu = mainMenu.gameObject; - __instance.m_menuList = mainMenu.Find("MenuList").gameObject; - __instance.m_menuSelectedButton = mainMenu.Find("MenuList/StartGame").GetComponent