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<float>? 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<float>("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<float>(0f, 5f), Array.Empty<object>()));
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<Reputation, float> ReputationCoeff = new Dictionary<Reputation, float>
{
{
(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<string> 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<int, JsonShift> 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<string> list = new List<string>();
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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<Label>)instruction2.labels), (IEnumerable<ExceptionBlock>)instruction2.blocks);
}
else
{
yield return instruction2;
}
}
if (swapped == 0)
{
Plugin.Log.LogError((object)"ProcessAutoTasks contained no OwnsShip call. Game version likely changed; volunteer task scope NOT applied.");
}
else
{
Plugin.Log.LogInfo((object)$"Volunteer task scope applied ({swapped} call site(s)).");
}
}
}
[HarmonyPatch(typeof(GUIFFWDRow), "SetCrew")]
internal static class FFWDRowPatch
{
private const string FreeKey = "Tick1HourShiftFree";
private static readonly FieldRef<GUIFFWDRow, Dictionary<string, int>?> PayloadsRef = AccessTools.FieldRefAccess<GUIFFWDRow, Dictionary<string, int>>("dictPayloads");
private static readonly FieldRef<GUIFFWDRow, TMP_Text[]?> HoursRef = AccessTools.FieldRefAccess<GUIFFWDRow, TMP_Text[]>("aHours");
[HarmonyPostfix]
private static void Postfix(GUIFFWDRow __instance, CondOwner co, int nHours)
{
try
{
if (((co != null) ? co.Company : null) == null)
{
return;
}
Dictionary<string, int> dictionary = PayloadsRef.Invoke(__instance);
if (dictionary == null)
{
return;
}
TMP_Text[] array = HoursRef.Invoke(__instance);
string title = DataHandler.GetInteraction("Tick1HourShiftVolunteer", (JsonInteractionSave)null, false)?.strTitle ?? "Civic";
int num = 0;
int num2 = ((array != null) ? Math.Min(nHours, array.Length) : nHours);
for (int i = 0; i < num2; i++)
{
JsonShift shift = co.Company.GetShift(StarSystem.nUTCHour + i, co);
if (shift != null && shift.nID == 3)
{
num++;
RelabelCell(array, i, title);
}
}
if (num != 0)
{
if (dictionary.TryGetValue("Tick1HourShiftFree", out var value))
{
dictionary["Tick1HourShiftFree"] = Math.Max(0, value - num);
}
dictionary["Tick1HourShiftVolunteer"] = num;
}
}
catch (Exception arg)
{
Plugin.Log.LogError((object)$"FFWDRowPatch failed, volunteer hours will pay as free time: {arg}");
}
}
private static void RelabelCell(TMP_Text[]? cells, int i, string title)
{
if (cells != null && i < cells.Length && !((Object)(object)cells[i] == (Object)null))
{
string text = cells[i].text ?? string.Empty;
int num = text.IndexOf('\n');
cells[i].text = ((num < 0) ? title : (title + text.Substring(num)));
}
}
}
[HarmonyPatch(typeof(Interaction), "ApplyEffects")]
internal static class RepairPayoutPatch
{
private readonly struct Claim
{
public readonly CondOwner Actor;
public readonly CondOwner Item;
public readonly Ship? Hull;
public readonly float Coeff;
public readonly double StartDamage;
public Claim(CondOwner actor, CondOwner item, Ship? hull, float coeff, double startDamage)
{
Actor = actor;
Item = item;
Hull = hull;
Coeff = coeff;
StartDamage = startDamage;
}
}
private static readonly HashSet<string> PaidDuties = new HashSet<string>(StringComparer.Ordinal) { "Repair", "Patch", "Restore" };
private static readonly Dictionary<string, Claim> Pending = new Dictionary<string, Claim>(StringComparer.Ordinal);
private static int _trickleCount;
private static float _trickleSum;
private static double _firstTrickleEpoch;
private static int _shiftActions;
private static float _shiftAltruism;
private static int _shiftCompletions;
private static double _shiftStartEpoch;
private static double _shiftStartAltruism;
private static double _shiftAttributed;
[HarmonyPostfix]
private static void Postfix(Interaction __instance, bool isCancelIa)
{
try
{
if (isCancelIa || __instance == null)
{
return;
}
if (__instance.strName == "Tick1HourShiftVolunteer")
{
Plugin.Log.LogInfo((object)"Volunteer FFWD: applied one fast-forwarded volunteer hour (-12.5 altruism from CONDTick1HourVolunteerMoods).");
}
else
{
if (__instance.strDuty == null || !PaidDuties.Contains(__instance.strDuty))
{
return;
}
SweepCompleted();
CondOwner objUs = __instance.objUs;
if (objUs == null || !VolunteerShift.IsVolunteering(objUs))
{
return;
}
CondOwner objThem = __instance.objThem;
if (objThem == null)
{
return;
}
Ship val = objThem.ship ?? objUs.ship;
if (!VolunteerScope.ShouldVolunteerOn(objUs, val))
{
return;
}
float num = VolunteerScope.AltruismCoeff(objUs, val);
float num2 = Plugin.TricklePerRepair?.Value ?? 0f;
if (num2 > 0f)
{
double num3 = CurrentAltruism();
Loot loot = DataHandler.GetLoot("CONDVolunteerRepairTick");
if (loot != null)
{
loot.ApplyCondLoot(objUs, num2 * num, (string)null, 0f);
}
_shiftAttributed += num3 - CurrentAltruism();
CountTrickle(num2 * num);
}
if (!Pending.ContainsKey(objThem.strID))
{
Pending[objThem.strID] = new Claim(objUs, objThem, val, num, objThem.GetCondAmount("StatDamage"));
}
}
}
catch (Exception arg)
{
Plugin.Log.LogError((object)$"RepairPayoutPatch failed: {arg}");
}
}
internal static void SweepCompleted()
{
if (Pending.Count == 0)
{
return;
}
List<string> list = null;
foreach (KeyValuePair<string, Claim> item2 in Pending)
{
CondOwner item = item2.Value.Item;
if (item == null || item.bDestroyed)
{
(list ?? (list = new List<string>())).Add(item2.Key);
}
else if (!(item.GetCondAmount("StatDamage") > 0.0))
{
if (item2.Value.StartDamage <= 0.0)
{
(list ?? (list = new List<string>())).Add(item2.Key);
continue;
}
(list ?? (list = new List<string>())).Add(item2.Key);
Pay(item2.Value, item);
}
}
if (list == null)
{
return;
}
foreach (string item3 in list)
{
Pending.Remove(item3);
}
}
private static void Pay(Claim claim, CondOwner item)
{
if (claim.Actor != null && VolunteerShift.IsVolunteering(claim.Actor))
{
Loot loot = DataHandler.GetLoot("CONDVolunteerRepairDone");
Loot loot2 = DataHandler.GetLoot("CONDVolunteerRepairAltruism");
if (loot == null || loot2 == null)
{
Plugin.Log.LogWarning((object)"Volunteer payout loot missing. Is the VolunteerShift data folder installed and listed in loading_order.json?");
return;
}
double num = CurrentAltruism();
loot.ApplyCondLoot(claim.Actor, 1f, (string)null, 0f);
loot2.ApplyCondLoot(claim.Actor, claim.Coeff, (string)null, 0f);
_shiftAttributed += num - CurrentAltruism();
_shiftCompletions++;
Plugin.Log.LogInfo((object)("Volunteer repair: " + claim.Actor.strNameFriendly + " finished " + (item.strNameFriendly ?? "?") + " / " + (claim.Hull?.strRegID ?? "?") + " " + $"(altruism coeff {claim.Coeff:0.##})"));
}
}
internal static void OnVolunteerShiftStart()
{
_shiftActions = 0;
_shiftAltruism = 0f;
_shiftCompletions = 0;
_shiftAttributed = 0.0;
_shiftStartEpoch = StarSystem.fEpoch;
_shiftStartAltruism = ((CrewSim.coPlayer == null) ? double.NaN : CurrentAltruism());
}
private static double CurrentAltruism()
{
CondOwner coPlayer = CrewSim.coPlayer;
if (coPlayer != null)
{
return coPlayer.GetCondAmount("StatAltruism");
}
return 0.0;
}
internal static void OnVolunteerShiftEnd(string who)
{
SweepCompleted();
Pending.Clear();
if (_shiftActions == 0)
{
string text = (double.IsNaN(_shiftStartAltruism) ? "no baseline captured" : ($"StatAltruism {_shiftStartAltruism:0.#} -> {CurrentAltruism():0.#} " + $"(moved {_shiftStartAltruism - CurrentAltruism():0.#})"));
Plugin.Log.LogInfo((object)("Volunteer shift ended for " + who + ": no repair actions; " + text + ". Fast-forwarded hours and drift only."));
}
else
{
double num = (StarSystem.fEpoch - _shiftStartEpoch) / 3600.0;
double num2 = (double.IsNaN(_shiftStartAltruism) ? 0.0 : (_shiftStartAltruism - CurrentAltruism()));
Plugin.Log.LogInfo((object)($"Volunteer shift ended for {who}: {_shiftActions} actions, " + $"{_shiftCompletions} completed over {num:0.##} game-hr " + $"({(double)_shiftActions / Math.Max(num, 0.01):0.#} actions/hr). " + $"StatAltruism {_shiftStartAltruism:0.#} -> {CurrentAltruism():0.#} " + $"(moved {num2:0.#} total, of which {_shiftAttributed:0.##} from " + "volunteer payouts; rest is drift + vanilla free-will)."));
}
}
private static void CountTrickle(float amount)
{
if (_trickleCount == 0)
{
_firstTrickleEpoch = StarSystem.fEpoch;
}
if (double.IsNaN(_shiftStartAltruism))
{
_shiftStartAltruism = CurrentAltruism() + (double)amount;
}
_trickleCount++;
_trickleSum += amount;
_shiftActions++;
_shiftAltruism += amount;
if (_trickleCount % 50 == 0)
{
double num = (StarSystem.fEpoch - _firstTrickleEpoch) / 3600.0;
string arg = ((num > 0.01) ? $"{(double)_trickleSum / num:0.##}/game-hr vs +8.33/hr drift" : "rate pending");
Plugin.Log.LogInfo((object)($"Volunteer trickle: {_trickleCount} repair actions, " + $"~{_trickleSum:0.#} altruism worked off ({arg})."));
}
}
}
[HarmonyPatch(typeof(GUIRoster), "SetCrew")]
internal static class RosterLegendPatch
{
private const string LabelMarker = "txtVolunteerLegend";
private const string SwatchMarker = "bmpVolunteerLegend";
[HarmonyPostfix]
private static void Postfix(GUIRoster __instance)
{
//IL_0124: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: Unknown result type (might be due to invalid IL or missing references)
//IL_016c: Unknown result type (might be due to invalid IL or missing references)
//IL_0171: Unknown result type (might be due to invalid IL or missing references)
//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
try
{
if (!VolunteerShift.Registered)
{
return;
}
TMP_Text val = FindLabel(__instance, ShiftName(2));
if ((Object)(object)val == (Object)null)
{
Plugin.Log.LogWarning((object)("Roster legend: no Work label found. " + DescribeLabels(__instance)));
return;
}
Transform parent = val.transform.parent;
if ((Object)(object)parent == (Object)null || (Object)(object)parent.Find("txtVolunteerLegend") != (Object)null)
{
return;
}
int siblingIndex = val.transform.GetSiblingIndex();
Transform transform = val.transform;
RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
RectTransform val3 = SiblingSwatch(parent, siblingIndex);
if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null)
{
Plugin.Log.LogWarning((object)("Roster legend: Work label at index " + siblingIndex + " has no adjacent swatch; skipping."));
return;
}
float num = EntrySpacing(parent, siblingIndex);
if (num <= 0f)
{
Plugin.Log.LogWarning((object)"Roster legend: could not derive entry spacing; skipping.");
return;
}
string text = "Civic" + TrailingPunctuation(val.text);
float num2 = 0f;
RectTransform val4 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
if ((Object)(object)val4 != (Object)null)
{
Rect rect = val4.rect;
if (((Rect)(ref rect)).width > 0f)
{
float x = val.GetPreferredValues(val.text).x;
float x2 = val.GetPreferredValues(text).x;
float num3 = Mathf.Max(0f, x2 - x);
rect = val4.rect;
num2 = num3 / ((Rect)(ref rect)).width;
}
}
RectTransform obj = Clone(val2, parent, "txtVolunteerLegend", num);
RectTransform val5 = Clone(val3, parent, "bmpVolunteerLegend", num + num2);
TMP_Text component = ((Component)obj).GetComponent<TMP_Text>();
if ((Object)(object)component != (Object)null)
{
component.text = text;
component.enableAutoSizing = false;
component.textWrappingMode = (TextWrappingModes)0;
}
Image component2 = ((Component)val5).GetComponent<Image>();
if ((Object)(object)component2 != (Object)null)
{
((Graphic)component2).color = JsonCompanyRules.Shifts()[3].clr;
}
Plugin.Log.LogInfo((object)("Roster legend: added Civic entry (spacing " + num.ToString("0.####") + ", swatch nudge " + num2.ToString("0.####") + ")."));
}
catch (Exception ex)
{
Plugin.Log.LogError((object)("RosterLegendPatch failed (legend will lack Volunteer): " + ex));
}
}
private static RectTransform Clone(RectTransform src, Transform parent, string name, float step)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
RectTransform obj = Object.Instantiate<RectTransform>(src, parent);
((Object)obj).name = name;
Vector2 anchorMin = src.anchorMin;
Vector2 anchorMax = src.anchorMax;
obj.anchorMin = new Vector2(anchorMin.x + step, anchorMin.y);
obj.anchorMax = new Vector2(anchorMax.x + step, anchorMax.y);
obj.anchoredPosition = src.anchoredPosition;
obj.sizeDelta = src.sizeDelta;
return obj;
}
private static RectTransform? SiblingSwatch(Transform parent, int labelIndex)
{
if (labelIndex + 1 >= parent.childCount)
{
return null;
}
Transform child = parent.GetChild(labelIndex + 1);
if ((Object)(object)((Component)child).GetComponent<Image>() == (Object)null)
{
return null;
}
if ((Object)(object)((Component)child).GetComponent<TMP_Text>() != (Object)null)
{
return null;
}
return (RectTransform?)(object)((child is RectTransform) ? child : null);
}
private static float EntrySpacing(Transform parent, int labelIndex)
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
if (labelIndex - 2 < 0)
{
return 0f;
}
Transform child = parent.GetChild(labelIndex - 2);
RectTransform val = (RectTransform)(object)((child is RectTransform) ? child : null);
Transform child2 = parent.GetChild(labelIndex);
RectTransform val2 = (RectTransform)(object)((child2 is RectTransform) ? child2 : null);
if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
{
return 0f;
}
if ((Object)(object)((Component)val).GetComponent<TMP_Text>() == (Object)null)
{
return 0f;
}
return val2.anchorMin.x - val.anchorMin.x;
}
private static string ShiftName(int id)
{
Dictionary<int, JsonShift> dictionary = JsonCompanyRules.Shifts();
if (dictionary == null || !dictionary.TryGetValue(id, out var value))
{
return "Work";
}
return value.strName;
}
private static TMP_Text? FindLabel(GUIRoster roster, string wanted)
{
TMP_Text[] componentsInChildren = ((Component)roster).GetComponentsInChildren<TMP_Text>(true);
foreach (TMP_Text val in componentsInChildren)
{
if (!((Object)(object)val == (Object)null) && val.text != null && string.Equals(Normalise(val.text), wanted, StringComparison.OrdinalIgnoreCase) && !((Object)(object)((Component)val).GetComponentInParent<GUIRosterHour>() != (Object)null))
{
return val;
}
}
return null;
}
private static string Normalise(string s)
{
return s.Trim().TrimEnd(':', '-', ' ').Trim();
}
private static string TrailingPunctuation(string? template)
{
string text = ((template == null) ? string.Empty : template.TrimEnd(Array.Empty<char>()));
if (text.Length == 0)
{
return string.Empty;
}
char c = text[text.Length - 1];
if (c != ':' && c != '-')
{
return string.Empty;
}
return c.ToString();
}
private static string DescribeLabels(GUIRoster roster)
{
List<string> list = new List<string>();
TMP_Text[] componentsInChildren = ((Component)roster).GetComponentsInChildren<TMP_Text>(true);
foreach (TMP_Text val in componentsInChildren)
{
if (!((Object)(object)val == (Object)null) && val.text != null && !((Object)(object)((Component)val).GetComponentInParent<GUIRosterHour>() != (Object)null))
{
string text = val.text.Trim();
if (text.Length > 0 && text.Length < 24 && !list.Contains(text))
{
list.Add(text);
}
}
}
return "Labels present: " + string.Join(" | ", list.ToArray());
}
}
[HarmonyPatch(typeof(CondOwner), "ShiftChange")]
internal static class ShiftChangeLogPatch
{
private static readonly Dictionary<string, int> LastShift = new Dictionary<string, int>(StringComparer.Ordinal);
[HarmonyPostfix]
private static void Postfix(CondOwner __instance, JsonShift js)
{
try
{
if ((Object)(object)__instance == (Object)null || (!__instance.HasCond("IsPlayer") && !__instance.HasCond("IsPlayerCrew")))
{
return;
}
int num = js?.nID ?? (-1);
string key = __instance.strID ?? "?";
if (!LastShift.TryGetValue(key, out var value) || value != num)
{
bool flag = value == 3;
LastShift[key] = num;
Plugin.Log.LogInfo((object)(string.Format("Shift -> {0} (id {1}) for {2}", js?.strName ?? "None", num, __instance.strNameFriendly) + ((num == 3) ? " [volunteer conditions applied]" : "")));
if (num == 3)
{
VolunteerShift.VerifyDataPack();
RepairPayoutPatch.OnVolunteerShiftStart();
}
else if (flag)
{
RepairPayoutPatch.OnVolunteerShiftEnd(__instance.strNameFriendly);
}
}
}
catch (Exception arg)
{
Plugin.Log.LogError((object)$"ShiftChangeLogPatch failed: {arg}");
}
}
}
[HarmonyPatch(typeof(CondOwner), "GetMove2")]
internal static class VolunteerAIPatch
{
private static readonly Func<CondOwner, bool>? ProcessAutoTasks = AccessTools.MethodDelegate<Func<CondOwner, bool>>(AccessTools.Method(typeof(CondOwner), "ProcessAutoTasks", (Type[])null, (Type[])null), (object)null, true);
private static readonly Dictionary<string, string> LastState = new Dictionary<string, string>(StringComparer.Ordinal);
[HarmonyPrefix]
private static bool Prefix(CondOwner __instance)
{
try
{
if (ProcessAutoTasks == null)
{
return true;
}
if (!VolunteerShift.IsVolunteering(__instance))
{
return true;
}
bool flag = false;
string state;
if (__instance.HasCond("IsAIManual"))
{
state = "idle (AI set to manual)";
}
else if (!HasRestorePermission(__instance))
{
state = "idle (Restore permission off in roster)";
}
else
{
CrewSim.objInstance.workManager.IdleAdd(__instance);
flag = ProcessAutoTasks(__instance);
state = (flag ? "repairing" : "no repairable targets in reach");
}
LogTransition(__instance, state);
return !flag;
}
catch (Exception arg)
{
Plugin.Log.LogError((object)$"VolunteerAIPatch failed, falling back to free-will: {arg}");
return true;
}
}
private static bool HasRestorePermission(CondOwner co)
{
if (co.Company == null)
{
return true;
}
if (co.Company.mapRoster.TryGetValue(co.strID, out var value) && value != null)
{
return value.bRestorePermission;
}
return true;
}
private static void LogTransition(CondOwner co, string state)
{
string key = co.strID ?? "?";
if (!LastState.TryGetValue(key, out string value) || !(value == state))
{
LastState[key] = state;
Plugin.Log.LogInfo((object)("Volunteer AI [" + co.strNameFriendly + "] on " + (co.ship?.strRegID ?? "?") + ": " + state));
}
}
}
}