From 49ad47bb4d4cef6ba876ad1471c764b3c29b953b Mon Sep 17 00:00:00 2001 From: zeobviouslyfakeacc Date: Sat, 8 Sep 2018 23:23:35 +0200 Subject: [PATCH] Initial commit --- .gitignore | 4 ++ LICENSE | 21 +++++++ Patches.cs | 103 +++++++++++++++++++++++++++++++++ Properties/AssemblyInfo.cs | 15 +++++ ReadingSettings.cs | 90 ++++++++++++++++++++++++++++ ShorterReadingIntervals.csproj | 46 +++++++++++++++ ShorterReadingIntervals.sln | 25 ++++++++ 7 files changed, 304 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Patches.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 ReadingSettings.cs create mode 100644 ShorterReadingIntervals.csproj create mode 100644 ShorterReadingIntervals.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd7da14 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin +obj +.vs +*.user diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..317132f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 zeobviouslyfakeacc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Patches.cs b/Patches.cs new file mode 100644 index 0000000..8914c9c --- /dev/null +++ b/Patches.cs @@ -0,0 +1,103 @@ +using System; +using System.Reflection.Emit; +using System.Collections.Generic; +using Harmony; +using UnityEngine; + +namespace ShorterReadingIntervals { + internal static class Patches { + + [HarmonyPatch(typeof(Panel_Inventory_Examine), "RefreshHoursToRead", new Type[0])] + private static class FixHoursToRead { + private static bool Prefix(Panel_Inventory_Examine __instance) { + float hoursResearchRemaining = GetHoursResearchRemaining(__instance); + float readingInterval = Settings.GetReadingIntervalHours(); + int maximumIntervals = Mathf.CeilToInt(hoursResearchRemaining / readingInterval); + + Traverse hoursTraverse = Traverse.Create(__instance).Field("m_HoursToRead"); + int intervalsToRead = hoursTraverse.GetValue(); + hoursTraverse.SetValue(Mathf.Clamp(intervalsToRead, 1, maximumIntervals)); + + float hoursToRead = Math.Min(intervalsToRead * readingInterval, hoursResearchRemaining); + __instance.m_TimeToReadLabel.text = hoursToRead.ToString("0.##"); + __instance.m_ReadHoursDecrease.gameObject.SetActive(intervalsToRead > 1); + __instance.m_ReadHoursIncrease.gameObject.SetActive(intervalsToRead < maximumIntervals); + + if (Utils.IsGamepadActive()) { + ButtonLegend buttonLegend = __instance.m_ButtonLegendContainer.m_ButtonLegend; + buttonLegend.ConfigureButtonIconSpriteName("Inventory_FilterLeft", ref __instance.m_GamepadReadHoursSpriteDecrease); + buttonLegend.ConfigureButtonIconSpriteName("Inventory_FilterRight", ref __instance.m_GamepadReadHoursSpriteIncrease); + } + + return false; // Never run the original + } + } + + [HarmonyPatch(typeof(Panel_Inventory_Examine), "OnReadHoursIncrease", new Type[0])] + private static class FixOnReadHoursIncrease { + private static bool Prefix(Panel_Inventory_Examine __instance) { + float hoursResearchRemaining = GetHoursResearchRemaining(__instance); + int maximumIntervals = Mathf.CeilToInt(hoursResearchRemaining / Settings.GetReadingIntervalHours()); + + Traverse hoursTraverse = Traverse.Create(__instance).Field("m_HoursToRead"); + int intervalsToRead = hoursTraverse.GetValue(); + + if (intervalsToRead >= maximumIntervals) { + GameAudioManager.PlayGUIError(); + } else { + hoursTraverse.SetValue(intervalsToRead + 1); + GameAudioManager.PlayGUIScroll(); + AccessTools.Method(typeof(Panel_Inventory_Examine), "RefreshHoursToRead").Invoke(__instance, new object[0]); + } + + return false; // Never run the original + } + } + + [HarmonyPatch(typeof(Panel_Inventory_Examine), "StartRead", new Type[] { typeof(int), typeof(string) })] + private static class ScaleStartRead { + private static void Prefix(Panel_Inventory_Examine __instance, ref int durationMinutes) { + float hoursResearchRemaining = GetHoursResearchRemaining(__instance); + float minutesToRead = Math.Min(durationMinutes * Settings.GetReadingIntervalHours(), hoursResearchRemaining * 60f); + float hoursToRead = minutesToRead / 60f; + + __instance.m_ReadTimeSeconds = 1 + 3 * Mathf.Log(1 + hoursToRead); + durationMinutes = Mathf.CeilToInt(minutesToRead); + } + } + + [HarmonyPatch(typeof(Panel_Inventory_Examine), "ReadComplete", new Type[] { typeof(float) })] + private static class ScaleReadComplete { + private static void Prefix(Panel_Inventory_Examine __instance, ref float normalizedProgress) { + int hoursToRead = Traverse.Create(__instance).Field("m_HoursToRead").GetValue(); + float intervalsRead = normalizedProgress * hoursToRead; + if (!Settings.GetAllowInterruptions()) { + intervalsRead = Mathf.Floor(intervalsRead); + } + + float hoursRead = intervalsRead * Settings.GetReadingIntervalHours(); + __instance.m_GearItem.m_ResearchItem.Read(hoursRead); + + // Do the rest of the method as if we read for 0 minutes + normalizedProgress = 0; + } + } + + [HarmonyPatch(typeof(Panel_Inventory_Examine), "RefreshReadPanel", new Type[0])] + private static class DisplayHoursReadProgressAsFraction { + private static IEnumerable Transpiler(IEnumerable instructions) { + foreach (CodeInstruction instruction in instructions) { + if (instruction.opcode == OpCodes.Ldstr && ((string) instruction.operand) == "F0") { + instruction.operand = "0.##"; + } + yield return instruction; + } + } + } + + private static float GetHoursResearchRemaining(Panel_Inventory_Examine panel) { + ResearchItem researchItem = panel.m_GearItem.m_ResearchItem; + return researchItem.m_TimeRequirementHours - researchItem.GetElapsedHours(); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..daf7863 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,15 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("ShorterReadingIntervals")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ShorterReadingIntervals")] +[assembly: AssemblyCopyright("MIT License")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("2878cf6b-b84b-4573-8185-12a5654f0496")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ReadingSettings.cs b/ReadingSettings.cs new file mode 100644 index 0000000..8c45eba --- /dev/null +++ b/ReadingSettings.cs @@ -0,0 +1,90 @@ +using System; +using System.IO; +using System.Reflection; +using ModSettings; +using UnityEngine; + +namespace ShorterReadingIntervals { + internal class ReadingSettings : ModSettingsBase { + + [Name("Reading interval length")] + [Description("Sets the shortest amount of time that a book can be read for.")] + [Choice("15 minutes", "30 minutes", "60 minutes")] + public IntervalLength intervalLength = IntervalLength.MINS_30; + + [Name("Count interrupted progress")] + [Description("Whether progress within a reading interval should still be counted when you're interrupted.")] + public bool allowInterruptions = true; + + protected override void OnConfirm() { + Settings.Save(); + } + } + + internal static class Settings { + + private static readonly string MODS_FOLDER_PATH = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + private static readonly string SETTINGS_PATH = Path.Combine(MODS_FOLDER_PATH, "ShorterReadingIntervals.json"); + + private static ReadingSettings settings; + + public static void OnLoad() { + settings = LoadOrCreateSettings(); + settings.AddToModSettings("Shorter Reading Intervals"); + + Version version = Assembly.GetExecutingAssembly().GetName().Version; + Debug.Log("[ShorterReadingIntervals] Version " + version + " loaded!"); + } + + private static ReadingSettings LoadOrCreateSettings() { + if (!File.Exists(SETTINGS_PATH)) { + Debug.Log("[ShorterReadingIntervals] Settings file did not exist, using default settings."); + return new ReadingSettings(); + } + + try { + string json = File.ReadAllText(SETTINGS_PATH, System.Text.Encoding.UTF8); + return JsonUtility.FromJson(json); + } catch (Exception ex) { + Debug.LogError("[ShorterReadingIntervals] Error while trying to read config file:"); + Debug.LogException(ex); + + // Re-throw to make error show up in main menu + throw new IOException("Error while trying to read config file", ex); + } + } + + internal static void Save() { + try { + string json = JsonUtility.ToJson(settings, prettyPrint: true); + File.WriteAllText(SETTINGS_PATH, json, System.Text.Encoding.UTF8); + Debug.Log("[ShorterReadingIntervals] Config file saved to " + SETTINGS_PATH); + } catch (Exception ex) { + Debug.LogError("[ShorterReadingIntervals] Error while trying to write config file:"); + Debug.LogException(ex); + } + } + + internal static float GetReadingIntervalHours() { + switch (settings.intervalLength) { + case IntervalLength.MINS_60: + return 1f; + case IntervalLength.MINS_30: + return 0.5f; + case IntervalLength.MINS_15: + return 0.25f; + default: + Debug.LogError("Unknown interval length: " + settings.intervalLength); + return 1f; + } + } + + internal static bool GetAllowInterruptions() { + return settings.allowInterruptions; + } + } + + internal enum IntervalLength { + MINS_15, MINS_30, MINS_60 + } +} diff --git a/ShorterReadingIntervals.csproj b/ShorterReadingIntervals.csproj new file mode 100644 index 0000000..1662e85 --- /dev/null +++ b/ShorterReadingIntervals.csproj @@ -0,0 +1,46 @@ + + + + + Debug + AnyCPU + {2878CF6B-B84B-4573-8185-12A5654F0496} + Library + Properties + ShorterReadingIntervals + ShorterReadingIntervals + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ShorterReadingIntervals.sln b/ShorterReadingIntervals.sln new file mode 100644 index 0000000..dbc961b --- /dev/null +++ b/ShorterReadingIntervals.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShorterReadingIntervals", "ShorterReadingIntervals.csproj", "{2878CF6B-B84B-4573-8185-12A5654F0496}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2878CF6B-B84B-4573-8185-12A5654F0496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2878CF6B-B84B-4573-8185-12A5654F0496}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2878CF6B-B84B-4573-8185-12A5654F0496}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2878CF6B-B84B-4573-8185-12A5654F0496}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {246A816D-F4A1-41E2-9CB4-D2650AF26804} + EndGlobalSection +EndGlobal