using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TerminalApi;
using TerminalApi.Classes;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("MaliciouslyCompliantQuotaCalculator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MaliciouslyCompliantQuotaCalculator")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("32ab3174-2628-412a-820e-f93f1f5640b9")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace MaliciouslyCompliantQuotaCalculator;
internal class View
{
private int start;
private int[] sequence;
public int this[int i]
{
get
{
return sequence[start + i];
}
set
{
sequence[start + i] = value;
}
}
public View(int[] sequence, int start)
{
this.sequence = sequence;
this.start = start;
}
}
[BepInPlugin("belea.mcqcm", "Maliciously Compliant Quota Calculator Mod", "1.0.0")]
public class MCQCModBase : BaseUnityPlugin
{
internal class MCQCMConfig
{
private readonly ConfigFile _configFile;
public ConfigEntry<int> OutputSpacing;
public ConfigEntry<bool> IncreasingOrder;
public MCQCMConfig(ConfigFile configFile)
{
_configFile = configFile;
}
public void RegisterOptions()
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Expected O, but got Unknown
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Expected O, but got Unknown
OutputSpacing = _configFile.Bind<int>("General", "OutputSpacing", 20, new ConfigDescription("Spacing between scrap name column and scrap value column.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(15, 30), Array.Empty<object>()));
IncreasingOrder = _configFile.Bind<bool>("General", "IncreasingOrder", true, new ConfigDescription("Ordering of items of the same type.", (AcceptableValueBase)null, Array.Empty<object>()));
}
}
private const string modGUID = "belea.mcqcm";
private const string modName = "Maliciously Compliant Quota Calculator Mod";
private const string modVersion = "1.0.0";
private readonly Harmony harmony = new Harmony("belea.mcqcm");
private static MCQCModBase Instance;
private const int formatSpacing = 27;
internal static MCQCMConfig Config;
internal ManualLogSource mls;
private void Awake()
{
//IL_0057: 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)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Expected O, but got Unknown
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
}
mls = Logger.CreateLogSource("belea.mcqcm");
Config = new MCQCMConfig(((BaseUnityPlugin)this).Config);
Config.RegisterOptions();
harmony.PatchAll(typeof(MCQCModBase));
TerminalApi.AddCommand("mcqc", new CommandInfo
{
Category = "other",
Description = "To calculate the optimal list of scrap to sell to fulfill quota.",
DisplayTextSupplier = OnCalculateCommand
}, "today", true);
mls.LogInfo((object)"The Maliciously Compliant Quota Calculator(TM) add-on has been installed...");
}
private int GetTotalValue(List<GrabbableObject> scrapList)
{
return scrapList.Sum((GrabbableObject scrap) => scrap.scrapValue);
}
private string ShortenItemName(string itemName, int space = 27)
{
if (itemName.Length < space - 2)
{
return itemName;
}
return itemName.Substring(0, space - 5) + "...";
}
private string AddDollarSign(int value)
{
return "$" + value;
}
private string SpaceOut(string left, string right, int space = 27)
{
left = ShortenItemName(left, space);
return left + new string(' ', space - left.Length) + right;
}
private string FormatOutput(List<GrabbableObject> scrapList, List<GrabbableObject> bestScrapList, int threshold, bool wantsToday = false)
{
StringBuilder stringBuilder = new StringBuilder();
if (wantsToday)
{
stringBuilder.AppendLine("Calculating for today's buying rate: " + Mathf.RoundToInt(StartOfRound.Instance.companyBuyingRate * 100f) + "%");
}
else
{
stringBuilder.AppendLine("Calculating for final day's buying rate: 100%");
}
stringBuilder.AppendLine();
int totalValue = GetTotalValue(scrapList);
int totalValue2 = GetTotalValue(bestScrapList);
if (bestScrapList.Count > 0)
{
stringBuilder.AppendLine(SpaceOut("Scrap name", "Value", Config.OutputSpacing.Value + totalValue2.ToString().Length - 4));
stringBuilder.AppendLine(new string('=', Config.OutputSpacing.Value + totalValue2.ToString().Length + 1));
}
foreach (GrabbableObject item in SortScrapList(bestScrapList))
{
stringBuilder.AppendLine(SpaceOut(item.itemProperties.itemName, AddDollarSign(item.scrapValue), Config.OutputSpacing.Value));
}
if (bestScrapList.Count == 0)
{
stringBuilder.Append("Quota cannot be fulfilled ");
if (wantsToday)
{
stringBuilder.Append("today ");
}
stringBuilder.AppendLine("with current scrap!");
}
else
{
stringBuilder.AppendLine();
stringBuilder.AppendLine(SpaceOut("Subtotal", AddDollarSign(totalValue2), Config.OutputSpacing.Value));
stringBuilder.AppendLine(new string('=', Config.OutputSpacing.Value + totalValue2.ToString().Length + 1));
}
stringBuilder.AppendLine();
stringBuilder.AppendLine(SpaceOut("Quota left", AddDollarSign(threshold)));
if (wantsToday)
{
stringBuilder.AppendLine(SpaceOut("In today's rate", AddDollarSign(ReadjustForCompanyRate(threshold, wantsToday))));
}
stringBuilder.AppendLine();
stringBuilder.AppendLine(SpaceOut("Total value on ship", AddDollarSign(totalValue)));
if (bestScrapList.Count > 0)
{
stringBuilder.AppendLine(SpaceOut("Total value after sale", AddDollarSign(totalValue - totalValue2)));
}
else
{
stringBuilder.AppendLine(SpaceOut("Total value needed", AddDollarSign(ReadjustForCompanyRate(threshold - AdjustForCompanyRate(totalValue, wantsToday), wantsToday))));
}
return stringBuilder.ToString();
}
private List<GrabbableObject> SortScrapList(List<GrabbableObject> scrapList)
{
scrapList.Sort((GrabbableObject left, GrabbableObject right) => (left.itemProperties.itemName == right.itemProperties.itemName) ? (left.scrapValue.CompareTo(right.scrapValue) * (Config.IncreasingOrder.Value ? 1 : (-1))) : left.itemProperties.itemName.CompareTo(right.itemProperties.itemName));
return scrapList;
}
private string OnCalculateCommand()
{
string terminalInput = TerminalApi.GetTerminalInput();
mls.LogInfo((object)("MCQC input: " + terminalInput));
List<GrabbableObject> scrapInShip = GetScrapInShip();
foreach (GrabbableObject item in scrapInShip)
{
mls.LogDebug((object)("MCQC Found scrap in ship: " + item.itemProperties.itemName + " - " + item.scrapValue + "..."));
}
bool wantsToday = terminalInput.ToLower().Split(new string[1] { " " }, StringSplitOptions.RemoveEmptyEntries).Contains("today");
int threshold = TimeOfDay.Instance.profitQuota - TimeOfDay.Instance.quotaFulfilled;
return FormatOutput(scrapInShip, Pisinger(scrapInShip, threshold, wantsToday), threshold, wantsToday);
}
private int AdjustForCompanyRate(int scrapValue, bool wantsToday)
{
if (!wantsToday)
{
return scrapValue;
}
return Mathf.FloorToInt((float)scrapValue * StartOfRound.Instance.companyBuyingRate);
}
private int ReadjustForCompanyRate(int scrapValue, bool wantsToday)
{
if (!wantsToday)
{
return scrapValue;
}
return Mathf.CeilToInt((float)scrapValue / StartOfRound.Instance.companyBuyingRate);
}
private List<GrabbableObject> Pisinger(List<GrabbableObject> scrapList, int threshold, bool wantsToday)
{
int totalValue = GetTotalValue(scrapList);
threshold = ReadjustForCompanyRate(AdjustForCompanyRate(totalValue, wantsToday) - threshold, wantsToday);
if (threshold < 0)
{
return new List<GrabbableObject>();
}
if (threshold == 0)
{
return scrapList;
}
int num = 0;
int num2 = 0;
foreach (GrabbableObject scrap in scrapList)
{
num += scrap.scrapValue;
num2 = Math.Max(num2, scrap.scrapValue);
}
int num3 = 0;
int i;
for (i = 0; num3 + scrapList[i].scrapValue <= threshold; i++)
{
num3 += scrapList[i].scrapValue;
}
int[][] array = new int[scrapList.Count - i + 1][];
for (int j = 0; j < array.Length; j++)
{
array[j] = new int[2 * num2];
for (int k = 0; k < array[j].Length; k++)
{
array[j][k] = 0;
}
}
View view = new View(array[0], num2 - 1);
for (int l = -num2 + 1; l <= 0; l++)
{
view[l] = -1;
}
for (int m = 1; m <= num2; m++)
{
view[m] = 0;
}
view[num3 - threshold] = i;
for (int n = i; n < scrapList.Count; n++)
{
View view2 = new View(array[n - i], num2 - 1);
View view3 = new View(array[n - i + 1], num2 - 1);
for (int num4 = -num2 + 1; num4 < num2 + 1; num4++)
{
view3[num4] = view2[num4];
}
for (int num5 = -num2 + 1; num5 <= 0; num5++)
{
int i2 = num5 + scrapList[n].scrapValue;
view3[i2] = Math.Max(view3[i2], view2[num5]);
}
for (int num6 = scrapList[n].scrapValue; num6 > 0; num6--)
{
for (int num7 = view3[num6] - 1; num7 > view2[num6] - 1; num7--)
{
int i3 = num6 - scrapList[num7].scrapValue;
view3[i3] = Math.Max(view3[i3], num7);
}
}
}
bool flag = false;
View view4 = new View(array[scrapList.Count - i], num2 - 1);
int num8;
for (num8 = 0; num8 >= -num2 + 1; num8--)
{
if (view4[num8] >= 0)
{
flag = true;
break;
}
}
if (flag)
{
bool[] array2 = new bool[scrapList.Count];
for (int num9 = 0; num9 < array2.Length; num9++)
{
array2[num9] = false;
}
for (int num10 = 0; num10 < i; num10++)
{
array2[num10] = true;
}
for (int num11 = scrapList.Count - 1; num11 > i - 1; num11--)
{
View view5 = new View(array[num11 - i + 1], num2 - 1);
View view6 = new View(array[num11 - i], num2 - 1);
int num13;
while (true)
{
int num12 = view5[num8];
num13 = num8 + scrapList[num12].scrapValue;
if (num13 > num2 || num12 >= view5[num13])
{
break;
}
num8 = num13;
array2[num12] = false;
}
num13 = num8 - scrapList[num11].scrapValue;
if (num13 >= -num2 + 1 && view6[num13] >= view5[num8])
{
num8 = num13;
array2[num11] = true;
}
}
List<GrabbableObject> list = new List<GrabbableObject>();
for (int num14 = 0; num14 < scrapList.Count; num14++)
{
if (!array2[num14])
{
list.Add(scrapList[num14]);
}
}
return list;
}
return new List<GrabbableObject>();
}
private List<GrabbableObject> GetScrapInShip()
{
List<GrabbableObject> list = new List<GrabbableObject>();
GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
foreach (GrabbableObject val in array)
{
if (val.isInShipRoom && val.itemProperties.isScrap && !(val is RagdollGrabbableObject))
{
list.Add(val);
}
}
return list;
}
}