using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using SpongePEAK.Core.Game;
using SpongePEAK.Core.Logging;
using SpongePEAK.Core.Patching;
using SpongePEAK.Core.Reflection;
using SpongePEAK.Lib.Events;
using SpongePEAK.Lib.Features;
using SpongePEAK.Lib.Items;
using SpongePEAK.Lib.Net;
using SpongePEAK.Lib.Plugin;
using SpongePEAK.Lib.UI;
using SpongePEAK.QOL.Features;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("sponge")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) sponge")]
[assembly: AssemblyDescription("Quality-of-life improvements, each individually toggleable.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ddcbf023fcb4c2065ad9b038910633cd4a3a93b0")]
[assembly: AssemblyProduct("SpongePEAKQOL")]
[assembly: AssemblyTitle("com.sponge.peakqol")]
[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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace SpongePEAK.QOL
{
[BepInPlugin("com.sponge.peakqol", "SpongePEAKQOL", "1.0.0")]
public sealed class QolPlugin : SpongePlugin
{
public const string Guid = "com.sponge.peakqol";
private const string RulesKey = "sponge.qolrules";
private FeatureRegistry _registry;
private GameEvents _events;
private HostRuleSet _rules;
private PushFeature _push;
private RopeCutFeature _ropeCut;
private ItemInfoFeature _itemInfo;
private MushroomInfoFeature _mushroomInfo;
private TeammateStatsFeature _teammateStats;
private CatchPlayersFeature _catchPlayers;
private BackpacksForEveryoneFeature _backpacks;
protected override string DisplayName => "SpongePEAKQOL";
protected override void OnLoad()
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Expected O, but got Unknown
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Expected O, but got Unknown
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
_registry = new FeatureRegistry(((BaseUnityPlugin)this).Config, ((SpongePlugin)this).Log);
_events = new GameEvents(((SpongePlugin)this).Log);
_rules = new HostRuleSet("sponge.qolrules", ((SpongePlugin)this).Log);
_rules.Declare("AllowRopeSwinging", true);
_rules.Declare("AllowPushing", true);
_rules.Declare("AllowRopeCutting", true);
RopeSwingFeature.Rules = _rules;
PushFeature.Rules = _rules;
RopeCutFeature.Rules = _rules;
LastChanceFeature lastChanceFeature = new LastChanceFeature();
lastChanceFeature.Configure(((BaseUnityPlugin)this).Config);
_itemInfo = new ItemInfoFeature();
_itemInfo.Configure(((BaseUnityPlugin)this).Config);
_mushroomInfo = new MushroomInfoFeature();
_mushroomInfo.Configure(((BaseUnityPlugin)this).Config);
_teammateStats = new TeammateStatsFeature();
_teammateStats.Configure(((BaseUnityPlugin)this).Config);
RopeSwingFeature ropeSwingFeature = new RopeSwingFeature();
ropeSwingFeature.Configure(((BaseUnityPlugin)this).Config);
_push = new PushFeature();
_push.Configure(((BaseUnityPlugin)this).Config);
_ropeCut = new RopeCutFeature();
_ropeCut.Configure(((BaseUnityPlugin)this).Config);
_catchPlayers = new CatchPlayersFeature();
_catchPlayers.Configure(((BaseUnityPlugin)this).Config);
_backpacks = new BackpacksForEveryoneFeature();
_backpacks.Configure(((BaseUnityPlugin)this).Config, (MonoBehaviour)(object)this);
_registry.Register((ModFeature)(object)new DroppablePassportFeature());
_registry.Register((ModFeature)(object)lastChanceFeature);
_registry.Register((ModFeature)(object)new GhostPingFeature());
_registry.Register((ModFeature)(object)_itemInfo);
_registry.Register((ModFeature)(object)_mushroomInfo);
_registry.Register((ModFeature)(object)_teammateStats);
_registry.Register((ModFeature)(object)_catchPlayers);
_registry.Register((ModFeature)(object)_backpacks);
_registry.Register((ModFeature)(object)new CustomItemsFeature());
_registry.Register((ModFeature)(object)ropeSwingFeature);
_registry.Register((ModFeature)(object)_push);
_registry.Register((ModFeature)(object)_ropeCut);
_registry.EnableAll(((SpongePlugin)this).Patches);
_rules.Start();
if (((ModFeature)_backpacks).IsActive)
{
_events.OnCharacterSpawned((Action<Character>)delegate
{
_backpacks.OnRunStarted();
});
_events.OnRunEnded((Action)delegate
{
_backpacks.OnRunEnded();
});
}
}
private void Update()
{
if (_itemInfo != null && ((ModFeature)_itemInfo).IsActive)
{
_itemInfo.Tick();
}
if (_mushroomInfo != null && ((ModFeature)_mushroomInfo).IsActive)
{
_mushroomInfo.Tick();
}
if (_teammateStats != null && ((ModFeature)_teammateStats).IsActive)
{
_teammateStats.Tick();
}
if (_catchPlayers != null && ((ModFeature)_catchPlayers).IsActive)
{
_catchPlayers.Tick();
}
}
protected override void OnUnload()
{
GameEvents events = _events;
if (events != null)
{
events.UnsubscribeAll();
}
_itemInfo?.Cleanup();
_mushroomInfo?.Cleanup();
_teammateStats?.Cleanup();
HostRuleSet rules = _rules;
if (rules != null)
{
rules.Stop();
}
RopeSwingFeature.Rules = null;
PushFeature.Rules = null;
RopeCutFeature.Rules = null;
}
}
}
namespace SpongePEAK.QOL.Features
{
public sealed class BackpacksForEveryoneFeature : ModFeature
{
private const string ItemResourcePath = "0_Items/";
internal static ConfigEntry<string> BackpackPrefab;
internal static ConfigEntry<float> SpawnDelay;
private ModLog _log;
private MonoBehaviour _coroutineHost;
private bool _givenThisRun;
public override string Name => "BackpacksForEveryone";
public override string Description => "Gives every player a backpack when a run starts, if they do not already have one. Host only.";
public override string Replaces => "Nozz-BagsForEveryone";
public override string Section => "Items";
internal void Configure(ConfigFile config, MonoBehaviour coroutineHost)
{
_coroutineHost = coroutineHost;
BackpackPrefab = config.Bind<string>("Items", "Backpacks_PrefabName", "Backpack", "Name of the backpack prefab to spawn, resolved under the game's 0_Items resource path.");
SpawnDelay = config.Bind<float>("Items", "Backpacks_SpawnDelaySeconds", 3f, "Seconds to wait after a run starts before handing out backpacks, so inventories are ready.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
_log = log;
if (!NetRoleInfo.HasAuthority)
{
ModLog log2 = _log;
if (log2 != null)
{
log2.Info((object)"BackpacksForEveryone is host-driven: this client is not authoritative, so the host decides.");
}
}
}
internal void OnRunStarted()
{
if (!_givenThisRun && NetRoleInfo.HasAuthority)
{
_givenThisRun = true;
if ((Object)(object)_coroutineHost != (Object)null)
{
_coroutineHost.StartCoroutine(GiveBackpacks());
}
}
}
internal void OnRunEnded()
{
_givenThisRun = false;
}
private IEnumerator GiveBackpacks()
{
yield return (object)new WaitForSeconds(SpawnDelay?.Value ?? 3f);
if (!PhotonNetwork.InRoom && !PhotonNetwork.OfflineMode)
{
yield break;
}
int num = 0;
List<Character> allCharacters = Character.AllCharacters;
if (allCharacters == null)
{
yield break;
}
for (int i = 0; i < allCharacters.Count; i++)
{
Character val = allCharacters[i];
if (!((Object)(object)val == (Object)null) && !val.isBot)
{
Player player = val.player;
if (!((Object)(object)player == (Object)null) && player.backpackSlot != null && ((ItemSlot)player.backpackSlot).IsEmpty() && TrySpawnBackpackFor(val))
{
num++;
}
}
}
if (num > 0)
{
ModLog log = _log;
if (log != null)
{
log.Info((object)$"Handed out {num} backpack(s) at run start.");
}
}
}
private bool TrySpawnBackpackFor(Character character)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
try
{
Vector3 val = character.Center + Vector3.up * 0.5f;
GameObject val2 = PhotonNetwork.Instantiate("0_Items/" + (BackpackPrefab?.Value ?? "Backpack"), val, Quaternion.identity, (byte)0, (object[])null);
if ((Object)(object)val2 == (Object)null)
{
return false;
}
Item val3 = default(Item);
if (!val2.TryGetComponent<Item>(ref val3))
{
return false;
}
PhotonView component = ((Component)character).GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null)
{
val3.RequestPickup(component);
}
return true;
}
catch (Exception ex)
{
ModLog log = _log;
if (log != null)
{
log.Exception("Could not give a backpack to '" + character.characterName + "'", ex);
}
return false;
}
}
}
public sealed class CatchPlayersFeature : ModFeature
{
internal static ConfigEntry<float> CatchRadius;
internal static ConfigEntry<float> MinFallTime;
private ModLog _log;
public override string Name => "CatchFallingPlayers";
public override string Description => "Cushions the landing of teammates who fall close to you, using the game's own catch mechanic.";
public override string Replaces => "tony4twenty-Catch_Players";
public override string Section => "Survival";
internal void Configure(ConfigFile config)
{
CatchRadius = config.Bind<float>("Survival", "Catch_Radius", 4f, "Metres within which a falling teammate's landing is cushioned.");
MinFallTime = config.Bind<float>("Survival", "Catch_MinFallSeconds", 0.6f, "How long someone must have been airborne before catching applies, so ordinary jumps are ignored.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
_log = log;
}
private static void LimitFalling(Character character)
{
MethodInfo methodInfo = ReflectionCache.Method(typeof(Character), "LimitFalling");
if (methodInfo == null)
{
return;
}
try
{
methodInfo.Invoke(character, null);
}
catch
{
}
}
internal void Tick()
{
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
Character localCharacter = Character.localCharacter;
if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null || localCharacter.data.dead || localCharacter.data.passedOut)
{
return;
}
List<Character> allCharacters = Character.AllCharacters;
if (allCharacters == null)
{
return;
}
float num = CatchRadius?.Value ?? 4f;
float num2 = num * num;
float num3 = MinFallTime?.Value ?? 0.6f;
for (int i = 0; i < allCharacters.Count; i++)
{
Character val = allCharacters[i];
if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)localCharacter) && !((Object)(object)val.data == (Object)null) && !val.isBot && !val.data.dead && !(val.data.sinceGrounded < num3))
{
Vector3 val2 = val.Center - localCharacter.Center;
if (!(((Vector3)(ref val2)).sqrMagnitude > num2))
{
LimitFalling(val);
}
}
}
}
}
public sealed class CustomItemsFeature : ModFeature
{
public override string Name => "ExampleCustomItems";
public override string Description => "Adds example cloned items (Grape Berrynana, Nuclear Dynamite) that spawn naturally. Demonstrates the item cloning API.";
public override string Replaces => "(new behaviour)";
public override string Section => "Items";
public override bool DefaultEnabled => true;
protected override void OnEnable(PatchHost patches, ModLog log)
{
ItemCloner.SetLogger(log);
RegisterGrapeBanana();
RegisterNuclearDynamite();
RegisterBananaTorch();
RegisterImposterBanana();
if (log != null)
{
log.Info((object)"Registered example cloned items; they build once the item database loads.");
}
}
private static void RegisterGrapeBanana()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//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_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_0147: Unknown result type (might be due to invalid IL or missing references)
ItemCloner.Register(new ItemCloneDefinition("Yellow Berrynana", "SpongeGrapeBanana", "Grape Berrynana").WithAlternateSources(new string[4] { "Berrynana Yellow", "Pink Berrynana", "Blue Berrynana", "Brown Berrynana" }).WithDescription("A berrynana that got ideas.").WithHueShift(270f, 1.15f, 1f)
.WithSpawning(new SpawnRule().InPool((SpawnPool)16, (Rarity)1).InPool((SpawnPool)4096, (Rarity)2).InPool((SpawnPool)536870912, (Rarity)5))
.WithEffectsWhenCooked(0, (StatusEffect[])(object)new StatusEffect[2]
{
StatusEffect.FromPoints((STATUSTYPE)6, 40f),
StatusEffect.FromPoints((STATUSTYPE)1, -10f)
})
.WithEffectsWhenCooked(1, (StatusEffect[])(object)new StatusEffect[2]
{
StatusEffect.FromPoints((STATUSTYPE)6, 20f),
StatusEffect.FromPoints((STATUSTYPE)1, -30f)
})
.WithEffectsWhenCooked(2, (StatusEffect[])(object)new StatusEffect[1] { StatusEffect.FromPoints((STATUSTYPE)1, -50f) })
.WithEffectsWhenCooked(4, (StatusEffect[])(object)new StatusEffect[2]
{
StatusEffect.FromPoints((STATUSTYPE)1, -25f),
StatusEffect.FromPoints((STATUSTYPE)3, 20f)
})
.WithEffectsWhenCooked(5, (StatusEffect[])(object)new StatusEffect[1] { StatusEffect.FromPoints((STATUSTYPE)3, 40f) }));
}
private static void RegisterNuclearDynamite()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Expected O, but got Unknown
//IL_00a5: Expected O, but got Unknown
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
ItemCloner.Register(new ItemCloneDefinition("Dynamite", "SpongeNuclearDynamite", "Nuclear Dynamite").WithDescription("Light the fuse. Then run, and keep running.").WithFuse(30f, true).WithHueShift(120f, 1.3f, 1f)
.WithScale(5f)
.WithExplosion(new ExplosionOverride
{
RadiusMultiplier = 200f,
DamageMultiplier = 200f,
ItemForceMultiplier = 200f,
KnockbackMultiplier = 200f,
Cloud = new StatusCloud
{
Status = (STATUSTYPE)3,
Amount = 0.5f,
RadiusFraction = 0.5f
}
})
.WithSpawning(new SpawnRule
{
BanInSolo = true
}.InPool((SpawnPool)8192, (Rarity)5).InPoolWithWeight((SpawnPool)536870912, 1)));
}
private static void RegisterBananaTorch()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
ItemCloner.Register(new ItemCloneDefinition("Yellow Berrynana", "SpongeBananaBaton", "Berrynana Baton").WithAlternateSources(new string[2] { "Berrynana Yellow", "Pink Berrynana" }).WithHueShift(30f, 0.7f, 1.2f).WithVariant((SpecialVariant)1)
.WithScale(1.4f)
.WithBehaviour(new ItemBehaviourSpec().ClearActions().Remove<ItemCooking>().Add<Action_ApplyInfiniteStamina>((Action<Action_ApplyInfiniteStamina>)delegate(Action_ApplyInfiniteStamina action)
{
action.buffTime = 6f;
action.drowsyAmount = 0f;
((ItemAction)action).OnCastFinished = true;
})
.ConfigureUIData((Action<Item>)delegate(Item item)
{
if (item.UIData != null)
{
item.UIData.mainInteractPrompt = "Use";
item.UIData.canDrop = true;
item.UIData.canThrow = true;
}
})));
}
private static void RegisterImposterBanana()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
ItemCloner.Register(new ItemCloneDefinition("Yellow Berrynana", "SpongeImposterBanana", "Yellow Berrynana").WithAlternateSources(new string[2] { "Berrynana Yellow", "Pink Berrynana" }).WithExplodeOnConsume("Dynamite"));
}
}
public sealed class DroppablePassportFeature : ModFeature
{
private const string PassportItemName = "Passport";
public override string Name => "DroppablePassport";
public override string Description => "Lets you drop and throw the Passport instead of it occupying a slot permanently.";
public override string Replaces => "Icebox-IForgorPassport";
public override string Section => "Items";
internal static string TargetItem => "Passport";
protected override void OnEnable(PatchHost patches, ModLog log)
{
if (!patches.PatchType(typeof(PassportDropPatch)))
{
((ModFeature)this).Fail("could not apply PassportDropPatch");
}
}
}
[HarmonyPatch(typeof(Item), "Awake")]
internal static class PassportDropPatch
{
[HarmonyPostfix]
private static void Postfix(Item __instance)
{
if (!((Object)(object)__instance == (Object)null) && __instance.UIData != null && !(__instance.UIData.itemName != DroppablePassportFeature.TargetItem))
{
__instance.UIData.canDrop = true;
__instance.UIData.canThrow = true;
}
}
}
public sealed class GhostPingFeature : ModFeature
{
public override string Name => "GhostPing";
public override string Description => "Lets dead players ping locations, so they can still help the group navigate.";
public override string Replaces => "boxofbiscuits97-GhostPing";
public override string Section => "Communication";
protected override void OnEnable(PatchHost patches, ModLog log)
{
if (!patches.PatchType(typeof(GhostPingPatch)))
{
((ModFeature)this).Fail("could not apply GhostPingPatch");
}
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
internal static class GhostPingPatch
{
[HarmonyPostfix]
private static void Postfix(PointPinger __instance, ref bool __result)
{
if (!__result)
{
Character value = Traverse.Create((object)__instance).Field("character").GetValue<Character>();
if (!((Object)(object)value == (Object)null) && !((Object)(object)value.data == (Object)null) && (value.IsGhost || value.data.dead))
{
bool value2 = Traverse.Create((object)__instance).Property("inCooldown", (object[])null).GetValue<bool>();
__result = !value2;
}
}
}
}
public sealed class ItemInfoFeature : ModFeature
{
internal static ConfigEntry<bool> ShowUses;
internal static ConfigEntry<float> FontSize;
private TextMeshProUGUI _label;
private ModLog _log;
private string _lastText = string.Empty;
private Item _lastItem;
private int _lastUses = int.MinValue;
public override string Name => "ItemInfo";
public override string Description => "Shows remaining uses and details for the item you are holding.";
public override string Replaces => "Yueby-ItemInfo";
public override string Section => "Display";
internal void Configure(ConfigFile config)
{
ShowUses = config.Bind<bool>("Display", "ItemInfo_ShowUses", true, "Shows remaining uses for consumable items.");
FontSize = config.Bind<float>("Display", "ItemInfo_FontSize", 18f, "Font size for the held-item readout.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
_log = log;
}
internal void Tick()
{
Character localCharacter = Character.localCharacter;
Item val = (((Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.data != (Object)null) ? localCharacter.data.currentItem : null);
if ((Object)(object)val == (Object)null)
{
_lastItem = null;
_lastUses = int.MinValue;
SetText(string.Empty);
return;
}
int remainingUses = GetRemainingUses(val);
if (val != _lastItem || remainingUses != _lastUses)
{
_lastItem = val;
_lastUses = remainingUses;
SetText(BuildReadout(val, remainingUses));
}
}
private static string BuildReadout(Item item, int remaining)
{
string text = ((item.UIData != null) ? item.UIData.itemName : null);
if (string.IsNullOrEmpty(text))
{
return string.Empty;
}
if (ShowUses == null || !ShowUses.Value)
{
return text;
}
if (item.totalUses <= 0)
{
return text;
}
if (remaining < 0)
{
return text;
}
return $"{text} <color=#B0B0B0>{remaining}/{item.totalUses}</color>";
}
private static int GetRemainingUses(Item item)
{
try
{
if (!item.HasData((DataEntryKey)2))
{
return -1;
}
OptionableIntItemData data = item.GetData<OptionableIntItemData>((DataEntryKey)2);
if (data == null || !data.HasData)
{
return -1;
}
return data.Value;
}
catch
{
return -1;
}
}
private void SetText(string text)
{
if (text == _lastText)
{
return;
}
if (string.IsNullOrEmpty(text))
{
if ((Object)(object)_label != (Object)null)
{
((TMP_Text)_label).text = string.Empty;
}
_lastText = text;
return;
}
if ((Object)(object)_label == (Object)null)
{
_label = HudRoot.CreateLabel("SpongeQOL_ItemInfo", (UiAnchor)3, FontSize?.Value ?? 18f);
if ((Object)(object)_label == (Object)null)
{
return;
}
ModLog log = _log;
if (log != null)
{
log.Debug((object)"ItemInfo label created.");
}
}
((TMP_Text)_label).text = text;
_lastText = text;
}
internal void Cleanup()
{
if ((Object)(object)_label != (Object)null)
{
Object.Destroy((Object)(object)((Component)_label).gameObject);
}
_label = null;
_lastText = string.Empty;
}
}
public sealed class LastChanceFeature : ModFeature
{
private const float PassOutThreshold = 0.999f;
internal static ConfigEntry<bool> Enabled;
public override string Name => "LastChanceCast";
public override string Description => "Delays passing out while you are mid-cast, so a last-second item use is not wasted.";
public override string Replaces => "IntRabbit-Last_Chance";
public override string Section => "Survival";
internal static float Threshold => 0.999f;
internal void Configure(ConfigFile config)
{
Enabled = config.Bind<bool>("Survival", "LastChance_Enabled", true, "Holds off passing out while an item cast is in progress. The reprieve ends as soon as the cast finishes or is cancelled.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
if (!patches.PatchType(typeof(LastChancePatch)))
{
((ModFeature)this).Fail("could not apply LastChancePatch");
}
}
internal static bool IsMidCast(Character character)
{
if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null)
{
return false;
}
Item currentItem = character.data.currentItem;
if ((Object)(object)currentItem == (Object)null)
{
return false;
}
if (currentItem.castProgress > 0f)
{
return !currentItem.finishedCast;
}
return false;
}
}
[HarmonyPatch(typeof(Character), "HandleLife")]
internal static class LastChancePatch
{
[HarmonyPrefix]
private static bool Prefix(Character __instance)
{
if (LastChanceFeature.Enabled == null || !LastChanceFeature.Enabled.Value)
{
return true;
}
if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.data == (Object)null || __instance.refs == null)
{
return true;
}
CharacterAfflictions afflictions = __instance.refs.afflictions;
if ((Object)(object)afflictions == (Object)null)
{
return true;
}
if (afflictions.statusSum < 1f)
{
return true;
}
if (__instance.data.passOutValue <= LastChanceFeature.Threshold)
{
return true;
}
return !LastChanceFeature.IsMidCast(__instance);
}
}
public sealed class MushroomInfoFeature : ModFeature
{
private const int EffectCount = 10;
internal static ConfigEntry<KeyCode> ToggleKey;
internal static ConfigEntry<bool> StartVisible;
internal static ConfigEntry<float> FontSize;
private TextMeshProUGUI _label;
private ModLog _log;
private bool _visible;
private MushroomManager _lastManager;
private string _cached;
public override string Name => "MushroomInfo";
public override string Description => "Shows what each mushroom does in this run, instead of learning by trial and error.";
public override string Replaces => "Kirshoo-MushroomsNOW";
public override string Section => "Display";
internal void Configure(ConfigFile config)
{
ToggleKey = config.Bind<KeyCode>("Display", "MushroomInfo_ToggleKey", (KeyCode)288, "Key that shows or hides the mushroom effect list.");
StartVisible = config.Bind<bool>("Display", "MushroomInfo_StartVisible", false, "Whether the list is shown without pressing the toggle key.");
FontSize = config.Bind<float>("Display", "MushroomInfo_FontSize", 15f, "Font size for the mushroom effect list.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
_log = log;
_visible = StartVisible?.Value ?? false;
}
internal void Tick()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
if (!GameState.InRun)
{
SetText(string.Empty);
return;
}
if (ToggleKey != null && Input.GetKeyDown(ToggleKey.Value))
{
_visible = !_visible;
}
MushroomManager instance = MushroomManager.instance;
if (!_visible || (Object)(object)instance == (Object)null)
{
SetText(string.Empty);
return;
}
if ((Object)(object)instance != (Object)(object)_lastManager || _cached == null)
{
_lastManager = instance;
_cached = BuildReadout(instance);
}
SetText(_cached);
}
private static string BuildReadout(MushroomManager manager)
{
int[] mushroomEffects = manager.mushroomEffects;
if (mushroomEffects == null || mushroomEffects.Length == 0)
{
return string.Empty;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("<b>Mushrooms</b>\n");
int num = Mathf.Min(mushroomEffects.Length, 10);
for (int i = 0; i < num; i++)
{
int effect = mushroomEffects[i];
string arg = (IsGood(effect) ? "#7FD97F" : "#E08A8A");
stringBuilder.Append($"<color={arg}>#{i + 1} {DescribeEffect(effect)}</color>\n");
}
return stringBuilder.ToString();
}
private static bool IsGood(int effect)
{
int[] goodEffects = Action_RandomMushroomEffect.GoodEffects;
if (goodEffects == null)
{
return effect <= 4;
}
for (int i = 0; i < goodEffects.Length; i++)
{
if (goodEffects[i] == effect)
{
return true;
}
}
return false;
}
private static string DescribeEffect(int effect)
{
return effect switch
{
0 => "Infinite stamina (4s)",
1 => "Faster climbing (5s)",
2 => "Low gravity (15s)",
3 => "Invincibility (10s)",
4 => "Cures hunger, injury and poison",
5 => "Spore explosion, launches you",
6 => "Blindness (60s)",
7 => "Fall damage",
8 => "Spores +25%",
9 => "Numbness (60s)",
_ => $"Unknown effect {effect}",
};
}
private void SetText(string text)
{
if (string.IsNullOrEmpty(text))
{
if ((Object)(object)_label != (Object)null && ((TMP_Text)_label).text.Length > 0)
{
((TMP_Text)_label).text = string.Empty;
}
return;
}
if ((Object)(object)_label == (Object)null)
{
_label = HudRoot.CreateLabel("SpongeQOL_MushroomInfo", (UiAnchor)1, FontSize?.Value ?? 15f);
if ((Object)(object)_label == (Object)null)
{
return;
}
ModLog log = _log;
if (log != null)
{
log.Debug((object)"Mushroom info label created.");
}
}
if (((TMP_Text)_label).text != text)
{
((TMP_Text)_label).text = text;
}
}
internal void Cleanup()
{
if ((Object)(object)_label != (Object)null)
{
Object.Destroy((Object)(object)((Component)_label).gameObject);
}
_label = null;
_cached = null;
_lastManager = null;
}
}
public sealed class PushFeature : ModFeature
{
public const string RuleId = "AllowPushing";
public const bool RuleDefault = true;
private ConfigEntry<KeyCode> _key;
private ConfigEntry<float> _force;
private ConfigEntry<float> _range;
private ConfigEntry<float> _cooldown;
private ModLog _log;
private float _nextPush;
public override string Name => "Push";
public override string Description => "Lets players shove each other. On by default; the host owns it for the whole lobby, because being pushed is not something the person falling gets to opt out of.";
public override string Replaces => "boxofbiscuits97-PushMod";
public override string Section => "Movement";
internal static HostRuleSet Rules { get; set; }
public void Configure(ConfigFile config)
{
_key = config.Bind<KeyCode>("Movement", "PushKey", (KeyCode)102, "Key that shoves the player you are looking at. Only works when the host allows pushing.");
_force = config.Bind<float>("Movement", "PushForce", 18f, "Impulse strength. Around 18 is a firm shove; much higher launches people, which is funny once and then ends someone's run.");
_range = config.Bind<float>("Movement", "PushRange", 3.5f, "How close you must be, in metres.");
_cooldown = config.Bind<float>("Movement", "PushCooldown", 1f, "Seconds between pushes. Prevents holding the key from stunlocking someone and flooding the room with RPCs.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
_log = log;
log.Info((object)"Push ready; host rule 'AllowPushing' decides whether it is allowed.");
}
public void Tick()
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
if (((ModFeature)this).IsActive && Rules != null && Rules.IsOn("AllowPushing"))
{
ConfigEntry<KeyCode> key = _key;
if (Input.GetKeyDown((KeyCode)((key == null) ? 102 : ((int)key.Value))) && !(Time.time < _nextPush))
{
TryPush();
}
}
}
private void TryPush()
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: 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_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_014c: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
try
{
Character localCharacter = Character.localCharacter;
if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null || localCharacter.data.dead)
{
return;
}
Camera main = Camera.main;
if ((Object)(object)main == (Object)null)
{
return;
}
Vector3 center = localCharacter.Center;
Vector3 forward = ((Component)main).transform.forward;
float num = Mathf.Clamp(_range?.Value ?? 3.5f, 1f, 10f);
Character val = null;
float num2 = 0.5f;
foreach (Character allCharacter in Character.AllCharacters)
{
if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)localCharacter || allCharacter.isBot || (Object)(object)allCharacter.data == (Object)null || allCharacter.data.dead)
{
continue;
}
Vector3 val2 = allCharacter.Center - center;
float magnitude = ((Vector3)(ref val2)).magnitude;
if (!(magnitude > num) && !(magnitude < 0.01f))
{
float num3 = Vector3.Dot(forward, val2 / magnitude);
if (num3 > num2)
{
num2 = num3;
val = allCharacter;
}
}
}
if (!((Object)(object)val == (Object)null))
{
Push(val, forward);
}
}
catch (Exception ex)
{
ModLog log = _log;
if (log != null)
{
log.Exception("Push failed", ex);
}
}
}
private void Push(Character target, Vector3 direction)
{
//IL_0043: 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_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Unknown result type (might be due to invalid IL or missing references)
PhotonView val = target.refs?.view;
if (!((Object)(object)val == (Object)null))
{
float num = Mathf.Clamp(_force?.Value ?? 18f, 1f, 60f);
Vector3 val2 = direction + Vector3.up * 0.25f;
Vector3 val3 = ((Vector3)(ref val2)).normalized * num;
val.RPC("RPCA_AddForceAtPosition", (RpcTarget)0, new object[3] { val3, target.Center, 1.5f });
_nextPush = Time.time + Mathf.Max(0.1f, _cooldown?.Value ?? 1f);
ModLog log = _log;
if (log != null)
{
log.Debug((object)("Pushed " + target.characterName + "."));
}
}
}
}
public sealed class RopeCutFeature : ModFeature
{
public const string RuleId = "AllowRopeCutting";
public const bool RuleDefault = true;
private ConfigEntry<KeyCode> _key;
private ConfigEntry<float> _range;
private ConfigEntry<bool> _requireEmpty;
private ModLog _log;
private Rope _pending;
private float _pendingUntil;
public override string Name => "RopeCutting";
public override string Description => "Lets players cut a rope down so a badly placed one is not permanent. Refuses a rope with someone on it unless that guard is disabled. The host can switch it off.";
public override string Replaces => "xammen-RopeCut";
public override string Section => "Movement";
internal static HostRuleSet Rules { get; set; }
public void Configure(ConfigFile config)
{
_key = config.Bind<KeyCode>("Movement", "RopeCutKey", (KeyCode)103, "Key that cuts the nearest rope. Only works when the host allows rope cutting.");
_range = config.Bind<float>("Movement", "RopeCutRange", 3f, "How close to the rope you must be, in metres.");
_requireEmpty = config.Bind<bool>("Movement", "RopeCutRequireEmpty", true, "Refuse to cut a rope somebody is currently climbing. Turning this off allows dropping other players, which is why the host rule exists at all.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
_log = log;
log.Info((object)"Rope cutting ready; host rule 'AllowRopeCutting' decides whether it is allowed.");
}
public void Tick()
{
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
if (!((ModFeature)this).IsActive)
{
return;
}
CompletePendingCut();
if (Rules != null && Rules.IsOn("AllowRopeCutting"))
{
ConfigEntry<KeyCode> key = _key;
if (Input.GetKeyDown((KeyCode)((key == null) ? 103 : ((int)key.Value))))
{
TryCut();
}
}
}
private void CompletePendingCut()
{
if ((Object)(object)_pending == (Object)null)
{
return;
}
if (Time.time > _pendingUntil)
{
ModLog log = _log;
if (log != null)
{
log.Debug((object)"Ownership transfer timed out; rope not cut.");
}
_pending = null;
return;
}
PhotonView photonView = ((MonoBehaviourPun)_pending).photonView;
if ((Object)(object)photonView == (Object)null)
{
_pending = null;
}
else if (photonView.IsMine)
{
Destroy(_pending);
_pending = null;
}
}
private void TryCut()
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
try
{
Character localCharacter = Character.localCharacter;
if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null || localCharacter.data.dead)
{
return;
}
float num = Mathf.Clamp(_range?.Value ?? 3f, 1f, 10f);
Vector3 center = localCharacter.Center;
Rope val = null;
float num2 = num;
Rope[] array = Object.FindObjectsByType<Rope>((FindObjectsInactive)0, (FindObjectsSortMode)0);
foreach (Rope val2 in array)
{
if ((Object)(object)val2 == (Object)null || !val2.IsActive())
{
continue;
}
ConfigEntry<bool> requireEmpty = _requireEmpty;
if ((requireEmpty != null && !requireEmpty.Value) || val2.charactersClimbing == null || val2.charactersClimbing.Count <= 0)
{
float num3 = NearestSegmentDistance(val2, center);
if (num3 < num2)
{
num2 = num3;
val = val2;
}
}
}
if (!((Object)(object)val == (Object)null))
{
Cut(val);
}
}
catch (Exception ex)
{
ModLog log = _log;
if (log != null)
{
log.Exception("Rope cut failed", ex);
}
}
}
private static float NearestSegmentDistance(Rope rope, Vector3 point)
{
//IL_0018: 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_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
List<Transform> ropeSegments = rope.GetRopeSegments();
if (ropeSegments == null || ropeSegments.Count == 0)
{
return Vector3.Distance(((Component)rope).transform.position, point);
}
float num = float.MaxValue;
foreach (Transform item in ropeSegments)
{
if (!((Object)(object)item == (Object)null))
{
float num2 = Vector3.Distance(item.position, point);
if (num2 < num)
{
num = num2;
}
}
}
return num;
}
private void Cut(Rope rope)
{
PhotonView photonView = ((MonoBehaviourPun)rope).photonView;
if ((Object)(object)photonView == (Object)null)
{
return;
}
if (photonView.IsMine)
{
Destroy(rope);
return;
}
photonView.RequestOwnership();
_pending = rope;
_pendingUntil = Time.time + 2f;
ModLog log = _log;
if (log != null)
{
log.Debug((object)"Requested rope ownership before cutting.");
}
}
private void Destroy(Rope rope)
{
try
{
PhotonNetwork.Destroy(((Component)rope).gameObject);
ModLog log = _log;
if (log != null)
{
log.Info((object)"Cut a rope.");
}
}
catch (Exception ex)
{
ModLog log2 = _log;
if (log2 != null)
{
log2.Exception("Could not destroy the rope", ex);
}
}
}
}
public sealed class RopeSwingFeature : ModFeature
{
public const string RuleId = "AllowRopeSwinging";
public const bool RuleDefault = true;
private ConfigEntry<float> _maxAngle;
internal const float VanillaAngle = 90f;
public override string Name => "RopeSwinging";
public override string Description => "Lets players swing on ropes rather than being dropped when the rope passes vertical. The host decides whether it is allowed for the lobby.";
public override string Replaces => "lamiiasuu-RopeSwingingMod";
public override string Section => "Movement";
internal static HostRuleSet Rules { get; set; }
internal static float SwingAngle { get; private set; } = 150f;
public void Configure(ConfigFile config)
{
_maxAngle = config.Bind<float>("Movement", "RopeSwingMaxAngle", 150f, "How far past vertical a rope may swing before it drops you, in degrees. The game uses 90. Higher allows a fuller swing; beyond about 170 the rope can invert and look wrong.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
SwingAngle = Mathf.Clamp(_maxAngle?.Value ?? 150f, 90f, 170f);
if (!patches.PatchType(typeof(RopeSwingPatch)))
{
((ModFeature)this).Fail("could not patch CharacterRopeHandling.Update");
}
else
{
log.Info((object)"Rope swinging ready; host rule 'AllowRopeSwinging' decides whether it applies.");
}
}
}
[HarmonyPatch(typeof(CharacterRopeHandling), "Update")]
internal static class RopeSwingPatch
{
[HarmonyPrefix]
private static void Prefix(CharacterRopeHandling __instance)
{
if (!((Object)(object)__instance == (Object)null))
{
bool flag = RopeSwingFeature.Rules != null && RopeSwingFeature.Rules.IsOn("AllowRopeSwinging");
__instance.maxRopeAngle = (flag ? RopeSwingFeature.SwingAngle : 90f);
}
}
}
public sealed class TeammateStatsFeature : ModFeature
{
private sealed class Readout
{
public HudBar Bar;
public TextMeshProUGUI Label;
}
internal static ConfigEntry<bool> ShowNames;
internal static ConfigEntry<float> MaxDistance;
private readonly Dictionary<Character, Readout> _readouts = new Dictionary<Character, Readout>();
private readonly List<Character> _stale = new List<Character>();
private ModLog _log;
public override string Name => "TeammateStats";
public override string Description => "Shows nearby teammates' stamina above their heads so you can tell who needs a rest.";
public override string Replaces => "nickklmao-PeakStats";
public override string Section => "Display";
internal void Configure(ConfigFile config)
{
ShowNames = config.Bind<bool>("Display", "TeammateStats_ShowNames", true, "Shows each teammate's name above their stamina bar.");
MaxDistance = config.Bind<float>("Display", "TeammateStats_MaxDistance", 30f, "Metres within which a teammate's stamina is shown.");
}
protected override void OnEnable(PatchHost patches, ModLog log)
{
_log = log;
}
internal void Tick()
{
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
Character localCharacter = Character.localCharacter;
if ((Object)(object)localCharacter == (Object)null || !HudRoot.IsReady)
{
HideAll();
return;
}
Camera main = Camera.main;
if ((Object)(object)main == (Object)null)
{
HideAll();
return;
}
float num = MaxDistance?.Value ?? 30f;
float num2 = num * num;
List<Character> allCharacters = Character.AllCharacters;
if (allCharacters == null)
{
return;
}
for (int i = 0; i < allCharacters.Count; i++)
{
Character val = allCharacters[i];
if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)localCharacter || (Object)(object)val.data == (Object)null)
{
continue;
}
if (val.isBot || val.data.dead)
{
Hide(val);
continue;
}
Vector3 head = val.Head;
Vector3 val2 = head - localCharacter.Center;
if (((Vector3)(ref val2)).sqrMagnitude > num2)
{
Hide(val);
}
else
{
Draw(val, head, main);
}
}
PruneDestroyed();
}
private void Draw(Character character, Vector3 head, Camera camera)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: 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_004a: 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_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
if (!_readouts.TryGetValue(character, out var value))
{
value = CreateReadout(character);
if (value == null)
{
return;
}
_readouts[character] = value;
}
Vector3 val = head + Vector3.up * 0.45f;
if (!HudRoot.PositionAtWorldPoint(value.Bar.Transform, val, camera))
{
SetVisible(value, visible: false);
return;
}
if ((Object)(object)value.Label != (Object)null)
{
HudRoot.PositionAtWorldPoint(((TMP_Text)value.Label).rectTransform, val + Vector3.up * 0.25f, camera);
}
float maxStamina = character.GetMaxStamina();
float currentStamina = character.data.currentStamina;
float num = ((maxStamina > 0f) ? Mathf.Clamp01(currentStamina / maxStamina) : 0f);
value.Bar.Value = num;
value.Bar.Color = ColourFor(num);
SetVisible(value, visible: true);
}
private static Color ColourFor(float fraction)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if (fraction > 0.5f)
{
return new Color(0.5f, 0.85f, 0.5f);
}
if (fraction > 0.25f)
{
return new Color(0.9f, 0.8f, 0.4f);
}
return new Color(0.9f, 0.45f, 0.45f);
}
private Readout CreateReadout(Character character)
{
HudBar val = HudBar.Create("SpongeQOL_Stam_" + character.characterName, 90f, 7f);
if (val == null)
{
return null;
}
TextMeshProUGUI val2 = null;
if (ShowNames == null || ShowNames.Value)
{
val2 = HudRoot.CreateLabel("SpongeQOL_Name_" + character.characterName, (UiAnchor)5, 13f);
if ((Object)(object)val2 != (Object)null)
{
((TMP_Text)val2).text = character.characterName;
}
}
ModLog log = _log;
if (log != null)
{
log.Debug((object)("Created stamina readout for '" + character.characterName + "'."));
}
return new Readout
{
Bar = val,
Label = val2
};
}
private void Hide(Character character)
{
if (_readouts.TryGetValue(character, out var value))
{
SetVisible(value, visible: false);
}
}
private void HideAll()
{
foreach (KeyValuePair<Character, Readout> readout in _readouts)
{
SetVisible(readout.Value, visible: false);
}
}
private static void SetVisible(Readout readout, bool visible)
{
HudBar bar = readout.Bar;
if (bar != null)
{
bar.SetVisible(visible);
}
if ((Object)(object)readout.Label != (Object)null && ((Component)readout.Label).gameObject.activeSelf != visible)
{
((Component)readout.Label).gameObject.SetActive(visible);
}
}
private void PruneDestroyed()
{
_stale.Clear();
foreach (KeyValuePair<Character, Readout> readout in _readouts)
{
if ((Object)(object)readout.Key == (Object)null)
{
_stale.Add(readout.Key);
}
}
foreach (Character item in _stale)
{
if (_readouts.TryGetValue(item, out var value))
{
DestroyReadout(value);
}
_readouts.Remove(item);
}
}
private static void DestroyReadout(Readout readout)
{
HudBar bar = readout.Bar;
if (bar != null)
{
bar.Destroy();
}
if ((Object)(object)readout.Label != (Object)null)
{
Object.Destroy((Object)(object)((Component)readout.Label).gameObject);
}
}
internal void Cleanup()
{
foreach (KeyValuePair<Character, Readout> readout in _readouts)
{
DestroyReadout(readout.Value);
}
_readouts.Clear();
}
}
}