using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace OstranautsVolunteer { internal static class DataPackInstaller { private const string PackName = "VolunteerShift"; internal static void Run() { try { string text = FindSource(); if (text == null) { return; } string text2 = Path.Combine(Application.dataPath, Path.Combine("Mods", "VolunteerShift")); string text3 = ReadVersion(Path.Combine(text, "mod_info.json")); string text4 = (Directory.Exists(text2) ? ReadVersion(Path.Combine(text2, "mod_info.json")) : null); if (text4 == null || !(text4 == text3)) { bool num = text4 == null && !Directory.Exists(text2); if (Directory.Exists(text2)) { Directory.Delete(text2, recursive: true); } CopyTree(text, text2); if (num) { Plugin.Log.LogWarning((object)("Installed the Civic Volunteer data pack (v" + text3 + ") into Ostranauts_Data/Mods/VolunteerShift. One-time step: enable Civic Volunteer in the in-game MODS menu, then restart the game. The game updates loading_order.json itself when you do.")); return; } Plugin.Log.LogInfo((object)("Civic Volunteer data pack refreshed (" + (text4 ?? "unknown") + " -> " + text3 + ").")); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not self-install the data pack (" + ex.GetType().Name + ": " + ex.Message + "). Manual fix: copy the mod's DataPack/VolunteerShift folder into Ostranauts_Data/Mods/, then enable Civic Volunteer in the in-game MODS menu.")); } } private static string? FindSource() { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); for (int i = 0; i < 3; i++) { if (string.IsNullOrEmpty(directoryName)) { break; } string text = Path.Combine(Path.Combine(directoryName, "DataPack"), "VolunteerShift"); if (File.Exists(Path.Combine(text, "mod_info.json"))) { return text; } directoryName = Path.GetDirectoryName(directoryName); } return null; } private static string? ReadVersion(string modInfoPath) { if (!File.Exists(modInfoPath)) { return null; } Match match = Regex.Match(File.ReadAllText(modInfoPath), "\"strModVersion\"\\s*:\\s*\"([^\"]+)\""); if (!match.Success) { return null; } return match.Groups[1].Value; } private static void CopyTree(string src, string dest) { Directory.CreateDirectory(dest); string[] files = Directory.GetFiles(src); foreach (string text in files) { File.Copy(text, Path.Combine(dest, Path.GetFileName(text)), overwrite: true); } files = Directory.GetDirectories(src); foreach (string text2 in files) { CopyTree(text2, Path.Combine(dest, Path.GetFileName(text2))); } } } [BepInPlugin("com.ostranauts.volunteer", "Civic Volunteer", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.ostranauts.volunteer"; internal static ManualLogSource Log; internal static ConfigEntry? TricklePerRepair; private Harmony? _harmony; private void Awake() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; TricklePerRepair = ((BaseUnityPlugin)this).Config.Bind("Payout", "AltruismPerRepairAction", 0.2f, new ConfigDescription("Altruism worked off per individual repair action on public station infrastructure, before the faction-standing multiplier (0.5-2.0). Deliberately generous relative to the fast-forwarded volunteer hour (-12.5/hr flat) so that playing a shift out beats skipping it: at 0.2 with a neutral station, real-time overtakes fast-forward at roughly 70 repair actions per game-hour. A full repair takes many actions, so the larger completion bonus still lands when an item is actually fixed. Set 0 to pay only completion bonuses.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), Array.Empty())); DataPackInstaller.Run(); VolunteerShift.Register(); _harmony = new Harmony("com.ostranauts.volunteer"); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"Civic Volunteer loaded."); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class VolunteerScope { private static readonly Dictionary ReputationCoeff = new Dictionary { { (Reputation)0, 2f }, { (Reputation)1, 1.5f }, { (Reputation)2, 1.25f }, { (Reputation)4, 1f }, { (Reputation)8, 0.75f }, { (Reputation)16, 0.5f } }; public static bool IsPublicInfrastructure(Ship? ship) { if (ship == null || ship.bDestroyed) { return false; } if (!ship.IsStation(true)) { return false; } if (ship.IsDerelict()) { return false; } return true; } public static bool ShouldVolunteerOn(CondOwner? co, Ship? ship) { if ((Object)(object)co == (Object)null || ship == null) { return false; } if (!VolunteerShift.IsVolunteering(co)) { return false; } if (co.OwnsShip(ship.strRegID)) { return false; } return IsPublicInfrastructure(ship); } public static float AltruismCoeff(CondOwner? co, Ship? ship) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)co == (Object)null || (Object)(object)((ship != null) ? ship.ShipCO : null) == (Object)null) { return 1f; } List allFactions = ship.ShipCO.GetAllFactions(); Reputation reputation = JsonFaction.GetReputation(co.GetFactionScore(allFactions)); if (!ReputationCoeff.TryGetValue(reputation, out var value)) { return 1f; } return value; } } internal static class VolunteerShift { public const int ID = 3; public const string Name = "Civic"; public const string CondLoot = "CONDShiftVolunteer"; public const string Cond = "IsShiftVolunteer"; public const string TickInteraction = "Tick1HourShiftVolunteer"; public const string RepairDoneLoot = "CONDVolunteerRepairDone"; public const string RepairAltruismLoot = "CONDVolunteerRepairAltruism"; public const string RepairTickLoot = "CONDVolunteerRepairTick"; private static readonly Color Colour = new Color(0.24f, 0.72f, 0.42f); private static bool _registered; private static bool _dataChecked; public static bool Registered => _registered; public static void Register() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (_registered) { return; } Dictionary dictionary = JsonCompanyRules.Shifts(); if (dictionary == null) { Plugin.Log.LogError((object)"JsonCompanyRules.Shifts() returned null; volunteer shift not registered."); return; } if (!dictionary.ContainsKey(3)) { dictionary[3] = new JsonShift(3, "Civic", "CONDShiftVolunteer", Colour); } _registered = true; Plugin.Log.LogInfo((object)string.Format("Registered shift {0} '{1}' ({2} shifts total).", 3, "Civic", dictionary.Count)); } public static void VerifyDataPack() { if (_dataChecked) { return; } _dataChecked = true; List list = new List(); string[] array = new string[4] { "CONDShiftVolunteer", "CONDVolunteerRepairTick", "CONDVolunteerRepairAltruism", "CONDVolunteerRepairDone" }; foreach (string text in array) { if (DataHandler.GetLoot(text) == null) { list.Add(text); } } if (list.Count == 0) { Plugin.Log.LogInfo((object)"Data pack found; payouts are active."); } else { Plugin.Log.LogError((object)("DATA PACK NOT LOADED: the Civic Volunteer plugin is running, but its data pack isn't registered, so the shift exists and pays nothing. Missing: " + string.Join(", ", list.ToArray()) + ". Fix: copy the mod's VolunteerShift folder into Ostranauts_Data/Mods/, then enable it in the in-game MODS menu (it must also appear in Ostranauts_Data/Mods/loading_order.json -- the game adds it there when you enable it).")); } } public static bool IsVolunteering(CondOwner? co) { if ((Object)(object)co != (Object)null) { return co.HasCond("IsShiftVolunteer"); } return false; } } } namespace OstranautsVolunteer.Patches { [HarmonyPatch(typeof(CondOwner), "ProcessAutoTasks")] internal static class AutoTaskScopePatch { private static readonly MethodInfo OwnsShipMethod = AccessTools.Method(typeof(CondOwner), "OwnsShip", new Type[1] { typeof(string) }, (Type[])null); private static readonly MethodInfo ReplacementMethod = AccessTools.Method(typeof(AutoTaskScopePatch), "MayGatherFrom", (Type[])null, (Type[])null); public static bool MayGatherFrom(CondOwner co, string strRegID) { if ((Object)(object)co == (Object)null) { return false; } if (co.OwnsShip(strRegID)) { return true; } return VolunteerScope.ShouldVolunteerOn(co, co.ship); } [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable instructions) { if (OwnsShipMethod == null || ReplacementMethod == null) { Plugin.Log.LogError((object)"Could not resolve OwnsShip; volunteer task scope NOT applied."); foreach (CodeInstruction instruction in instructions) { yield return instruction; } yield break; } int swapped = 0; foreach (CodeInstruction instruction2 in instructions) { if (CodeInstructionExtensions.Calls(instruction2, OwnsShipMethod)) { swapped++; yield return CodeInstructionExtensions.WithBlocks(CodeInstructionExtensions.WithLabels(new CodeInstruction(OpCodes.Call, (object)ReplacementMethod), (IEnumerable