using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.Json;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using SOD.Common;
using SOD.Common.BepInEx;
using SOD.Common.BepInEx.Configuration;
using SOD.Common.Helpers;
using SideStreet.Core;
using SideStreet.Patches.BoardPatches;
using SideStreet.Patches.SideJobPatches;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SideStreet")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c0fd6cd84d138b50117fbaadbe4984e3a2e93716")]
[assembly: AssemblyProduct("Side job thematic coherence")]
[assembly: AssemblyTitle("SideStreet")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 SideStreet
{
public interface IPluginBindings : IPatchToggleBindings
{
}
public interface IPatchToggleBindings
{
[Binding(true, "Post arrest warrants only at City Hall", "_PatchEnable.ArrestWarrantsAtCityHall")]
bool PatchEnableArrestWarrantsAtCityHall { get; set; }
[Binding(true, "Keep apartment listings off the City Hall board", "_PatchEnable.ApartmentListingsOffCityHallBoard")]
bool PatchEnableApartmentListingsOffCityHallBoard { get; set; }
[Binding(true, "Scale arrest warrant count to city population", "_PatchEnable.ArrestWarrantQuota")]
bool PatchEnableArrestWarrantQuota { get; set; }
}
[BepInPlugin("Sclamy.SideStreet", "SideStreet", "0.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : PluginController<Plugin, IPluginBindings>
{
public const string PLUGIN_GUID = "Sclamy.SideStreet";
public const string PLUGIN_NAME = "SideStreet";
public const string PLUGIN_VERSION = "0.1.0";
public override void Load()
{
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Expected O, but got Unknown
PatchIfEnabled(typeof(ArrestWarrantsAtCityHall), base.Config.PatchEnableArrestWarrantsAtCityHall);
PatchIfEnabled(typeof(ApartmentListingsOffCityHallBoard), base.Config.PatchEnableApartmentListingsOffCityHallBoard);
PatchIfEnabled(typeof(ArrestWarrantQuota), base.Config.PatchEnableArrestWarrantQuota);
SideStreetSaveData.Enable();
ArrestWarrantRefresh.Enable();
ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(20, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Plugin ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Sclamy.SideStreet");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.0");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" is loaded!");
}
log.LogInfo(val);
}
private void PatchIfEnabled(Type patchType, bool enabled)
{
if (enabled)
{
base.Harmony.CreateClassProcessor(patchType).Patch();
Type[] nestedTypes = patchType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
foreach (Type type in nestedTypes)
{
base.Harmony.CreateClassProcessor(type).Patch();
}
}
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "SideStreet";
public const string PLUGIN_NAME = "Side job thematic coherence";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace SideStreet.Patches.SideJobPatches
{
internal static class ArrestWarrantQuota
{
[HarmonyPatch(typeof(SideJobController), "JobCreationCheck")]
internal static class ChooseTier
{
[HarmonyPrefix]
private static void Prefix(SideJobController __instance)
{
chosenTier = null;
cullTier = null;
if ((Object)(object)__instance == (Object)null || __instance.jobTracking == null)
{
return;
}
int num = ArrestWarrants.CountPosted(__instance);
int num2 = ArrestWarrants.DesiredTotal();
if (num > num2)
{
cullTier = FirstStandingTier(__instance);
}
else
{
if (num >= num2 || JobBoards.FreePostingSlots(FirstArrestPosting(__instance)) <= 0)
{
return;
}
List<JobPreset> list = new List<JobPreset>();
for (int i = 0; i < __instance.jobTracking.Count; i++)
{
JobTracking val = __instance.jobTracking[i];
if (val != null && ArrestWarrants.IsArrestWarrant(val.preset) && val.activeJobs.Count <= 0 && VanillaFrequency(val.preset) > 0)
{
list.Add(val.preset);
}
}
if (list.Count > 0)
{
chosenTier = list[Toolbox.Instance.Rand(0, list.Count, false)];
}
}
}
private static InteractablePreset FirstArrestPosting(SideJobController controller)
{
for (int i = 0; i < controller.jobTracking.Count; i++)
{
JobTracking val = controller.jobTracking[i];
if (val != null && ArrestWarrants.IsArrestWarrant(val.preset))
{
return val.preset.jobPosting;
}
}
return null;
}
private static JobPreset FirstStandingTier(SideJobController controller)
{
for (int i = 0; i < controller.jobTracking.Count; i++)
{
JobTracking val = controller.jobTracking[i];
if (val != null && ArrestWarrants.IsArrestWarrant(val.preset) && val.activeJobs.Count > 0)
{
return val.preset;
}
}
return null;
}
private static int VanillaFrequency(JobPreset preset)
{
ApplyTier.readingVanilla = true;
try
{
return preset.GetFrequencyForSocialCreditLevel();
}
finally
{
ApplyTier.readingVanilla = false;
}
}
}
[HarmonyPatch(typeof(JobPreset), "GetFrequencyForSocialCreditLevel")]
internal static class ApplyTier
{
internal static bool readingVanilla;
[HarmonyPostfix]
private static void Postfix(JobPreset __instance, ref int __result)
{
if (readingVanilla || !ArrestWarrants.IsArrestWarrant(__instance))
{
return;
}
SideJobController instance = SideJobController.Instance;
if ((Object)(object)instance == (Object)null || instance.jobTracking == null)
{
return;
}
int num = 0;
for (int i = 0; i < instance.jobTracking.Count; i++)
{
JobTracking val = instance.jobTracking[i];
if (val != null && !((SoCustomComparison)(object)val.preset != (SoCustomComparison)(object)__instance))
{
num = val.activeJobs.Count;
break;
}
}
if ((SoCustomComparison)(object)__instance == (SoCustomComparison)(object)chosenTier)
{
__result = num + 1;
}
else if ((SoCustomComparison)(object)__instance == (SoCustomComparison)(object)cullTier)
{
__result = num - 1;
}
else
{
__result = num;
}
}
}
private static JobPreset chosenTier;
private static JobPreset cullTier;
}
[HarmonyPatch(typeof(SideJob), "PostJob")]
internal static class ArrestWarrantsAtCityHall
{
[HarmonyPrefix]
private static void Prefix(SideJob __instance, ref List<FurnitureLocation> __state)
{
__state = null;
if (__instance != null)
{
bool flag = ArrestWarrants.IsArrestWarrant(__instance.preset);
__state = JobBoards.Restrict(flag);
if (__state == null && flag)
{
PluginController<Plugin, IPluginBindings>.Log.LogError((object)"No City Hall job board found - falling back to vanilla placement for this warrant");
}
}
}
[HarmonyFinalizer]
private static void Finalizer(ref List<FurnitureLocation> __state)
{
JobBoards.Restore(__state);
__state = null;
}
}
}
namespace SideStreet.Patches.BoardPatches
{
[HarmonyPatch(typeof(CitizenBehaviour), "UpdateForSale")]
internal static class ApartmentListingsOffCityHallBoard
{
[HarmonyPrefix]
private static void Prefix(ref List<FurnitureLocation> __state)
{
EvictExistingListings();
__state = JobBoards.Restrict(cityHall: false);
}
[HarmonyFinalizer]
private static void Finalizer(ref List<FurnitureLocation> __state)
{
JobBoards.Restore(__state);
__state = null;
}
private static void EvictExistingListings()
{
GameplayController instance = GameplayController.Instance;
if ((Object)(object)instance == (Object)null || instance.forSale == null)
{
return;
}
for (int i = 0; i < instance.forSale.Count; i++)
{
NewAddress val = instance.forSale[i];
if (!((Object)(object)val == (Object)null) && val.saleNote != null && JobBoards.IsCityHall(val.saleNote.furnitureParent))
{
val.saleNote.Delete();
val.saleNote = null;
}
}
}
}
}
namespace SideStreet.Core
{
internal static class ArrestWarrantRefresh
{
private const float HoursBetweenRetirements = 6f;
internal static void Enable()
{
Lib.Time.OnHourChanged += delegate
{
CheckForRetirement();
};
}
private static void CheckForRetirement()
{
float gameTime = SessionData.Instance.gameTime;
SaveState state = SideStreetSaveData.State;
if (!(gameTime - state.LastWarrantRetirement < 6f) && RetireOneWarrant())
{
state.LastWarrantRetirement = gameTime;
}
}
private static bool RetireOneWarrant()
{
//IL_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Expected O, but got Unknown
SideJobController instance = SideJobController.Instance;
if ((Object)(object)instance == (Object)null || instance.jobTracking == null)
{
return false;
}
List<SideJob> list = new List<SideJob>();
List<SideJob> list2 = new List<SideJob>();
for (int i = 0; i < instance.jobTracking.Count; i++)
{
JobTracking val = instance.jobTracking[i];
if (val == null || !ArrestWarrants.IsArrestWarrant(val.preset))
{
continue;
}
for (int j = 0; j < val.activeJobs.Count; j++)
{
SideJob val2 = val.activeJobs[j];
if (val2 != null && !val2.accepted)
{
if (val2.post == null)
{
list2.Add(val2);
}
else if (CanRetire(val2))
{
list.Add(val2);
}
}
}
}
foreach (SideJob item in list2)
{
item.End();
}
if (list2.Count > 0)
{
ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
bool flag = default(bool);
BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(44, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Cleared ");
((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<int>(list2.Count);
((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" warrant(s) that could not be placed");
}
log.LogWarning(val3);
}
if (list.Count <= 0)
{
return false;
}
list[Toolbox.Instance.Rand(0, list.Count, false)].End();
return true;
}
private static bool CanRetire(SideJob job)
{
if (job.post.rPl || job.post.node == null)
{
return false;
}
Player instance = Player.Instance;
if ((Object)(object)instance == (Object)null || (Object)(object)((Actor)instance).currentGameLocation == (Object)null)
{
return false;
}
return (Object)(object)job.post.node.gameLocation != (Object)(object)((Actor)instance).currentGameLocation;
}
}
internal static class ArrestWarrants
{
private const string ArrestCaseName = "arrest";
private const int TwoWarrantsBelow = 250;
private const int ThreeWarrantsBelow = 400;
private const int FourWarrantsBelow = 525;
internal static bool IsArrestWarrant(JobPreset preset)
{
if ((SoCustomComparison)(object)preset == (SoCustomComparison)null)
{
return false;
}
string caseName = preset.caseName;
return !string.IsNullOrEmpty(caseName) && caseName.Equals("arrest", StringComparison.OrdinalIgnoreCase);
}
internal static int DesiredTotal()
{
CityData instance = CityData.Instance;
if ((Object)(object)instance == (Object)null || instance.citizenDirectory == null)
{
return 2;
}
int count = instance.citizenDirectory.Count;
if (count < 250)
{
return 2;
}
if (count < 400)
{
return 3;
}
if (count < 525)
{
return 4;
}
return 5;
}
internal static int CountPosted(SideJobController controller)
{
if ((Object)(object)controller == (Object)null || controller.jobTracking == null)
{
return 0;
}
int num = 0;
for (int i = 0; i < controller.jobTracking.Count; i++)
{
JobTracking val = controller.jobTracking[i];
if (val == null || !IsArrestWarrant(val.preset))
{
continue;
}
for (int j = 0; j < val.activeJobs.Count; j++)
{
SideJob val2 = val.activeJobs[j];
if (val2 != null && val2.post != null)
{
num++;
}
}
}
return num;
}
}
internal static class JobBoards
{
internal static bool IsCityHall(FurnitureLocation board)
{
if (board == null || board.anchorNode == null)
{
return false;
}
NewGameLocation gameLocation = board.anchorNode.gameLocation;
if ((Object)(object)gameLocation == (Object)null || (Object)(object)gameLocation.building == (Object)null)
{
return false;
}
BuildingPreset preset = gameLocation.building.preset;
RoutineControls instance = RoutineControls.Instance;
if ((SoCustomComparison)(object)preset == (SoCustomComparison)null || (Object)(object)instance == (Object)null || (SoCustomComparison)(object)instance.cityHall == (SoCustomComparison)null)
{
return false;
}
return ((SoCustomComparison)preset).presetName == ((SoCustomComparison)instance.cityHall).presetName;
}
internal static List<FurnitureLocation> Collect(bool cityHall)
{
List<FurnitureLocation> val = new List<FurnitureLocation>();
CityData instance = CityData.Instance;
if ((Object)(object)instance == (Object)null || instance.jobBoardsDirectory == null)
{
return val;
}
List<FurnitureLocation> jobBoardsDirectory = instance.jobBoardsDirectory;
for (int i = 0; i < jobBoardsDirectory.Count; i++)
{
FurnitureLocation val2 = jobBoardsDirectory[i];
if (IsCityHall(val2) == cityHall)
{
val.Add(val2);
}
}
return val;
}
internal static List<FurnitureLocation> Restrict(bool cityHall)
{
CityData instance = CityData.Instance;
if ((Object)(object)instance == (Object)null)
{
return null;
}
List<FurnitureLocation> jobBoardsDirectory = instance.jobBoardsDirectory;
if (jobBoardsDirectory == null || jobBoardsDirectory.Count <= 0)
{
return null;
}
List<FurnitureLocation> val = Collect(cityHall);
if (val.Count <= 0)
{
return null;
}
instance.jobBoardsDirectory = val;
return jobBoardsDirectory;
}
internal static int FreePostingSlots(InteractablePreset posting)
{
if ((SoCustomComparison)(object)posting == (SoCustomComparison)null || posting.subObjectClasses == null)
{
return 0;
}
int num = 0;
List<FurnitureLocation> val = Collect(cityHall: true);
for (int i = 0; i < val.Count; i++)
{
FurnitureLocation val2 = val[i];
object obj;
if (val2 == null)
{
obj = null;
}
else
{
FurniturePreset furniture = val2.furniture;
obj = ((furniture != null) ? furniture.subObjects : null);
}
if (obj == null || val2.spawnedInteractables == null)
{
continue;
}
for (int j = 0; j < val2.furniture.subObjects.Count; j++)
{
SubObject val3 = val2.furniture.subObjects[j];
if (val3 == null || !posting.subObjectClasses.Contains(val3.preset))
{
continue;
}
bool flag = false;
for (int k = 0; k < val2.spawnedInteractables.Count; k++)
{
Interactable val4 = val2.spawnedInteractables[k];
if (val4 != null && val4.subObject == val3)
{
flag = true;
break;
}
}
if (!flag)
{
num++;
}
}
}
return num;
}
internal static void Restore(List<FurnitureLocation> original)
{
if (original != null)
{
CityData instance = CityData.Instance;
if ((Object)(object)instance != (Object)null)
{
instance.jobBoardsDirectory = original;
}
}
}
}
internal class SaveState
{
public float LastWarrantRetirement { get; set; }
}
internal static class SideStreetSaveData
{
private const string FileName = "sidestreet.json";
internal static SaveState State { get; private set; } = new SaveState();
internal static void Enable()
{
Lib.SaveGame.OnAfterSave += OnSave;
Lib.SaveGame.OnAfterLoad += OnLoad;
Lib.SaveGame.OnAfterNewGame += delegate
{
State = new SaveState();
};
}
private static void OnSave(object sender, SaveGameArgs args)
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
string saveGameDataPath = Lib.SaveGame.GetSaveGameDataPath(args, "sidestreet.json");
try
{
File.WriteAllText(saveGameDataPath, JsonSerializer.Serialize(State));
}
catch (IOException ex)
{
ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
bool flag = default(bool);
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(18, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not write ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("sidestreet.json");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
}
log.LogWarning(val);
}
}
private static void OnLoad(object sender, SaveGameArgs args)
{
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Expected O, but got Unknown
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Expected O, but got Unknown
string saveGameDataPath = Lib.SaveGame.GetSaveGameDataPath(args, "sidestreet.json");
bool flag = default(bool);
try
{
if (File.Exists(saveGameDataPath))
{
State = JsonSerializer.Deserialize<SaveState>(File.ReadAllText(saveGameDataPath)) ?? new SaveState();
return;
}
}
catch (JsonException ex)
{
ManualLogSource log = PluginController<Plugin, IPluginBindings>.Log;
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(33, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not read ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("sidestreet.json");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", starting fresh: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
}
log.LogWarning(val);
}
catch (IOException ex2)
{
ManualLogSource log2 = PluginController<Plugin, IPluginBindings>.Log;
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(33, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not read ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("sidestreet.json");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", starting fresh: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex2.Message);
}
log2.LogWarning(val);
}
State = new SaveState
{
LastWarrantRetirement = SessionData.Instance.gameTime
};
}
}
}