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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using KroesTerminal.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("KroesTerminal")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("KroesTerminal")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("37420e05-2d4b-4fcd-a09f-844ba6ff45aa")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace KroesTerminal
{
internal class ConfigControl
{
private ConfigEntry<bool> QuotaUICfg;
private ConfigEntry<bool> QuotaNotifCfg;
private ConfigEntry<bool> KScanCfg;
private ConfigEntry<bool> KItemsCfg;
private ConfigEntry<bool> KEnemyCfg;
private ConfigEntry<bool> KEnemyPeacefulCfg;
private ConfigEntry<bool> KPlayersCfg;
private ConfigEntry<bool> InsideJokesCfg;
internal bool enableQuotaUI
{
get
{
return QuotaUICfg.Value;
}
set
{
QuotaUICfg.Value = value;
}
}
internal bool QuotaNotif
{
get
{
return QuotaNotifCfg.Value;
}
set
{
QuotaNotifCfg.Value = value;
}
}
internal bool KScan
{
get
{
return KScanCfg.Value;
}
set
{
KScanCfg.Value = value;
}
}
internal bool KItems
{
get
{
return KItemsCfg.Value;
}
set
{
KItemsCfg.Value = value;
}
}
internal bool KEnemy
{
get
{
return KEnemyCfg.Value;
}
set
{
KEnemyCfg.Value = value;
}
}
internal bool EnemyPeaceful
{
get
{
return KEnemyPeacefulCfg.Value;
}
set
{
KEnemyPeacefulCfg.Value = value;
}
}
internal bool KPlayers
{
get
{
return KPlayersCfg.Value;
}
set
{
KPlayersCfg.Value = value;
}
}
internal bool InsideJokes
{
get
{
return InsideJokesCfg.Value;
}
set
{
InsideJokesCfg.Value = value;
}
}
public ConfigControl(ConfigFile config)
{
QuotaUICfg = config.Bind<bool>("Features", "Enable Quota UI", true, (ConfigDescription)null);
QuotaNotifCfg = config.Bind<bool>("Features", "Enable Quota notification", true, (ConfigDescription)null);
KScanCfg = config.Bind<bool>("Terminal Commands", "Allow KSCAN command", true, (ConfigDescription)null);
KItemsCfg = config.Bind<bool>("Terminal Commands", "Allow KITEMS command", true, (ConfigDescription)null);
KEnemyCfg = config.Bind<bool>("Terminal Commands", "Allow KENEMY command", true, (ConfigDescription)null);
KEnemyPeacefulCfg = config.Bind<bool>("Terminal Commands", "List non-hostile enemies", true, (ConfigDescription)null);
KPlayersCfg = config.Bind<bool>("Terminal Commands", "Allow KPLAYERS command", true, (ConfigDescription)null);
InsideJokesCfg = config.Bind<bool>("Extra", "Enable Inside Jokes", false, "A part of the mod which enables inside jokes for my friends.");
}
}
[BepInPlugin("com.kroes.kroesterminal", "KroesTerminal", "1.0.10")]
public class KroesPlugin : BaseUnityPlugin
{
private const string GUID = "com.kroes.kroesterminal";
private const string NAME = "KroesTerminal";
private const string VERSION = "1.0.10";
internal static ManualLogSource Log;
internal static ConfigControl Configuration;
private Harmony harmony = new Harmony("com.kroes.kroesterminal");
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
Configuration = new ConfigControl(((BaseUnityPlugin)this).Config);
harmony.PatchAll(typeof(TerminalPatch));
if (Configuration.enableQuotaUI || Configuration.QuotaNotif)
{
harmony.PatchAll(typeof(HUDManagerPatch));
}
if (Configuration.InsideJokes)
{
harmony.PatchAll(typeof(EnemyAIPatch));
}
Log.LogInfo((object)"Loaded succesfully!");
}
}
internal class TextJumpAnimation : MonoBehaviour
{
private TextMeshProUGUI text;
private float jumpTime;
private const float jumpDuration = 0.2f;
private const float jumpScale = 1.5f;
private readonly Vector3 originalScale = new Vector3(1f, 1f, 1f);
public void StartJump(TextMeshProUGUI target)
{
text = target;
jumpTime = 0.2f;
}
private void Update()
{
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)text == (Object)null) && jumpTime > 0f)
{
float num = 1f - jumpTime / 0.2f;
float num2 = Mathf.Sin(num * (float)Math.PI) * 0.5f + 1f;
((TMP_Text)text).transform.localScale = originalScale * num2;
jumpTime -= Time.deltaTime;
if (jumpTime <= 0f)
{
((TMP_Text)text).transform.localScale = originalScale;
Object.Destroy((Object)(object)this);
}
}
}
}
internal class Utilities
{
public static TextMeshProUGUI QuotaText;
public static GameObject ship;
public static bool reachedQuota = false;
public static int totalShipLoot;
internal static readonly HashSet<string> nonHostileEnemies = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "docile locust bees", "manticoil", "tulip snake" };
internal static TerminalNode CreateTerminalNode(string name)
{
TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
val.displayText = "[" + name + "]";
val.terminalEvent = "";
val.clearPreviousText = true;
val.maxCharactersToType = 35;
val.buyItemIndex = -1;
val.buyVehicleIndex = -1;
val.isConfirmationNode = false;
val.buyRerouteToMoon = -1;
val.displayPlanetInfo = -1;
val.shipUnlockableID = -1;
val.buyUnlockable = false;
val.returnFromStorage = false;
val.itemCost = 0;
val.creatureFileID = -1;
val.creatureName = "";
val.storyLogFileID = -1;
val.overrideOptions = false;
val.acceptAnything = false;
val.terminalOptions = Array.Empty<CompatibleNoun>();
val.playSyncedClip = -1;
val.loadImageSlowly = false;
val.persistentImage = false;
return val;
}
internal static void OnSubmitEnd(Terminal terminal)
{
terminal.screenText.ActivateInputField();
((Selectable)terminal.screenText).Select();
}
internal static bool ShouldListItem(GrabbableObject item)
{
if (!item.itemProperties.isScrap)
{
return false;
}
StunGrenadeItem val = (StunGrenadeItem)(object)((item is StunGrenadeItem) ? item : null);
if (val != null)
{
return !val.hasExploded;
}
return true;
}
internal static string KScanDisplayText()
{
string text = "";
int num = 0;
GrabbableObject[] source = Object.FindObjectsOfType<GrabbableObject>();
GrabbableObject[] array = (from obj in source
where ShouldListItem(obj) && !obj.isInShipRoom && !obj.isInElevator
orderby obj.itemProperties.itemName
select obj).ToArray();
if (array.Length == 0)
{
return "\n\n\nNo objects were found.\n\n";
}
GrabbableObject[] array2 = array;
foreach (GrabbableObject val in array2)
{
text += $"\n* {val.itemProperties.itemName} // ${val.scrapValue}";
num += val.scrapValue;
}
return $"\n\n\nThere are {array.Length} objects outside the ship, totalling a value of ${num}.\n{text}\n\n";
}
internal static string KItemsDisplayText()
{
int num = 0;
int num2 = 0;
int num3 = 0;
int num4 = 0;
int num5 = 0;
int num6 = 0;
int num7 = 0;
int num8 = 0;
GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
GrabbableObject[] array2 = array;
foreach (GrabbableObject val in array2)
{
if (ShouldListItem(val))
{
if (val.isInShipRoom)
{
num3++;
num4 += val.scrapValue;
}
else if (val.isHeld)
{
num7++;
num8 += val.scrapValue;
}
else if (val.isInElevator)
{
num5++;
num6 += val.scrapValue;
}
else
{
num++;
num2 += val.scrapValue;
}
}
}
return $"\n\n\n[Items on moon] {num} : ${num2}\n[Items in ship] {num3} : ${num4}\n[Items being held] {num7} : ${num8}\n[Items on elevator/ship edge] {num5} : ${num6}\n\n";
}
private static bool ShouldListEnemy(EnemyAI enemy)
{
if (enemy.isEnemyDead)
{
return false;
}
if (!KroesPlugin.Configuration.EnemyPeaceful && nonHostileEnemies.Contains(enemy.enemyType.enemyName))
{
return false;
}
return true;
}
internal static string KEnemyDisplayText()
{
string text = "";
string text2 = "";
EnemyAI[] source = Object.FindObjectsOfType<EnemyAI>();
EnemyAI[] array = (from enemy in source
where !enemy.isOutside && ShouldListEnemy(enemy)
orderby enemy.enemyType.enemyName
select enemy).ToArray();
EnemyAI[] array2 = (from enemy in source
where enemy.isOutside && ShouldListEnemy(enemy)
orderby enemy.enemyType.enemyName
select enemy).ToArray();
if (array.Length == 0 && array2.Length == 0)
{
return "\n\n\nNo enemies were found.\n\n";
}
EnemyAI[] array3 = array;
foreach (EnemyAI val in array3)
{
string text3 = "???";
DressGirlAI val2 = (DressGirlAI)(object)((val is DressGirlAI) ? val : null);
text3 = ((val2 == null) ? val.enemyType.enemyPrefab.GetComponentInChildren<ScanNodeProperties>()?.headerText : ((Object)val2).name);
text += $"\n* {text3} : {val.enemyHP}HP";
}
EnemyAI[] array4 = array2;
foreach (EnemyAI val3 in array4)
{
string text4 = "???";
DressGirlAI val4 = (DressGirlAI)(object)((val3 is DressGirlAI) ? val3 : null);
text4 = ((val4 == null) ? val3.enemyType.enemyPrefab.GetComponentInChildren<ScanNodeProperties>()?.headerText : ((Object)val4).name);
text2 += $"\n* {text4} : {val3.enemyHP}HP";
}
int num = array.Length + array2.Length;
string text5 = $"\n\n\nThere are {num} enemies out there.\n";
if (array.Length != 0)
{
text5 = text5 + "\n[Inside]" + text + "\n";
}
if (array2.Length != 0)
{
text5 = text5 + "\n[Outside]" + text2 + "\n";
}
return text5 + "\n";
}
internal static int calculateShipLootTotal()
{
List<GrabbableObject> source = (from obj in ship.GetComponentsInChildren<GrabbableObject>()
where obj.itemProperties.isScrap && !(obj is RagdollGrabbableObject)
select obj).ToList();
return source.Sum((GrabbableObject scrap) => scrap.scrapValue);
}
public static void TriggerJump()
{
TextMeshProUGUI quotaText = QuotaText;
if (!((Object)(object)quotaText == (Object)null))
{
TextJumpAnimation textJumpAnimation = ((Component)quotaText).gameObject.AddComponent<TextJumpAnimation>();
textJumpAnimation.StartJump(quotaText);
}
}
internal static string ConfigPermissionDisplayText(string command)
{
return "\n\n\n[" + command + "] is disabled in the mod config file.\n\n";
}
internal static string KroesDisplayText()
{
string text = "\n\n\nThanks for using KroesTerminal!\n\n";
if (KroesPlugin.Configuration.KScan)
{
text += ">KSCAN\nTo see a detailed scan of all scrap.\n\n";
}
if (KroesPlugin.Configuration.KItems)
{
text += ">KITEMS\nTo see a count of all scrap.\n\n";
}
if (KroesPlugin.Configuration.KEnemy)
{
text += ">KENEMY\nTo see a scan of all enemies.\n\n";
}
if (KroesPlugin.Configuration.KPlayers)
{
text += ">KPLAYERS\nTo see a list of all player items.\n\n";
}
return text;
}
internal static string PlayersDisplayText()
{
PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
PlayerControllerB[] array = allPlayerScripts.Where((PlayerControllerB player) => player.isPlayerControlled && !player.isPlayerDead).ToArray();
int num = array.Length;
string text = $"\n\n\nThere are {num} players alive:\n";
PlayerControllerB[] array2 = array;
foreach (PlayerControllerB val in array2)
{
text = text + "\n[" + val.playerUsername + "]\n";
GrabbableObject[] itemSlots = val.ItemSlots;
GrabbableObject[] array3 = itemSlots;
foreach (GrabbableObject val2 in array3)
{
if ((Object)(object)val2 != (Object)null)
{
text = text + "- " + val2.itemProperties.itemName + "\n";
}
}
}
return text + "\n";
}
}
}
namespace KroesTerminal.Patches
{
[HarmonyPatch(typeof(EnemyAI))]
internal class EnemyAIPatch
{
[HarmonyPostfix]
[HarmonyPatch("Start")]
private static void Postfix(EnemyAI __instance)
{
ScanNodeProperties componentInChildren = __instance.enemyType.enemyPrefab.GetComponentInChildren<ScanNodeProperties>();
if (__instance is FlowermanAI)
{
componentInChildren.headerText = "Mammoet Mevrouw";
}
if (__instance is ClaySurgeonAI)
{
componentInChildren.headerText = "Fes de Barber";
}
}
}
[HarmonyPatch(typeof(HUDManager))]
internal class HUDManagerPatch
{
[HarmonyPatch("Awake")]
[HarmonyPostfix]
private static void AwakePostfix(HUDManager __instance)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Expected O, but got Unknown
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_012b: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
if (KroesPlugin.Configuration.enableQuotaUI)
{
GameObject val = new GameObject("myHotbarUI");
val.transform.SetParent(__instance.HUDContainer.transform, false);
RectTransform val2 = val.AddComponent<RectTransform>();
val2.anchorMin = new Vector2(0f, 1f);
val2.anchorMax = new Vector2(0f, 1f);
val2.pivot = new Vector2(0f, 1f);
val2.anchoredPosition = new Vector2(60f, -10f);
GameObject val3 = new GameObject("QuotaText");
val3.transform.SetParent(val.transform, false);
TextMeshProUGUI val4 = val3.AddComponent<TextMeshProUGUI>();
RectTransform component = val3.GetComponent<RectTransform>();
component.anchorMin = new Vector2(0.5f, 0.5f);
component.anchorMax = new Vector2(0.5f, 0.5f);
component.pivot = new Vector2(0.5f, 0.5f);
component.anchoredPosition = new Vector2(50f, 20f);
component.sizeDelta = new Vector2(300f, 50f);
((TMP_Text)val4).font = ((TMP_Text)__instance.controlTipLines[0]).font;
((TMP_Text)val4).fontSize = 20f;
((TMP_Text)val4).fontStyle = (FontStyles)1;
((TMP_Text)val4).text = "-placeholder-";
((Graphic)val4).color = Color.white;
((TMP_Text)val4).alignment = (TextAlignmentOptions)513;
((TMP_Text)val4).enableWordWrapping = true;
((TMP_Text)val4).overflowMode = (TextOverflowModes)0;
Utilities.QuotaText = val4;
}
Utilities.ship = GameObject.Find("/Environment/HangarShip");
}
[HarmonyPatch("Update")]
[HarmonyPostfix]
private static void UpdatePostfix(HUDManager __instance)
{
int num = Utilities.calculateShipLootTotal();
int profitQuota = TimeOfDay.Instance.profitQuota;
if (KroesPlugin.Configuration.enableQuotaUI)
{
if (num != Utilities.totalShipLoot)
{
Utilities.totalShipLoot = num;
Utilities.TriggerJump();
}
((TMP_Text)Utilities.QuotaText).text = $"[QUOTA] {Utilities.totalShipLoot} : {profitQuota}";
}
if (KroesPlugin.Configuration.QuotaNotif)
{
if (!Utilities.reachedQuota && num >= profitQuota)
{
Utilities.reachedQuota = true;
KroesPlugin.Log.LogInfo((object)"sending quota notif...");
__instance.globalNotificationAnimator.SetTrigger("TriggerNotif");
((TMP_Text)__instance.globalNotificationText).text = "Quota has been reached!";
__instance.UIAudio.PlayOneShot(__instance.globalNotificationSFX);
}
else if (Utilities.reachedQuota && num < profitQuota)
{
Utilities.reachedQuota = false;
}
}
}
}
[HarmonyPatch(typeof(Terminal))]
internal class TerminalPatch
{
[HarmonyPatch("OnSubmit")]
[HarmonyPrefix]
private static bool OnSubmitPrefix(Terminal __instance)
{
string text = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded);
switch (text.ToLower())
{
case "kscan":
{
KroesPlugin.Log.LogInfo((object)"Custom Command: kscan");
TerminalNode val5 = Utilities.CreateTerminalNode("kscan");
__instance.LoadNewNode(val5);
return false;
}
case "kitems":
{
KroesPlugin.Log.LogInfo((object)"Custom Command: kitems");
TerminalNode val4 = Utilities.CreateTerminalNode("kitems");
__instance.LoadNewNode(val4);
return false;
}
case "kenemy":
{
KroesPlugin.Log.LogInfo((object)"Custom Command: kenemy");
TerminalNode val3 = Utilities.CreateTerminalNode("kenemy");
__instance.LoadNewNode(val3);
return false;
}
case "kroes":
{
KroesPlugin.Log.LogInfo((object)"Custom Command: kroes");
TerminalNode val2 = Utilities.CreateTerminalNode("kroes");
__instance.LoadNewNode(val2);
return false;
}
case "kplayers":
{
KroesPlugin.Log.LogInfo((object)"Custom Command: kplayers");
TerminalNode val = Utilities.CreateTerminalNode("kplayers");
__instance.LoadNewNode(val);
return false;
}
default:
return true;
}
}
[HarmonyPatch("TextPostProcess")]
[HarmonyPrefix]
private static bool TextPostProcessPrefix(ref string modifiedDisplayText, TerminalNode node, ref string __result, Terminal __instance)
{
string text = modifiedDisplayText.Trim();
if (text.Contains("[kscan]"))
{
if (!KroesPlugin.Configuration.KScan)
{
KroesPlugin.Log.LogInfo((object)"[kscan] not permitted.");
__result = Utilities.ConfigPermissionDisplayText("kscan");
Utilities.OnSubmitEnd(__instance);
return false;
}
KroesPlugin.Log.LogInfo((object)"Processing [kscan]...");
__result = Utilities.KScanDisplayText();
Utilities.OnSubmitEnd(__instance);
return false;
}
if (text.Contains("[kitems]"))
{
if (!KroesPlugin.Configuration.KItems)
{
KroesPlugin.Log.LogInfo((object)"[kitems] not permitted.");
__result = Utilities.ConfigPermissionDisplayText("kitems");
Utilities.OnSubmitEnd(__instance);
return false;
}
KroesPlugin.Log.LogInfo((object)"Processing [kitems]...");
__result = Utilities.KItemsDisplayText();
Utilities.OnSubmitEnd(__instance);
return false;
}
if (text.Contains("[kenemy]"))
{
if (!KroesPlugin.Configuration.KEnemy)
{
KroesPlugin.Log.LogInfo((object)"[kenemy] not permitted.");
__result = Utilities.ConfigPermissionDisplayText("kenemy");
Utilities.OnSubmitEnd(__instance);
return false;
}
KroesPlugin.Log.LogInfo((object)"Processing [kenemy]...");
__result = Utilities.KEnemyDisplayText();
Utilities.OnSubmitEnd(__instance);
return false;
}
if (text.Contains("[kroes]"))
{
KroesPlugin.Log.LogInfo((object)"Processing [kroes]...");
__result = Utilities.KroesDisplayText();
Utilities.OnSubmitEnd(__instance);
return false;
}
if (text.Contains("[kplayers]"))
{
if (!KroesPlugin.Configuration.KPlayers)
{
KroesPlugin.Log.LogInfo((object)"[kplayers] not permitted.");
__result = Utilities.ConfigPermissionDisplayText("kplayers");
Utilities.OnSubmitEnd(__instance);
return false;
}
KroesPlugin.Log.LogInfo((object)"Processing [kplayers]...");
__result = Utilities.PlayersDisplayText();
Utilities.OnSubmitEnd(__instance);
return false;
}
return true;
}
}
}