using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckShrineOfTheMountain")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds the Risk of Rain 2 Shrine of the Mountain. A free activation stacks the next boss.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+1911ecd07d605294f346388086fc942f4c18d4fa")]
[assembly: AssemblyProduct("Shrine of the Mountain")]
[assembly: AssemblyTitle("MuckShrineOfTheMountain")]
[assembly: InternalsVisibleTo("MuckShrineOfTheMountain.Tests")]
[assembly: AssemblyVersion("0.1.0.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
internal sealed class IsReadOnlyAttribute : Attribute
{
}
}
namespace MuckShrineOfTheMountain
{
[HarmonyPatch(typeof(LootExtra), "BossLoot")]
internal static class BossLootPatch
{
private static void Postfix(Transform __0, BossType __1)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Invalid comparison between Unknown and I4
//IL_00ae: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
try
{
if (!LocalClient.serverOwner || MountainPlugin.Ledger == null)
{
return;
}
bool flag = (int)__1 == 1;
int num = DyingBossId(__0);
if (!MountainPlugin.Ledger.TryTakeReward(flag, num, out var drops))
{
if (flag && MountainPlugin.Ledger.Armed && num != MountainPlugin.Ledger.ArmedMobId)
{
MountainPlugin.Log.LogInfo((object)("Shrine of the Mountain skipped boss " + num + "; stacked boss is " + MountainPlugin.Ledger.ArmedMobId + "."));
}
return;
}
Vector3 at = (((Object)(object)__0 != (Object)null) ? __0.position : Vector3.zero);
MountainConfig cfg = MountainPlugin.Cfg;
float white = cfg?.BonusWhite.Value ?? 1f;
float blue = cfg?.BonusBlue.Value ?? 3f;
float orange = cfg?.BonusOrange.Value ?? 1f;
int dropped = MountainPayout.Pay(drops, white, blue, orange, at);
MountainPlugin.Log.LogInfo((object)("Shrine of the Mountain paid " + dropped + " bonus powerups."));
string text = MountainText.Payout(dropped);
if (text != null)
{
ServerSend.SendChatMessage(-1, "", text);
ChatBox instance = ChatBox.Instance;
if ((Object)(object)instance != (Object)null)
{
instance.AppendMessage(-1, text, "");
}
}
}
catch (Exception ex)
{
MountainPlugin.Log.LogError((object)("Shrine of the Mountain boss loot failed: " + ex));
}
}
internal static int DyingBossId(Transform drop)
{
if ((Object)(object)drop == (Object)null)
{
return -1;
}
MobManager instance = MobManager.Instance;
if ((Object)(object)instance == (Object)null || instance.mobs == null)
{
return -1;
}
foreach (KeyValuePair<int, Mob> mob in instance.mobs)
{
Mob value = mob.Value;
if (!((Object)(object)value == (Object)null))
{
Transform transform = ((Component)value).transform;
if ((Object)(object)transform == (Object)(object)drop || drop.IsChildOf(transform))
{
return mob.Key;
}
}
}
return -1;
}
}
[HarmonyPatch(typeof(ChatBox), "AppendMessage")]
internal static class ChatEchoPatch
{
private static bool Prefix(string message)
{
if (!MountainText.IsOurs(message) || MountainPlugin.Echo == null)
{
return true;
}
return MountainPlugin.Echo.Allow(message, Time.time);
}
}
internal static class ConfigStamp
{
public static string Code(int count, int spacingCm, int uses, int perThousand, int maxFactorThousand, int dropsPerStack, int white, int blue, int orange)
{
uint h = 2166136261u;
h = Mix(h, count);
h = Mix(h, spacingCm);
h = Mix(h, uses);
h = Mix(h, perThousand);
h = Mix(h, maxFactorThousand);
h = Mix(h, dropsPerStack);
h = Mix(h, white);
h = Mix(h, blue);
return Mix(h, orange).ToString("X8");
}
private static uint Mix(uint h, int value)
{
h ^= (uint)value;
return h * 16777619;
}
}
internal sealed class EchoGate
{
private string line;
private float at = float.NaN;
public bool Allow(string message, float now, float window = 2f)
{
if (!float.IsNaN(at) && message == line && now >= at && now - at < window)
{
return false;
}
line = message;
at = now;
return true;
}
public void Reset()
{
line = null;
at = float.NaN;
}
}
internal static class Hash01
{
public static ulong SplitMix64(ulong x)
{
long num = (long)x + -7046029254386353131L;
long num2 = (num ^ (num >>> 30)) * -4658895280553007687L;
long num3 = (num2 ^ (num2 >>> 27)) * -7723592293110705685L;
return (ulong)(num3 ^ (num3 >>> 31));
}
public static double Of(int a, int b, int c)
{
return ToUnit(SplitMix64(SplitMix64(Key(a, b)) ^ (uint)c));
}
private static ulong Key(int a, int b)
{
return ((ulong)(uint)a << 32) | (uint)b;
}
private static double ToUnit(ulong h)
{
return (double)(h >> 11) * 1.1102230246251565E-16;
}
}
internal sealed class MountainConfig
{
public readonly ConfigEntry<int> Count;
public readonly ConfigEntry<float> MinSpacing;
public readonly ConfigEntry<int> Uses;
public readonly ConfigEntry<float> BonusPerStack;
public readonly ConfigEntry<float> MaxFactor;
public readonly ConfigEntry<int> BonusDropsPerStack;
public readonly ConfigEntry<float> BonusWhite;
public readonly ConfigEntry<float> BonusBlue;
public readonly ConfigEntry<float> BonusOrange;
public readonly ConfigEntry<bool> VerboseLogging;
public MountainConfig(ConfigFile cfg)
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Expected O, but got Unknown
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Expected O, but got Unknown
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Expected O, but got Unknown
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Expected O, but got Unknown
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_0134: Expected O, but got Unknown
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Expected O, but got Unknown
//IL_019c: Unknown result type (might be due to invalid IL or missing references)
//IL_01a6: Expected O, but got Unknown
//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
//IL_01df: Expected O, but got Unknown
Count = cfg.Bind<int>("World", "Count", 2, new ConfigDescription("How many Shrines of the Mountain to place on grass on each map, including each Wave Run island. 0 places none. On by default. Every player should use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 16), Array.Empty<object>()));
MinSpacing = cfg.Bind<float>("World", "MinSpacing", 80f, new ConfigDescription("Minimum distance in meters between shrines. Every player should use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), Array.Empty<object>()));
Uses = cfg.Bind<int>("Shrine", "Uses", 2, new ConfigDescription("Free activations on each shrine. Risk of Rain 2 uses the shrine twice. Every player should use the same value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
BonusPerStack = cfg.Bind<float>("Fight", "BonusPerStack", 1f, new ConfigDescription("Added to 1 for each stack on the next boss. 1 makes one stack double health and damage, the weight of one extra boss. The host's value is the one that matters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f), Array.Empty<object>()));
MaxFactor = cfg.Bind<float>("Fight", "MaxFactor", 8f, new ConfigDescription("The most the stacked boss's health and damage are multiplied, however many stacks are waiting. 8 is eight times a normal boss. 128 stacks at BonusPerStack 4 would be 513 without this. The host's value is the one that matters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 32f), Array.Empty<object>()));
BonusDropsPerStack = cfg.Bind<int>("Fight", "BonusDropsPerStack", 1, new ConfigDescription("Extra powerups when the stacked boss dies, per stack. Capped at 12 for the whole fight. The host's value is the one that matters.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 6), Array.Empty<object>()));
BonusWhite = cfg.Bind<float>("Fight", "BonusWhite", 1f, new ConfigDescription("White weight for a bonus powerup. Passed to ItemManager.GetRandomPowerup with the blue and orange weights.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
BonusBlue = cfg.Bind<float>("Fight", "BonusBlue", 3f, new ConfigDescription("Blue weight for a bonus powerup.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
BonusOrange = cfg.Bind<float>("Fight", "BonusOrange", 1f, new ConfigDescription("Orange weight for a bonus powerup.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log each shrine spot and each accepted use.");
}
public string Code()
{
return ConfigStamp.Code(Count.Value, (int)Math.Round(MinSpacing.Value * 100f), Uses.Value, (int)Math.Round(BonusPerStack.Value * 1000f), (int)Math.Round(MaxFactor.Value * 1000f), BonusDropsPerStack.Value, (int)Math.Round(BonusWhite.Value * 100f), (int)Math.Round(BonusBlue.Value * 100f), (int)Math.Round(BonusOrange.Value * 100f));
}
}
internal sealed class MountainLedger
{
public int Stacks { get; private set; }
public int ArmedMobId { get; private set; } = -1;
public int ArmedStacks { get; private set; }
public float ArmedFactor { get; private set; }
public int ArmedDrops { get; private set; }
public bool Armed => ArmedMobId >= 0;
public int AddStack()
{
Stacks++;
return Stacks;
}
public void ReleaseIfGone(bool stillPresent)
{
if (!(ArmedMobId < 0 || stillPresent))
{
Stacks += ArmedStacks;
ClearArmed();
}
}
public FightBuff Peek(bool bossNight, int guardianType, float perStack, int dropsPerStack, float maxFactor = 8f)
{
if (ArmedMobId >= 0)
{
return FightBuff.None;
}
return MountainMath.Offer(Stacks, bossNight, guardianType, perStack, dropsPerStack, maxFactor);
}
public void Commit(int mobId, FightBuff buff)
{
if (buff.Active && ArmedMobId < 0)
{
int stacks = buff.Stacks;
if (stacks > Stacks)
{
stacks = Stacks;
}
Stacks -= stacks;
ArmedMobId = mobId;
ArmedStacks = buff.Stacks;
ArmedFactor = buff.Factor;
ArmedDrops = buff.BonusDrops;
}
}
public bool TryTakeReward(bool bossNight, int mobId, out int drops)
{
drops = 0;
if (!bossNight || ArmedMobId < 0 || mobId != ArmedMobId)
{
return false;
}
drops = ArmedDrops;
ClearArmed();
return true;
}
public void OnNewWorld()
{
Stacks = 0;
ClearArmed();
}
public void OnNewMap()
{
ReleaseIfGone(stillPresent: false);
}
private void ClearArmed()
{
ArmedMobId = -1;
ArmedStacks = 0;
ArmedFactor = 0f;
ArmedDrops = 0;
}
}
internal readonly struct FightBuff
{
public readonly bool Active;
public readonly int Stacks;
public readonly float Factor;
public readonly int BonusDrops;
public static FightBuff None => default(FightBuff);
public FightBuff(bool active, int stacks, float factor, int bonusDrops)
{
Active = active;
Stacks = stacks;
Factor = factor;
BonusDrops = bonusDrops;
}
}
internal static class MountainMath
{
public const int MaxBonusDrops = 12;
public const float DefaultMaxFactor = 8f;
public static FightBuff Offer(int stacks, bool bossNight, int guardianType, float perStack, int dropsPerStack, float maxFactor = 8f)
{
if (stacks <= 0 || !bossNight || guardianType >= 0)
{
return FightBuff.None;
}
return new FightBuff(active: true, stacks, Factor(stacks, perStack, maxFactor), BonusDrops(stacks, dropsPerStack));
}
public static float Factor(int stacks, float perStack, float maxFactor = 8f)
{
if (stacks <= 0 || !(perStack > 0f) || float.IsInfinity(perStack))
{
return 1f;
}
float num = 1f + (float)stacks * perStack;
if (float.IsNaN(num) || float.IsInfinity(num) || num < 1f)
{
return 1f;
}
float num2 = maxFactor;
if (!(num2 >= 1f) || float.IsNaN(num2) || float.IsInfinity(num2))
{
num2 = 8f;
}
if (num > num2)
{
return num2;
}
return num;
}
public static int BonusDrops(int stacks, int dropsPerStack)
{
if (stacks <= 0 || dropsPerStack <= 0)
{
return 0;
}
long num = (long)stacks * (long)dropsPerStack;
if (num > 12)
{
return 12;
}
return (int)num;
}
public static float ScaledMultiplier(float incoming, float factor)
{
if (float.IsNaN(incoming) || float.IsInfinity(incoming) || !(factor > 0f) || float.IsInfinity(factor))
{
return incoming;
}
return incoming * factor;
}
}
internal static class MountainPayout
{
public static void Offset(int index, out float x, out float z)
{
double num = (double)index * 2.399963229728653;
x = (float)(Math.Cos(num) * 1.6);
z = (float)(Math.Sin(num) * 1.6);
}
public static int Pay(int count, float white, float blue, float orange, Vector3 at)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
if (count <= 0)
{
return 0;
}
ItemManager instance = ItemManager.Instance;
if (instance == null)
{
MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain bonus skipped: ItemManager.Instance is not set.");
return 0;
}
int num = 0;
for (int i = 0; i < count; i++)
{
try
{
if (DropOne(instance, white, blue, orange, at, i))
{
num++;
}
}
catch (Exception ex)
{
MountainPlugin.Log.LogError((object)("Shrine of the Mountain bonus drop " + (i + 1) + " failed: " + ex));
}
}
return num;
}
private static bool DropOne(ItemManager items, float white, float blue, float orange, Vector3 at, int index)
{
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
Powerup randomPowerup = items.GetRandomPowerup(white, blue, orange);
if ((Object)(object)randomPowerup == (Object)null)
{
MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain bonus: GetRandomPowerup returned nothing.");
return false;
}
Dictionary<int, GameObject> list = items.list;
if (!ObjectIds.TryMint((Func<int>)items.GetNextId, (Func<int, bool>)((int key) => list != null && list.ContainsKey(key)), 1000, out int id, out int skipped))
{
MountainPlugin.Log.LogError((object)"Shrine of the Mountain bonus skipped: item ids are already in ItemManager.list.");
return false;
}
if (skipped > 0)
{
MountainPlugin.Log.LogWarning((object)("Shrine of the Mountain bonus skipped " + skipped + " taken item ids, using " + id + "."));
}
Offset(index, out var x, out var z);
Vector3 val = at + new Vector3(x, 0.5f, z);
items.DropPowerupAtPosition(randomPowerup.id, val, id);
ServerSend.DropPowerupAtPosition(randomPowerup.id, id, val);
return true;
}
}
[BepInPlugin("evan.muck.shrineofthemountain", "Shrine of the Mountain", "0.1.0")]
public class MountainPlugin : BaseUnityPlugin
{
public const string Guid = "evan.muck.shrineofthemountain";
public const string Name = "Shrine of the Mountain";
public const string Version = "0.1.0";
internal static ManualLogSource Log;
internal static MountainConfig Cfg;
internal static MountainLedger Ledger = new MountainLedger();
internal static EchoGate Echo = new EchoGate();
private void Awake()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Cfg = new MountainConfig(((BaseUnityPlugin)this).Config);
Ledger = new MountainLedger();
Echo = new EchoGate();
Harmony harmony = new Harmony("evan.muck.shrineofthemountain");
if (!TryPatch(harmony, typeof(StructureSpawnerStartPatch)))
{
Log.LogError((object)"Shrine of the Mountain 0.1.0 could not hook world generation. No shrines will appear.");
return;
}
if (!TryPatch(harmony, typeof(SpawnBossPatch)))
{
Log.LogError((object)"Shrine of the Mountain could not hook boss spawns. Shrines will not toughen the next boss.");
}
if (!TryPatch(harmony, typeof(BossLootPatch)))
{
Log.LogError((object)"Shrine of the Mountain could not hook boss loot. A stacked boss will not drop the bonus powerups.");
}
if (!TryPatch(harmony, typeof(ChatEchoPatch)))
{
Log.LogWarning((object)"Shrine of the Mountain could not hook chat. The host can see an activation line twice.");
}
Log.LogInfo((object)("Shrine of the Mountain 0.1.0 loaded. Config " + Cfg.Code() + "."));
}
private static bool TryPatch(Harmony harmony, Type patchClass)
{
try
{
harmony.CreateClassProcessor(patchClass).Patch();
return true;
}
catch (Exception ex)
{
Log.LogError((object)("Shrine of the Mountain failed to patch " + patchClass.Name + ": " + ex));
return false;
}
}
}
public class MountainShrine : MonoBehaviour, Interactable, SharedObject
{
private static readonly Color Spent = new Color(0.28f, 0.28f, 0.3f);
private int id;
private ShrineUses shown;
private ShrineUses host;
private float pendingUntil;
private Material[] materials;
public static void Build(int shrineId, Vector3 ground, int uses)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Expected O, but got Unknown
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: 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_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("ShrineOfTheMountain");
val.transform.position = ground;
val.layer = 15;
BoxCollider obj = val.AddComponent<BoxCollider>();
((Collider)obj).isTrigger = true;
obj.size = new Vector3(2.4f, 3.2f, 2.4f);
obj.center = new Vector3(0f, 1.5f, 0f);
GameObject val2 = new GameObject("ShrineOfTheMountain_solid")
{
layer = 10
};
val2.transform.SetParent(val.transform, false);
BoxCollider obj2 = val2.AddComponent<BoxCollider>();
((Collider)obj2).isTrigger = false;
obj2.size = new Vector3(1.6f, 2.3f, 1.6f);
obj2.center = new Vector3(0f, 1.15f, 0f);
MountainShrine mountainShrine = val.AddComponent<MountainShrine>();
mountainShrine.SetId(shrineId);
mountainShrine.shown = new ShrineUses(uses);
mountainShrine.host = new ShrineUses(uses);
mountainShrine.materials = ShrineMesh.Attach(val.transform);
ResourceManager.Instance.AddObject(shrineId, val);
}
public void Interact()
{
if (shown != null && !shown.Spent && !(Time.time < pendingUntil))
{
pendingUntil = Time.time + 3f;
ClientSend.Interact(id);
}
}
public void LocalExecute()
{
}
public void ServerExecute(int fromClient = -1)
{
if (LocalClient.serverOwner && host != null && host.TrySpend())
{
int stacks = MountainPlugin.Ledger.AddStack();
string text = MountainText.Activation(PlayerNames.Of(fromClient), stacks, host.Left);
MountainPlugin.Log.LogInfo((object)text);
ServerSend.SendChatMessage(-1, "", text);
Say(text);
if (MountainPlugin.Cfg != null && MountainPlugin.Cfg.VerboseLogging.Value)
{
MountainPlugin.Log.LogInfo((object)("Shrine " + id + " use accepted for client " + fromClient + ". Stacks " + stacks + "."));
}
}
}
public void AllExecute()
{
pendingUntil = 0f;
if (shown != null && shown.TrySpend() && shown.Spent)
{
Grey();
}
}
public void RemoveObject()
{
Object.Destroy((Object)(object)((Component)this).gameObject);
}
public string GetName()
{
return MountainText.Prompt(shown != null && shown.Spent);
}
public bool IsStarted()
{
if (shown != null)
{
return shown.Spent;
}
return false;
}
public void SetId(int newId)
{
id = newId;
}
public int GetId()
{
return id;
}
private void OnDestroy()
{
if (materials == null)
{
return;
}
for (int i = 0; i < materials.Length; i++)
{
if ((Object)(object)materials[i] != (Object)null)
{
Object.Destroy((Object)(object)materials[i]);
}
}
}
private void Grey()
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
if (materials == null)
{
return;
}
for (int i = 0; i < materials.Length; i++)
{
if ((Object)(object)materials[i] != (Object)null)
{
materials[i].color = Spent;
}
}
}
private static void Say(string message)
{
ChatBox instance = ChatBox.Instance;
if ((Object)(object)instance != (Object)null)
{
instance.AppendMessage(-1, message, "");
}
}
}
internal static class MountainText
{
public const int ChatMax = 120;
public const string ActivationHead = "Shrine of the Mountain: ";
public const string PayoutHead = "The mountain boss falls.";
public static string Prompt(bool spent)
{
if (!spent)
{
return "Shrine of the Mountain (free)";
}
return "Shrine of the Mountain (spent)";
}
public static string Player(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return "Someone";
}
name = name.Replace('\r', ' ').Replace('\n', ' ').Trim();
if (name.Length > 40)
{
name = name.Substring(0, 40).Trim();
}
if (name.Length != 0)
{
return name;
}
return "Someone";
}
public static string Activation(string playerName, int stacks, int usesLeftHere)
{
string text = Player(playerName);
string text2 = ((stacks == 1) ? "stack" : "stacks");
string text3 = ((usesLeftHere <= 0) ? "It is spent." : ((usesLeftHere == 1) ? "1 use left here." : (usesLeftHere + " uses left here.")));
return Fit("Shrine of the Mountain: " + text + " added a stack (" + stacks + " " + text2 + " on the next boss). " + text3);
}
public static string Payout(int dropped)
{
if (dropped <= 0)
{
return null;
}
if (dropped == 1)
{
return "The mountain boss falls. An extra powerup spills out.";
}
return "The mountain boss falls. " + dropped + " extra powerups spill out.";
}
public static string Spot(float standX, float standY, float standZ, float aimX, float aimY, float aimZ)
{
return string.Format(CultureInfo.InvariantCulture, "Shrine of the Mountain spot {0:0.0} {1:0.0} {2:0.0} {3:0.0} {4:0.0} {5:0.0}", standX, standY, standZ, aimX, aimY, aimZ);
}
public static string Placed(int count, int seed)
{
return "Placed " + count + " Shrines of the Mountain (seed " + seed + ")";
}
public static string Armed(int mobId, float factor, int stacks)
{
return string.Format(CultureInfo.InvariantCulture, "Shrine of the Mountain armed boss {0} x{1:0.00} ({2} stacks).", mobId, factor, stacks);
}
public static bool IsOurs(string message)
{
if (string.IsNullOrEmpty(message))
{
return false;
}
if (!message.StartsWith("Shrine of the Mountain: "))
{
return message.StartsWith("The mountain boss falls.");
}
return true;
}
public static bool UsesReservedPrefix(string message)
{
if (string.IsNullOrEmpty(message))
{
return false;
}
if (!message.StartsWith("~sv"))
{
return message.StartsWith("~wr");
}
return true;
}
public static string Fit(string line)
{
if (string.IsNullOrEmpty(line))
{
return "";
}
if (line.Length <= 120)
{
return line;
}
return line.Substring(0, 120);
}
}
internal static class MountainWorld
{
private static ResourceManager placedFor;
private static int placedSeed;
private static bool warnedMissingManager;
private static readonly List<(double X, double Z)> placed = new List<(double, double)>();
public static void PlaceOnce(StructureSpawner spawner)
{
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_0149: Unknown result type (might be due to invalid IL or missing references)
//IL_015f: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_0170: Invalid comparison between Unknown and I4
//IL_017b: Unknown result type (might be due to invalid IL or missing references)
//IL_0181: Invalid comparison between Unknown and I4
//IL_018b: Unknown result type (might be due to invalid IL or missing references)
//IL_0191: Invalid comparison between Unknown and I4
//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
ResourceManager instance = ResourceManager.Instance;
int seed = GameManager.GetSeed();
bool num = instance != null;
switch (WorldGate.Decide(num, num && (Object)(object)placedFor == (Object)(object)instance, placedSeed == seed))
{
default:
return;
case PlaceKind.MissingManager:
if (!warnedMissingManager)
{
warnedMissingManager = true;
MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain skipped placement: ResourceManager.Instance is null.");
}
return;
case PlaceKind.AlreadyPlaced:
return;
case PlaceKind.NewWorld:
MountainPlugin.Ledger.OnNewWorld();
MountainPlugin.Echo.Reset();
break;
case PlaceKind.NewMap:
MountainPlugin.Ledger.OnNewMap();
break;
}
placedFor = instance;
placedSeed = seed;
placed.Clear();
warnedMissingManager = false;
float halfExtent = Placement.HalfExtent(MapGenerator.mapChunkSize, spawner.worldScale);
MountainConfig cfg = MountainPlugin.Cfg;
int num2 = Placement.ClampCount(cfg.Count.Value);
float value = cfg.MinSpacing.Value;
int value2 = cfg.Uses.Value;
int num3 = num2 * 8;
int num4 = 0;
RaycastHit val = default(RaycastHit);
for (int i = 0; i < num3; i++)
{
if (num4 >= num2)
{
break;
}
(double, double) item = Placement.Candidate(seed, i, halfExtent);
if (Placement.FarEnough(placed, item.Item1, item.Item2, value) && Physics.Raycast(new Vector3((float)item.Item1, 200f, (float)item.Item2), Vector3.down, ref val, 500f, LayerMask.op_Implicit(spawner.whatIsTerrain)))
{
float y = ((RaycastHit)(ref val)).point.y;
bool hit = (int)WorldUtility.WorldHeightToBiome(y) == 2;
bool below = (int)WorldUtility.WorldHeightToBiome(y - 0.5f) == 2;
bool above = (int)WorldUtility.WorldHeightToBiome(y + 0.5f) == 2;
if (Placement.InsideGrass(hit, below, above))
{
MountainShrine.Build(ShrineIds.For(i), ((RaycastHit)(ref val)).point, value2);
placed.Add(item);
num4++;
Placement.StandAndAim(((RaycastHit)(ref val)).point.x, ((RaycastHit)(ref val)).point.y, ((RaycastHit)(ref val)).point.z, out var sx, out var sy, out var sz, out var ax, out var ay, out var az);
MountainPlugin.Log.LogInfo((object)MountainText.Spot(sx, sy, sz, ax, ay, az));
}
}
}
MountainPlugin.Log.LogInfo((object)MountainText.Placed(num4, seed));
MountainPlugin.Log.LogInfo((object)("Shrine of the Mountain config " + cfg.Code() + "."));
}
}
internal static class ObjectIds
{
public const int MaxTries = 1000;
public static bool TryMint(Func<int> next, Func<int, bool> isTaken, int maxTries, out int id, out int skipped)
{
if (next == null)
{
throw new ArgumentNullException("next");
}
if (isTaken == null)
{
throw new ArgumentNullException("isTaken");
}
skipped = 0;
for (int i = 0; i < maxTries; i++)
{
id = next();
if (!isTaken(id))
{
return true;
}
skipped++;
}
id = -1;
return false;
}
}
internal sealed class ObjPart
{
public readonly string Material;
public readonly List<float> Positions = new List<float>();
public int Triangles => Positions.Count / 9;
public ObjPart(string material)
{
Material = material;
}
public void Add(List<float> verts, int a, int b, int c)
{
Copy(verts, a);
Copy(verts, b);
Copy(verts, c);
}
private void Copy(List<float> verts, int index)
{
int num = index * 3;
Positions.Add(verts[num]);
Positions.Add(verts[num + 1]);
Positions.Add(verts[num + 2]);
}
}
internal sealed class ObjModel
{
public readonly List<ObjPart> Parts;
public int Triangles
{
get
{
int num = 0;
for (int i = 0; i < Parts.Count; i++)
{
num += Parts[i].Triangles;
}
return num;
}
}
public ObjModel(List<ObjPart> parts)
{
Parts = parts;
}
public void Bounds(out float minY, out float maxY)
{
Box(out var _, out var _, out minY, out maxY, out var _, out var _);
}
public void Box(out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ)
{
minX = (minY = (minZ = float.PositiveInfinity));
maxX = (maxY = (maxZ = float.NegativeInfinity));
for (int i = 0; i < Parts.Count; i++)
{
List<float> positions = Parts[i].Positions;
for (int j = 0; j < positions.Count; j += 3)
{
float num = positions[j];
float num2 = positions[j + 1];
float num3 = positions[j + 2];
if (num < minX)
{
minX = num;
}
if (num > maxX)
{
maxX = num;
}
if (num2 < minY)
{
minY = num2;
}
if (num2 > maxY)
{
maxY = num2;
}
if (num3 < minZ)
{
minZ = num3;
}
if (num3 > maxZ)
{
maxZ = num3;
}
}
}
}
public static ObjModel Parse(string text)
{
List<float> list = new List<float>();
List<ObjPart> list2 = new List<ObjPart>();
ObjPart objPart = null;
string text2 = "stone";
if (string.IsNullOrEmpty(text))
{
return new ObjModel(list2);
}
string[] array = text.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < array.Length; i++)
{
string text3 = array[i].Trim();
if (text3.Length == 0 || text3[0] == '#')
{
continue;
}
if (text3.StartsWith("usemtl "))
{
text2 = text3.Substring(7).Trim();
objPart = null;
}
else if (text3.StartsWith("v "))
{
string[] array2 = text3.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
list.Add(float.Parse(array2[1], CultureInfo.InvariantCulture));
list.Add(float.Parse(array2[2], CultureInfo.InvariantCulture));
list.Add(float.Parse(array2[3], CultureInfo.InvariantCulture));
}
else
{
if (!text3.StartsWith("f "))
{
continue;
}
string[] array3 = text3.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
List<int> list3 = new List<int>(array3.Length - 1);
int count = list.Count / 3;
for (int j = 1; j < array3.Length; j++)
{
list3.Add(VertexIndex(array3[j], count));
}
if (list3.Count >= 3)
{
if (objPart == null || objPart.Material != text2)
{
objPart = new ObjPart(text2);
list2.Add(objPart);
}
for (int k = 1; k < list3.Count - 1; k++)
{
objPart.Add(list, list3[0], list3[k], list3[k + 1]);
}
}
}
}
return new ObjModel(list2);
}
private static int VertexIndex(string corner, int count)
{
int num = int.Parse(corner.Split(new char[1] { '/' })[0], CultureInfo.InvariantCulture);
if (num < 0)
{
return count + num;
}
return num - 1;
}
}
internal static class Placement
{
public const int Salt = 1297370673;
public const float BiomeMargin = 0.5f;
public const int MaxCount = 16;
public const float StandDistance = 2.5f;
public const float StandHeight = 1f;
public const float AimAboveBase = 0.7f;
public static int ClampCount(int count)
{
if (count < 0)
{
return 0;
}
if (count > 16)
{
return 16;
}
return count;
}
public static float HalfExtent(int mapChunkSize, float worldScaleAfterBuffer)
{
return (float)mapChunkSize / 2f * worldScaleAfterBuffer;
}
public static (double X, double Z) Candidate(int seed, int attempt, float halfExtent)
{
double item = (Hash01.Of(seed, 1297370673, 2 * attempt) * 2.0 - 1.0) * (double)halfExtent;
double item2 = (Hash01.Of(seed, 1297370673, 2 * attempt + 1) * 2.0 - 1.0) * (double)halfExtent;
return (X: item, Z: item2);
}
public static bool FarEnough(IReadOnlyList<(double X, double Z)> placed, double x, double z, float minSpacing)
{
double num = (double)minSpacing * (double)minSpacing;
for (int i = 0; i < placed.Count; i++)
{
double num2 = placed[i].X - x;
double num3 = placed[i].Z - z;
if (num2 * num2 + num3 * num3 < num)
{
return false;
}
}
return true;
}
public static bool InsideGrass(bool hit, bool below, bool above)
{
return hit && below && above;
}
public static void StandAndAim(float x, float y, float z, out float sx, out float sy, out float sz, out float ax, out float ay, out float az)
{
sx = x + 2.5f;
sy = y + 1f;
sz = z;
ax = x;
ay = y + 0.7f;
az = z;
}
}
internal static class PlayerNames
{
public static string Of(int clientId)
{
Dictionary<int, PlayerManager> players = GameManager.players;
if (players != null && players.TryGetValue(clientId, out var value) && (Object)(object)value != (Object)null && !string.IsNullOrEmpty(value.username))
{
return value.username;
}
return "Someone";
}
}
internal static class ShrineIds
{
public const int Base = 1400000000;
public const int Span = 1000000;
public static int For(int attempt)
{
return 1400000000 + attempt;
}
public static bool IsOurs(int id)
{
if (id >= 1400000000)
{
return id < 1401000000;
}
return false;
}
}
internal static class ShrineMesh
{
public const string ResourceName = "MuckShrineOfTheMountain.shrine.obj";
private static Mesh[] meshes;
private static string[] materials;
private static bool failed;
private static Shader shader;
private static bool shaderLogged;
public static Material[] Attach(Transform parent)
{
//IL_0042: 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_004f: 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_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Expected O, but got Unknown
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
Ensure();
if (failed || meshes == null)
{
return (Material[])(object)new Material[0];
}
Shader val = ShaderNow();
Material[] array = (Material[])(object)new Material[meshes.Length];
for (int i = 0; i < meshes.Length; i++)
{
GameObject val2 = new GameObject("ShrineOfTheMountain_" + materials[i])
{
layer = 10
};
val2.transform.SetParent(parent, false);
val2.AddComponent<MeshFilter>().sharedMesh = meshes[i];
MeshRenderer val3 = val2.AddComponent<MeshRenderer>();
if (!((Object)(object)val == (Object)null))
{
Material val4 = new Material(val);
val4.color = ShrinePalette.ColorOf(materials[i]);
array[i] = val4;
((Renderer)val3).sharedMaterial = val4;
}
}
return array;
}
public static string ReadResource()
{
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MuckShrineOfTheMountain.shrine.obj");
if (stream == null)
{
return null;
}
using StreamReader streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
}
private static void Ensure()
{
//IL_00a4: 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)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Expected O, but got Unknown
//IL_011e: Unknown result type (might be due to invalid IL or missing references)
if (meshes != null || failed)
{
return;
}
string text = ReadResource();
if (string.IsNullOrEmpty(text))
{
failed = true;
MountainPlugin.Log.LogError((object)"Shrine of the Mountain mesh is missing from the assembly.");
return;
}
ObjModel objModel = ObjModel.Parse(text);
if (objModel.Triangles <= 0)
{
failed = true;
MountainPlugin.Log.LogError((object)"Shrine of the Mountain mesh parsed no triangles.");
return;
}
meshes = (Mesh[])(object)new Mesh[objModel.Parts.Count];
materials = new string[objModel.Parts.Count];
for (int i = 0; i < objModel.Parts.Count; i++)
{
ObjPart objPart = objModel.Parts[i];
materials[i] = objPart.Material;
Mesh val = new Mesh
{
name = "ShrineOfTheMountain_" + objPart.Material,
hideFlags = (HideFlags)61
};
List<Vector3> list = new List<Vector3>(objPart.Positions.Count / 3);
List<int> list2 = new List<int>(objPart.Triangles * 3);
for (int j = 0; j < objPart.Positions.Count; j += 3)
{
list.Add(new Vector3(objPart.Positions[j], objPart.Positions[j + 1], objPart.Positions[j + 2]));
list2.Add(list.Count - 1);
}
val.SetVertices(list);
val.SetTriangles(list2, 0);
val.RecalculateNormals();
val.RecalculateBounds();
meshes[i] = val;
}
}
private static Shader ShaderNow()
{
if ((Object)(object)shader != (Object)null)
{
return shader;
}
shader = Shader.Find("Standard");
if ((Object)(object)shader == (Object)null)
{
shader = Shader.Find("Diffuse");
}
if ((Object)(object)shader == (Object)null)
{
shader = Shader.Find("Legacy Shaders/Diffuse");
}
if ((Object)(object)shader == (Object)null && !shaderLogged)
{
shaderLogged = true;
MountainPlugin.Log.LogWarning((object)"Shrine of the Mountain found no lit shader. The collider is still there.");
}
return shader;
}
}
internal static class ShrinePalette
{
private static readonly Dictionary<string, float[]> Colors = new Dictionary<string, float[]>
{
{
"stone",
new float[3] { 0.62f, 0.58f, 0.52f }
},
{
"stone_dark",
new float[3] { 0.36f, 0.33f, 0.3f }
},
{
"rock",
new float[3] { 0.42f, 0.48f, 0.52f }
},
{
"snow",
new float[3] { 0.93f, 0.94f, 0.9f }
},
{
"gold",
new float[3] { 0.95f, 0.72f, 0.22f }
}
};
public static IEnumerable<string> Names => Colors.Keys;
public static bool Knows(string material)
{
if (material != null)
{
return Colors.ContainsKey(material);
}
return false;
}
public static Color ColorOf(string material)
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
if (material != null && Colors.TryGetValue(material, out var value))
{
return new Color(value[0], value[1], value[2]);
}
return new Color(0.5f, 0.5f, 0.5f);
}
}
internal sealed class ShrineUses
{
public int Left { get; private set; }
public bool Spent => Left <= 0;
public ShrineUses(int uses)
{
Left = ((uses >= 0) ? uses : 0);
}
public bool TrySpend()
{
if (Left <= 0)
{
return false;
}
Left--;
return true;
}
}
[HarmonyPatch(typeof(MobSpawner), "ServerSpawnNewMob")]
internal static class SpawnBossPatch
{
private static FightBuff pending = FightBuff.None;
[HarmonyAfter(new string[] { "evan.muck.ror2enemies", "evan.muck.artifactofthenight" })]
[HarmonyPriority(100)]
private static void Prefix(ref float __3, BossType __5, int __6)
{
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Invalid comparison between Unknown and I4
pending = FightBuff.None;
try
{
if (LocalClient.serverOwner && MountainPlugin.Ledger != null && MountainPlugin.Cfg != null)
{
MountainPlugin.Ledger.ReleaseIfGone(MobStillThere(MountainPlugin.Ledger.ArmedMobId));
FightBuff fightBuff = MountainPlugin.Ledger.Peek((int)__5 == 1, __6, MountainPlugin.Cfg.BonusPerStack.Value, MountainPlugin.Cfg.BonusDropsPerStack.Value, MountainPlugin.Cfg.MaxFactor.Value);
if (fightBuff.Active)
{
__3 = MountainMath.ScaledMultiplier(__3, fightBuff.Factor);
pending = fightBuff;
}
}
}
catch (Exception ex)
{
pending = FightBuff.None;
MountainPlugin.Log.LogError((object)("Shrine of the Mountain boss scale failed: " + ex));
}
}
private static void Postfix(int __0)
{
FightBuff buff = pending;
pending = FightBuff.None;
if (!buff.Active)
{
return;
}
try
{
if (!MobStillThere(__0))
{
MountainPlugin.Log.LogWarning((object)("Shrine of the Mountain scaled a spawn but mob " + __0 + " was not in MobManager.mobs. Stacks were kept."));
return;
}
MountainPlugin.Ledger.Commit(__0, buff);
MountainPlugin.Log.LogInfo((object)MountainText.Armed(__0, buff.Factor, buff.Stacks));
}
catch (Exception ex)
{
MountainPlugin.Log.LogError((object)("Shrine of the Mountain could not arm boss " + __0 + ": " + ex));
}
}
private static bool MobStillThere(int mobId)
{
if (mobId < 0)
{
return false;
}
MobManager instance = MobManager.Instance;
if ((Object)(object)instance != (Object)null && instance.mobs != null && instance.mobs.TryGetValue(mobId, out var value))
{
return (Object)(object)value != (Object)null;
}
return false;
}
}
[HarmonyPatch(typeof(StructureSpawner), "Start")]
internal static class StructureSpawnerStartPatch
{
private static void Postfix(StructureSpawner __instance)
{
try
{
MountainWorld.PlaceOnce(__instance);
}
catch (Exception ex)
{
MountainPlugin.Log.LogError((object)("Shrine of the Mountain placement failed: " + ex));
}
}
}
internal enum PlaceKind
{
NewWorld,
NewMap,
AlreadyPlaced,
MissingManager
}
internal static class WorldGate
{
public static PlaceKind Decide(bool hasManager, bool sameManager, bool sameSeed)
{
if (!hasManager)
{
return PlaceKind.MissingManager;
}
if (sameManager && sameSeed)
{
return PlaceKind.AlreadyPlaced;
}
if (sameManager)
{
return PlaceKind.NewMap;
}
return PlaceKind.NewWorld;
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "MuckShrineOfTheMountain";
public const string PLUGIN_NAME = "Shrine of the Mountain";
public const string PLUGIN_VERSION = "0.1.0";
}
}