using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;
[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("MuckWaxQuail")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds a Wax Quail sprint-jump launch to Muck as a Blue powerup.")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+879fc7ac66fec639f2f84b4b7a65d5c61c9ed734")]
[assembly: AssemblyProduct("Wax Quail")]
[assembly: AssemblyTitle("MuckWaxQuail")]
[assembly: InternalsVisibleTo("MuckWaxQuail.Tests")]
[assembly: AssemblyVersion("0.2.1.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
internal sealed class IsReadOnlyAttribute : Attribute
{
}
}
namespace MuckWaxQuail
{
internal sealed class FlightMeter
{
private bool _airborne;
private float _start;
private float _originX;
private float _originZ;
public bool IsActive { get; private set; }
public void Begin(float now, float x, float z)
{
IsActive = true;
_airborne = false;
_start = now;
_originX = x;
_originZ = z;
}
public void MarkAirborne()
{
if (IsActive)
{
_airborne = true;
}
}
public void Reset()
{
IsActive = false;
_airborne = false;
}
public bool TryLand(float now, float x, float z, bool grounded, out float seconds, out float metres)
{
seconds = 0f;
metres = 0f;
if (!IsActive || !_airborne || !grounded)
{
return false;
}
float num = x - _originX;
float num2 = z - _originZ;
seconds = now - _start;
metres = (float)Math.Sqrt(num * num + num2 * num2);
Reset();
return true;
}
}
internal static class LaunchGate
{
public static bool ShouldArm(int stacks, bool wasGrounded, bool sprinting, float horizontalSpeed, float minRunSpeed, bool hasDirection, bool underwater)
{
if (stacks > 0 && wasGrounded && sprinting && hasDirection && !underwater)
{
return horizontalSpeed > minRunSpeed;
}
return false;
}
}
internal static class LaunchMath
{
public const float MinInput = 0.1f;
public const float MinAirtime = 0.1f;
public static bool Direction(float inputX, float inputY, float fwdX, float fwdZ, float rightX, float rightZ, out float dirX, out float dirZ)
{
float num = fwdX * inputY + rightX * inputX;
float num2 = fwdZ * inputY + rightZ * inputX;
float num3 = (float)Math.Sqrt(num * num + num2 * num2);
if (!(num3 >= 0.1f))
{
dirX = 0f;
dirZ = 0f;
return false;
}
dirX = num / num3;
dirZ = num2 / num3;
return true;
}
public static float AlongSpeed(float velX, float velZ, float dirX, float dirZ)
{
return velX * dirX + velZ * dirZ;
}
public static float Airtime(float velocityY, float heightAboveTakeoff, float mass, float extraGravity, float gravityY, float fallback)
{
float num = 0f - gravityY + extraGravity / mass;
if (!(num > 0f) || float.IsInfinity(num))
{
return fallback;
}
float num2 = ((heightAboveTakeoff > 0f) ? heightAboveTakeoff : 0f);
float num3 = (velocityY + (float)Math.Sqrt(velocityY * velocityY + 2f * num * num2)) / num;
if (!(num3 >= 0.1f) || float.IsInfinity(num3))
{
return fallback;
}
return num3;
}
public static float BoostSpeed(float distance, float airtime)
{
if (!(distance > 0f) || !(airtime > 0f))
{
return 0f;
}
return distance / airtime;
}
public static float Delta(float alongSpeed, float boostSpeed, float runCap)
{
if (!(boostSpeed > 0f))
{
return 0f;
}
float num = runCap + boostSpeed - alongSpeed;
if (!(num > 0f))
{
return 0f;
}
return Math.Min(num, boostSpeed);
}
}
internal readonly struct ArmedLaunch
{
public readonly float DirX;
public readonly float DirZ;
public readonly float Distance;
public readonly float RunCap;
public readonly float TakeoffY;
public ArmedLaunch(float dirX, float dirZ, float distance, float runCap, float takeoffY)
{
DirX = dirX;
DirZ = dirZ;
Distance = distance;
RunCap = runCap;
TakeoffY = takeoffY;
}
}
internal enum LaunchStep
{
None,
Wait,
Deliver,
Expired
}
internal sealed class PendingLaunch
{
private bool _armed;
private float _armedAt;
private float _dirX;
private float _dirZ;
private float _distance;
private float _runCap;
private float _takeoffY;
public bool IsArmed => _armed;
public void Arm(float now, float dirX, float dirZ, float distance, float runCap, float takeoffY)
{
_armed = true;
_armedAt = now;
_dirX = dirX;
_dirZ = dirZ;
_distance = distance;
_runCap = runCap;
_takeoffY = takeoffY;
}
public void Cancel()
{
_armed = false;
}
public LaunchStep Step(float now, bool grounded, float window, out ArmedLaunch launch)
{
launch = default(ArmedLaunch);
if (!_armed)
{
return LaunchStep.None;
}
float num = now - _armedAt;
if (num < 0f)
{
num = 0f;
}
if (num > window)
{
_armed = false;
return LaunchStep.Expired;
}
if (grounded)
{
return LaunchStep.Wait;
}
launch = new ArmedLaunch(_dirX, _dirZ, _distance, _runCap, _takeoffY);
_armed = false;
return LaunchStep.Deliver;
}
}
internal static class PickupArt
{
public const string ObjResource = "MuckWaxQuail.pickup.obj";
public const string JsonResource = "MuckWaxQuail.pickup.json";
public static void Apply(Powerup powerup, Powerup[] pool)
{
if ((Object)(object)powerup == (Object)null || (Object)(object)powerup.mesh != (Object)null)
{
return;
}
Powerup val = Donor(pool, powerup);
try
{
PickupMesh data = PickupMesh.From(PickupModel.Parse(Read("MuckWaxQuail.pickup.obj"), Read("MuckWaxQuail.pickup.json")));
Material val2 = (((Object)(object)val != (Object)null) ? val.material : null);
if ((Object)(object)val2 == (Object)null)
{
val2 = FallbackShader();
}
bool vertexColors = (Object)(object)val2 == (Object)null || !val2.HasProperty("_MainTex");
powerup.mesh = ToMesh(data, powerup.name, vertexColors);
powerup.material = ToMaterial(data, val2, powerup.name);
}
catch (Exception ex)
{
WaxQuailPlugin.Log.LogWarning((object)("Wax Quail pickup model was not applied (" + ex.Message + ")."));
if ((Object)(object)val != (Object)null && (Object)(object)val.mesh != (Object)null)
{
powerup.mesh = val.mesh;
powerup.material = val.material;
}
else
{
WaxQuailPlugin.Log.LogWarning((object)"No Blue powerup to borrow a pickup model from; dropped Wax Quails will be invisible.");
}
}
}
private static Powerup Donor(Powerup[] pool, Powerup self)
{
if (pool == null)
{
return null;
}
Powerup val = null;
foreach (Powerup val2 in pool)
{
if (!((Object)(object)val2 == (Object)null) && val2 != self)
{
if ((Object)(object)val2.material != (Object)null)
{
return val2;
}
if ((Object)(object)val == (Object)null && (Object)(object)val2.mesh != (Object)null)
{
val = val2;
}
}
}
return val;
}
private static string Read(string name)
{
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
if (stream == null)
{
throw new FileNotFoundException(name);
}
using StreamReader streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
}
private static Mesh ToMesh(PickupMesh data, string itemName, bool vertexColors)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_0127: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
Mesh val = new Mesh
{
name = itemName + " pickup",
hideFlags = (HideFlags)32
};
List<Vector3> list = new List<Vector3>(data.Positions.Length);
List<Vector3> list2 = new List<Vector3>(data.Normals.Length);
List<Vector2> list3 = new List<Vector2>(data.U.Length);
List<Color> list4 = new List<Color>(data.Positions.Length);
for (int i = 0; i < data.Positions.Length; i++)
{
list.Add(new Vector3(data.Positions[i].X, data.Positions[i].Y, data.Positions[i].Z));
list2.Add(new Vector3(data.Normals[i].X, data.Normals[i].Y, data.Normals[i].Z));
list3.Add(new Vector2(data.U[i], 0.5f));
list4.Add((Color)(vertexColors ? new Color(data.R[i], data.G[i], data.B[i], 1f) : Color.white));
}
val.SetVertices(list);
val.SetNormals(list2);
val.SetUVs(0, list3);
val.SetColors(list4);
val.SetTriangles(data.Triangles, 0);
val.RecalculateBounds();
return val;
}
private static Material FallbackShader()
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Expected O, but got Unknown
Shader val = Shader.Find("Standard");
if ((Object)(object)val == (Object)null)
{
val = Shader.Find("Diffuse");
}
if (!((Object)(object)val != (Object)null))
{
return null;
}
return new Material(val);
}
private static Material ToMaterial(PickupMesh data, Material source, string itemName)
{
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: 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_00a2: Expected O, but got Unknown
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: 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_0054: 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_0067: Expected O, but got Unknown
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Expected O, but got Unknown
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)source == (Object)null)
{
return null;
}
if (!source.HasProperty("_MainTex"))
{
MaterialSpec materialSpec = data.Palette[0];
return new Material(source)
{
name = itemName + " pickup",
color = new Color(materialSpec.R, materialSpec.G, materialSpec.B, 1f),
hideFlags = (HideFlags)32
};
}
Texture2D val = new Texture2D(data.Palette.Count, 1, (TextureFormat)4, false)
{
name = itemName + " palette",
filterMode = (FilterMode)0,
wrapMode = (TextureWrapMode)1,
hideFlags = (HideFlags)32
};
for (int i = 0; i < data.Palette.Count; i++)
{
MaterialSpec materialSpec2 = data.Palette[i];
val.SetPixel(i, 0, new Color(materialSpec2.R, materialSpec2.G, materialSpec2.B, 1f));
}
val.Apply(false, true);
Material val2 = new Material(source)
{
name = itemName + " pickup",
hideFlags = (HideFlags)32
};
val2.mainTexture = (Texture)(object)val;
val2.mainTextureScale = Vector2.one;
val2.mainTextureOffset = Vector2.zero;
if (val2.HasProperty("_Color"))
{
val2.color = Color.white;
}
return val2;
}
}
internal readonly struct Vec3
{
public readonly float X;
public readonly float Y;
public readonly float Z;
public Vec3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public static Vec3 operator -(Vec3 a, Vec3 b)
{
return new Vec3(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
}
internal readonly struct MaterialSpec
{
public readonly float R;
public readonly float G;
public readonly float B;
public readonly float Emission;
public MaterialSpec(float r, float g, float b, float emission)
{
R = r;
G = g;
B = b;
Emission = emission;
}
}
internal sealed class SubMesh
{
public readonly string Material;
public readonly int[] Triangles;
public SubMesh(string material, int[] triangles)
{
Material = material;
Triangles = triangles;
}
}
internal sealed class PickupModel
{
private sealed class ObjObject
{
public readonly List<Vec3> Positions = new List<Vec3>();
public readonly List<Vec3> Normals = new List<Vec3>();
public readonly List<string> MaterialOrder = new List<string>();
public readonly Dictionary<string, List<int>> Triangles = new Dictionary<string, List<int>>();
}
public readonly string Name;
public readonly Vec3[] Positions;
public readonly Vec3[] Normals;
public readonly IReadOnlyList<SubMesh> SubMeshes;
public readonly IReadOnlyDictionary<string, MaterialSpec> Materials;
public int TriangleCount => Positions.Length / 3;
public PickupModel(string name, Vec3[] positions, Vec3[] normals, IReadOnlyList<SubMesh> subMeshes, IReadOnlyDictionary<string, MaterialSpec> materials)
{
Name = name;
Positions = positions;
Normals = normals;
SubMeshes = subMeshes;
Materials = materials;
}
public static PickupModel Parse(string objText, string json)
{
if (!(JsonReader.Parse(json) is Dictionary<string, object> obj))
{
throw new FormatException("pickup json: top level is not an object.");
}
string text = Str(obj, "name");
Dictionary<string, MaterialSpec> dictionary = ReadMaterials(Obj(obj, "materials"));
Dictionary<string, ObjObject> dictionary2 = ReadObj(objText, text);
if (dictionary2.Count != 1)
{
throw new FormatException($"{text}: a pickup OBJ needs exactly one object, it has {dictionary2.Count}.");
}
ObjObject objObject = null;
foreach (ObjObject value in dictionary2.Values)
{
objObject = value;
}
List<SubMesh> list = new List<SubMesh>();
foreach (string item in objObject.MaterialOrder)
{
if (!dictionary.ContainsKey(item))
{
throw new FormatException(text + ": uses undefined material '" + item + "'.");
}
list.Add(new SubMesh(item, objObject.Triangles[item].ToArray()));
}
PickupModel pickupModel = new PickupModel(text, objObject.Positions.ToArray(), objObject.Normals.ToArray(), list, dictionary);
int num = (int)Num(obj, "tris");
if (pickupModel.TriangleCount != num)
{
throw new FormatException($"{text}: OBJ has {pickupModel.TriangleCount} triangles, the json says {num}.");
}
return pickupModel;
}
private static Dictionary<string, ObjObject> ReadObj(string text, string name)
{
if (text == null)
{
throw new FormatException(name + ": OBJ text is null.");
}
List<Vec3> list = new List<Vec3>();
List<Vec3> list2 = new List<Vec3>();
Dictionary<string, ObjObject> dictionary = new Dictionary<string, ObjObject>();
ObjObject objObject = null;
string text2 = null;
string[] array = text.Split(new char[1] { '\n' });
for (int i = 0; i < array.Length; i++)
{
string[] array2 = array[i].Trim().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
if (array2.Length == 0)
{
continue;
}
switch (array2[0])
{
case "v":
{
Vec3 vec2 = ReadVec(array2, name, i);
list.Add(new Vec3(0f - vec2.X, vec2.Y, vec2.Z));
break;
}
case "vn":
{
Vec3 vec = ReadVec(array2, name, i);
list2.Add(new Vec3(0f - vec.X, vec.Y, vec.Z));
break;
}
case "o":
{
string text3 = string.Join(" ", array2, 1, array2.Length - 1);
if (dictionary.ContainsKey(text3))
{
throw new FormatException(name + ": OBJ object " + text3 + " appears twice.");
}
objObject = new ObjObject();
dictionary.Add(text3, objObject);
text2 = null;
break;
}
case "usemtl":
text2 = ((array2.Length > 1) ? array2[1] : null);
break;
case "f":
if (objObject == null || text2 == null)
{
throw new FormatException($"{name}: OBJ line {i + 1} has a face outside an object or material.");
}
if (array2.Length != 4)
{
throw new FormatException($"{name}: OBJ line {i + 1} is not a triangle.");
}
AddTriangle(objObject, text2, list, list2, array2, name, i);
break;
}
}
return dictionary;
}
private static void AddTriangle(ObjObject obj, string material, List<Vec3> vertices, List<Vec3> normals, string[] tokens, string name, int line)
{
if (!obj.Triangles.TryGetValue(material, out var value))
{
value = new List<int>();
obj.Triangles.Add(material, value);
obj.MaterialOrder.Add(material);
}
int[] array = new int[3] { 1, 3, 2 };
foreach (int num in array)
{
string[] array2 = tokens[num].Split(new char[1] { '/' });
Vec3 item = vertices[Index(array2[0], vertices.Count, name, line)];
if (array2.Length < 3 || array2[2].Length == 0)
{
throw new FormatException($"{name}: OBJ line {line + 1} has a corner without a normal.");
}
value.Add(obj.Positions.Count);
obj.Positions.Add(item);
obj.Normals.Add(normals[Index(array2[2], normals.Count, name, line)]);
}
}
private static int Index(string raw, int count, string name, int line)
{
if (!int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result == 0)
{
throw new FormatException($"{name}: OBJ line {line + 1} has a bad index '{raw}'.");
}
int num = ((result > 0) ? (result - 1) : (count + result));
if (num < 0 || num >= count)
{
throw new FormatException($"{name}: OBJ line {line + 1} index {raw} is out of range.");
}
return num;
}
private static Vec3 ReadVec(string[] tokens, string name, int line)
{
if (tokens.Length < 4)
{
throw new FormatException($"{name}: OBJ line {line + 1} needs three numbers.");
}
return new Vec3(Float(tokens[1], name, line), Float(tokens[2], name, line), Float(tokens[3], name, line));
}
private static float Float(string raw, string name, int line)
{
if (!float.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
throw new FormatException($"{name}: OBJ line {line + 1} has a bad number '{raw}'.");
}
return result;
}
private static Dictionary<string, MaterialSpec> ReadMaterials(Dictionary<string, object> raw)
{
Dictionary<string, MaterialSpec> dictionary = new Dictionary<string, MaterialSpec>();
foreach (KeyValuePair<string, object> item in raw)
{
if (!(item.Value is Dictionary<string, object> obj))
{
throw new FormatException("pickup json: material " + item.Key + " is not an object.");
}
Vec3 vec = ReadVec3(obj, "rgb");
dictionary.Add(item.Key, new MaterialSpec(vec.X, vec.Y, vec.Z, (float)Num(obj, "emission")));
}
return dictionary;
}
private static Vec3 ReadVec3(Dictionary<string, object> obj, string key)
{
List<object> list = Arr(obj, key);
if (list.Count != 3 || !(list[0] is double num) || !(list[1] is double num2) || !(list[2] is double num3))
{
throw new FormatException("pickup json: '" + key + "' is not three numbers.");
}
return new Vec3((float)num, (float)num2, (float)num3);
}
private static object Field(Dictionary<string, object> obj, string key)
{
if (!obj.TryGetValue(key, out var value))
{
throw new FormatException("pickup json: missing '" + key + "'.");
}
return value;
}
private static string Str(Dictionary<string, object> obj, string key)
{
return (Field(obj, key) as string) ?? throw new FormatException("pickup json: '" + key + "' is not a string.");
}
private static double Num(Dictionary<string, object> obj, string key)
{
object obj2 = Field(obj, key);
if (obj2 is double)
{
return (double)obj2;
}
throw new FormatException("pickup json: '" + key + "' is not a number.");
}
private static List<object> Arr(Dictionary<string, object> obj, string key)
{
return (Field(obj, key) as List<object>) ?? throw new FormatException("pickup json: '" + key + "' is not an array.");
}
private static Dictionary<string, object> Obj(Dictionary<string, object> obj, string key)
{
return (Field(obj, key) as Dictionary<string, object>) ?? throw new FormatException("pickup json: '" + key + "' is not an object.");
}
}
internal sealed class PickupMesh
{
public readonly Vec3[] Positions;
public readonly Vec3[] Normals;
public readonly float[] U;
public readonly float[] R;
public readonly float[] G;
public readonly float[] B;
public readonly int[] Triangles;
public readonly IReadOnlyList<MaterialSpec> Palette;
private PickupMesh(Vec3[] positions, Vec3[] normals, float[] u, float[] r, float[] g, float[] b, int[] triangles, IReadOnlyList<MaterialSpec> palette)
{
Positions = positions;
Normals = normals;
U = u;
R = r;
G = g;
B = b;
Triangles = triangles;
Palette = palette;
}
public static PickupMesh From(PickupModel model)
{
Vec3 vec = new Vec3(float.MaxValue, float.MaxValue, float.MaxValue);
Vec3 vec2 = new Vec3(float.MinValue, float.MinValue, float.MinValue);
Vec3[] positions = model.Positions;
for (int i = 0; i < positions.Length; i++)
{
Vec3 vec3 = positions[i];
vec = new Vec3(Math.Min(vec.X, vec3.X), Math.Min(vec.Y, vec3.Y), Math.Min(vec.Z, vec3.Z));
vec2 = new Vec3(Math.Max(vec2.X, vec3.X), Math.Max(vec2.Y, vec3.Y), Math.Max(vec2.Z, vec3.Z));
}
Vec3 vec4 = new Vec3((vec.X + vec2.X) / 2f, (vec.Y + vec2.Y) / 2f, (vec.Z + vec2.Z) / 2f);
int count = model.SubMeshes.Count;
List<Vec3> list = new List<Vec3>(model.Positions.Length);
List<Vec3> list2 = new List<Vec3>(model.Positions.Length);
List<float> list3 = new List<float>(model.Positions.Length);
List<float> list4 = new List<float>(model.Positions.Length);
List<float> list5 = new List<float>(model.Positions.Length);
List<float> list6 = new List<float>(model.Positions.Length);
List<MaterialSpec> list7 = new List<MaterialSpec>(count);
for (int j = 0; j < count; j++)
{
SubMesh subMesh = model.SubMeshes[j];
MaterialSpec item = model.Materials[subMesh.Material];
list7.Add(item);
float item2 = ((float)j + 0.5f) / (float)count;
int[] triangles = subMesh.Triangles;
foreach (int num in triangles)
{
list.Add(model.Positions[num] - vec4);
list2.Add(model.Normals[num]);
list3.Add(item2);
list4.Add(item.R);
list5.Add(item.G);
list6.Add(item.B);
}
}
int[] array = new int[list.Count];
for (int k = 0; k < array.Length; k++)
{
array[k] = k;
}
return new PickupMesh(list.ToArray(), list2.ToArray(), list3.ToArray(), list4.ToArray(), list5.ToArray(), list6.ToArray(), array, list7);
}
}
internal sealed class JsonReader
{
private readonly string text;
private int pos;
private JsonReader(string text)
{
this.text = text;
}
public static object Parse(string text)
{
if (text == null)
{
throw new FormatException("JSON text is null.");
}
JsonReader jsonReader = new JsonReader(text);
object result = jsonReader.ReadValue();
jsonReader.SkipWhitespace();
if (jsonReader.pos != text.Length)
{
throw jsonReader.Error("trailing characters");
}
return result;
}
private object ReadValue()
{
SkipWhitespace();
if (pos >= text.Length)
{
throw Error("unexpected end");
}
char c = text[pos];
switch (c)
{
case '{':
return ReadObject();
case '[':
return ReadArray();
case '"':
return ReadString();
case 't':
Expect("true");
return true;
case 'f':
Expect("false");
return false;
case 'n':
Expect("null");
return null;
default:
switch (c)
{
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return ReadNumber();
default:
throw Error($"unexpected '{c}'");
}
}
}
private Dictionary<string, object> ReadObject()
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
pos++;
SkipWhitespace();
if (Peek() == '}')
{
pos++;
return dictionary;
}
while (true)
{
SkipWhitespace();
if (Peek() != '"')
{
throw Error("expected a key");
}
string key = ReadString();
SkipWhitespace();
if (Peek() != ':')
{
break;
}
pos++;
dictionary[key] = ReadValue();
SkipWhitespace();
char c = Peek();
pos++;
switch (c)
{
case ',':
break;
case '}':
return dictionary;
default:
throw Error("expected ',' or '}'");
}
}
throw Error("expected ':'");
}
private List<object> ReadArray()
{
List<object> list = new List<object>();
pos++;
SkipWhitespace();
if (Peek() == ']')
{
pos++;
return list;
}
while (true)
{
list.Add(ReadValue());
SkipWhitespace();
char c = Peek();
pos++;
switch (c)
{
case ',':
break;
case ']':
return list;
default:
throw Error("expected ',' or ']'");
}
}
}
private string ReadString()
{
pos++;
StringBuilder stringBuilder = new StringBuilder();
while (pos < text.Length)
{
char c = text[pos++];
switch (c)
{
case '"':
return stringBuilder.ToString();
default:
stringBuilder.Append(c);
break;
case '\\':
{
if (pos >= text.Length)
{
throw Error("unterminated escape");
}
char c2 = text[pos++];
switch (c2)
{
case '"':
stringBuilder.Append('"');
break;
case '\\':
stringBuilder.Append('\\');
break;
case '/':
stringBuilder.Append('/');
break;
case 'b':
stringBuilder.Append('\b');
break;
case 'f':
stringBuilder.Append('\f');
break;
case 'n':
stringBuilder.Append('\n');
break;
case 'r':
stringBuilder.Append('\r');
break;
case 't':
stringBuilder.Append('\t');
break;
case 'u':
if (pos + 4 > text.Length)
{
throw Error("short \\u escape");
}
stringBuilder.Append((char)int.Parse(text.Substring(pos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture));
pos += 4;
break;
default:
throw Error($"bad escape '\\{c2}'");
}
break;
}
}
}
throw Error("unterminated string");
}
private double ReadNumber()
{
int num = pos;
while (pos < this.text.Length && "+-0123456789.eE".IndexOf(this.text[pos]) >= 0)
{
pos++;
}
string text = this.text.Substring(num, pos - num);
if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
throw Error("bad number '" + text + "'");
}
return result;
}
private void Expect(string word)
{
if (string.CompareOrdinal(text, pos, word, 0, word.Length) != 0)
{
throw Error("expected '" + word + "'");
}
pos += word.Length;
}
private char Peek()
{
if (pos >= text.Length)
{
return '\0';
}
return text[pos];
}
private void SkipWhitespace()
{
while (pos < text.Length && char.IsWhiteSpace(text[pos]))
{
pos++;
}
}
private FormatException Error(string what)
{
return new FormatException($"JSON: {what} at offset {pos}.");
}
}
[HarmonyPatch(typeof(PlayerMovement), "Movement", new Type[]
{
typeof(float),
typeof(float)
})]
internal static class PlayerMovementMovementPatch
{
private static void Postfix(PlayerMovement __instance)
{
if (!WaxQuailLauncher.NeedsTick)
{
return;
}
try
{
WaxQuailLauncher.OnPhysicsTick(__instance);
}
catch (Exception arg)
{
WaxQuailPlugin.Log.LogError((object)$"Wax Quail launch failed: {arg}");
WaxQuailLauncher.Reset();
}
}
}
[HarmonyPatch(typeof(PlayerStatus), "Jump", new Type[] { })]
internal static class PlayerStatusJumpPatch
{
private static void Postfix()
{
try
{
WaxQuailLauncher.OnJumpSucceeded();
}
catch (Exception arg)
{
WaxQuailPlugin.Log.LogError((object)$"Wax Quail jump hook failed: {arg}");
}
}
}
internal readonly struct PowerupClaim
{
public readonly bool Claimed;
public readonly int Id;
public PowerupClaim(bool claimed, int id)
{
Claimed = claimed;
Id = id;
}
public static PowerupClaim Resolve(bool nameExists, bool existingIsOurs, int existingId, IEnumerable<int> existingIds)
{
if (nameExists && !existingIsOurs)
{
return new PowerupClaim(claimed: false, existingId);
}
if (nameExists)
{
return new PowerupClaim(claimed: true, existingId);
}
return new PowerupClaim(claimed: true, PowerupIds.Resolve(existingIds, nameExists: false, 0));
}
}
internal static class PowerupIds
{
public static int Resolve(IEnumerable<int> existingIds, bool nameExists, int existingId)
{
if (nameExists)
{
return existingId;
}
int num = 0;
bool flag = false;
foreach (int existingId2 in existingIds)
{
if (!flag || existingId2 > num)
{
num = existingId2;
flag = true;
}
}
if (!flag)
{
return 0;
}
return num + 1;
}
public static bool FitsInventory(int id, int countAfterAdd)
{
if (id >= 0)
{
return id < countAfterAdd;
}
return false;
}
}
internal static class PowerupPool
{
public static T[] Without<T>(T[] pool, T item) where T : class
{
if (pool == null)
{
return null;
}
int num = Array.IndexOf(pool, item);
if (num < 0)
{
return pool;
}
T[] array = new T[pool.Length - 1];
Array.Copy(pool, 0, array, 0, num);
Array.Copy(pool, num + 1, array, num, pool.Length - num - 1);
return array;
}
}
internal readonly struct QuailScaling
{
public readonly float Base;
public readonly float PerStack;
public QuailScaling(float baseDistance, float distancePerStack)
{
Base = baseDistance;
PerStack = distancePerStack;
}
public float DistanceFor(int stacks)
{
if (stacks <= 0)
{
return 0f;
}
float num = Base + PerStack * (float)(stacks - 1);
if (!(num > 0f))
{
return 0f;
}
return num;
}
public string Description()
{
return string.Format(CultureInfo.InvariantCulture, "Jumping while sprinting launches you forward about {0:0.#}m (+{1:0.#}m per stack).", Base, PerStack);
}
}
internal sealed class WaxQuailConfig
{
public readonly ConfigEntry<float> BaseDistance;
public readonly ConfigEntry<float> DistancePerStack;
public readonly ConfigEntry<float> FallbackAirtimeSeconds;
public readonly ConfigEntry<float> MinRunSpeed;
public readonly ConfigEntry<float> RunSpeedReference;
public readonly ConfigEntry<float> ArmWindowSeconds;
public readonly ConfigEntry<KeyboardShortcut> GiveWaxQuailKey;
public readonly ConfigEntry<bool> VerboseLogging;
public WaxQuailConfig(ConfigFile cfg)
{
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Expected O, but got Unknown
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Expected O, but got Unknown
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Expected O, but got Unknown
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Expected O, but got Unknown
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Expected O, but got Unknown
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
//IL_0157: Expected O, but got Unknown
//IL_0168: Unknown result type (might be due to invalid IL or missing references)
BaseDistance = cfg.Bind<float>("Wax Quail", "BaseDistance", 6f, new ConfigDescription("Metres of extra air travel with 1 stack. Tuning value, not measured from Muck. RoR2 uses 10.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
DistancePerStack = cfg.Bind<float>("Wax Quail", "DistancePerStack", 4f, new ConfigDescription("Extra metres per stack after the first. Tuning value, not measured from Muck. RoR2 uses 10.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
FallbackAirtimeSeconds = cfg.Bind<float>("Wax Quail", "FallbackAirtimeSeconds", 0.8f, new ConfigDescription("Seconds of air used to turn metres into launch speed only when the flight time cannot be worked out from your vertical speed, gravity and mass at take-off. Default 0.8.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
MinRunSpeed = cfg.Bind<float>("Wax Quail", "MinRunSpeed", 5f, new ConfigDescription("Horizontal speed, in m/s, required to arm a launch. Vanilla treats speed above 5 as running.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
RunSpeedReference = cfg.Bind<float>("Wax Quail", "RunSpeedReference", 13f, new ConfigDescription("Vanilla max run speed, in m/s, before the speed multiplier. Each launch tops horizontal speed up to this times your speed multiplier, plus the boost. Holding jump keeps that speed, because vanilla skips its grounded clamp while jump is held.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
ArmWindowSeconds = cfg.Bind<float>("Wax Quail", "ArmWindowSeconds", 0.3f, new ConfigDescription("How long a jump may stay grounded before the launch is dropped. The grounded grace after take-off is about 0.14s, so a window under 0.2s expires before you leave the ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 1f), Array.Empty<object>()));
GiveWaxQuailKey = cfg.Bind<KeyboardShortcut>("Debug", "GiveWaxQuailKey", KeyboardShortcut.Empty, "Press in-game to give yourself one Wax Quail stack (local test helper). Empty = disabled.");
VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log arming, the launch, expiry, and the measured flight time and distance.");
BaseDistance.SettingChanged += delegate
{
WaxQuailPowerup.RefreshDescription();
};
DistancePerStack.SettingChanged += delegate
{
WaxQuailPowerup.RefreshDescription();
};
}
public QuailScaling Scaling()
{
return new QuailScaling(BaseDistance.Value, DistancePerStack.Value);
}
}
internal static class WaxQuailLauncher
{
internal static readonly PendingLaunch Pending = new PendingLaunch();
internal static readonly FlightMeter Meter = new FlightMeter();
public static bool NeedsTick
{
get
{
if (!Pending.IsArmed)
{
return Meter.IsActive;
}
return true;
}
}
public static void Reset()
{
Pending.Cancel();
Meter.Reset();
}
public static void OnJumpSucceeded()
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: 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_0042: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_0161: Unknown result type (might be due to invalid IL or missing references)
//IL_0191: Unknown result type (might be due to invalid IL or missing references)
//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
PlayerMovement instance = PlayerMovement.Instance;
if ((Object)(object)instance == (Object)null || (Object)(object)PowerupInventory.Instance == (Object)null)
{
return;
}
int num = WaxQuailPowerup.LocalStacks();
if (num <= 0)
{
return;
}
Vector3 velocity = instance.GetVelocity();
float horizontalSpeed = Mathf.Sqrt(velocity.x * velocity.x + velocity.z * velocity.z);
PlayerInput instance2 = PlayerInput.Instance;
float x;
float y;
bool sprinting;
if ((Object)(object)instance2 != (Object)null)
{
x = instance2.x;
y = instance2.y;
sprinting = instance2.sprinting;
}
else
{
Vector2 input = instance.GetInput();
x = input.x;
y = input.y;
sprinting = instance.sprinting;
}
Transform orientation = instance.orientation;
if ((Object)(object)orientation == (Object)null)
{
return;
}
float dirX;
float dirZ;
bool hasDirection = LaunchMath.Direction(x, y, orientation.forward.x, orientation.forward.z, orientation.right.x, orientation.right.z, out dirX, out dirZ);
if (LaunchGate.ShouldArm(num, instance.grounded, sprinting, horizontalSpeed, WaxQuailPlugin.Cfg.MinRunSpeed.Value, hasDirection, instance.IsUnderWater()))
{
float num2 = WaxQuailPlugin.Cfg.Scaling().DistanceFor(num);
float num3 = WaxQuailPlugin.Cfg.RunSpeedReference.Value * PowerupInventory.Instance.GetSpeedMultiplier((int[])null);
Pending.Arm(Time.time, dirX, dirZ, num2, num3, instance.GetRb().position.y);
if (WaxQuailPlugin.Cfg.VerboseLogging.Value)
{
Meter.Begin(Time.time, ((Component)instance).transform.position.x, ((Component)instance).transform.position.z);
WaxQuailPlugin.Log.LogInfo((object)$"Wax Quail armed: +{num2:0.0} m toward ({dirX:0.00}, {dirZ:0.00}), run cap {num3:0.0}.");
}
}
}
public static void OnPhysicsTick(PlayerMovement pm)
{
//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
if (pm.dead || pm.IsUnderWater())
{
Reset();
return;
}
ArmedLaunch launch;
LaunchStep launchStep = Pending.Step(Time.time, pm.grounded, WaxQuailPlugin.Cfg.ArmWindowSeconds.Value, out launch);
switch (launchStep)
{
case LaunchStep.Deliver:
Deliver(pm, launch);
break;
case LaunchStep.Expired:
if (WaxQuailPlugin.Cfg.VerboseLogging.Value)
{
WaxQuailPlugin.Log.LogInfo((object)"Wax Quail launch expired before take-off.");
}
Meter.Reset();
break;
default:
throw new InvalidOperationException($"Unexpected launch step {launchStep}.");
case LaunchStep.None:
case LaunchStep.Wait:
break;
}
if (Meter.IsActive && Meter.TryLand(Time.time, ((Component)pm).transform.position.x, ((Component)pm).transform.position.z, pm.grounded, out var seconds, out var metres))
{
WaxQuailPlugin.Log.LogInfo((object)$"Wax Quail flight: {seconds:0.00}s, {metres:0.0}m");
}
}
private static void Deliver(PlayerMovement pm, ArmedLaunch launch)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: 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_0041: 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_0073: 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_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
Rigidbody rb = pm.GetRb();
Vector3 velocity = rb.velocity;
float num = LaunchMath.Airtime(velocity.y, rb.position.y - launch.TakeoffY, rb.mass, pm.extraGravity, rb.useGravity ? Physics.gravity.y : 0f, WaxQuailPlugin.Cfg.FallbackAirtimeSeconds.Value);
float num2 = LaunchMath.BoostSpeed(launch.Distance, num);
float num3 = LaunchMath.AlongSpeed(velocity.x, velocity.z, launch.DirX, launch.DirZ);
float num4 = LaunchMath.Delta(num3, num2, launch.RunCap);
if (num4 > 0f)
{
rb.AddForce(new Vector3(launch.DirX, 0f, launch.DirZ) * num4, (ForceMode)2);
}
Meter.MarkAirborne();
if (WaxQuailPlugin.Cfg.VerboseLogging.Value)
{
WaxQuailPlugin.Log.LogInfo((object)$"Wax Quail launch: +{num4:0.0} m/s (along {num3:0.0}, cap {launch.RunCap + num2:0.0}), predicted {num:0.00}s of air from here (vy {velocity.y:0.0}, mass {rb.mass:0.##}, extraGravity {pm.extraGravity:0.#})");
}
}
}
[BepInPlugin("evan.muck.waxquail", "Wax Quail", "0.2.1")]
public class WaxQuailPlugin : BaseUnityPlugin
{
public const string Guid = "evan.muck.waxquail";
internal static ManualLogSource Log;
internal static WaxQuailConfig Cfg;
private Harmony _harmony;
private void Awake()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
//IL_0181: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
Cfg = new WaxQuailConfig(((BaseUnityPlugin)this).Config);
_harmony = new Harmony("evan.muck.waxquail");
if (!TryPatch(typeof(PlayerStatusJumpPatch)) || !TryPatch(typeof(PlayerMovementMovementPatch)))
{
_harmony.UnpatchSelf();
return;
}
try
{
WaxQuailPowerup.Create();
}
catch (Exception arg)
{
_harmony.UnpatchSelf();
Log.LogError((object)$"Wax Quail disabled: could not create the powerup. The powerup will not drop. {arg}");
return;
}
if (!TryPatch(typeof(UnnumberWaxQuailPatch)) || !TryPatch(typeof(RegisterWaxQuailPatch)))
{
_harmony.UnpatchSelf();
return;
}
WaxQuailRunner.Ensure();
Log.LogInfo((object)("Wax Quail 0.2.1 loaded. " + $"BaseDistance={Cfg.BaseDistance.Value} DistancePerStack={Cfg.DistancePerStack.Value} " + $"FallbackAirtimeSeconds={Cfg.FallbackAirtimeSeconds.Value} " + $"MinRunSpeed={Cfg.MinRunSpeed.Value} RunSpeedReference={Cfg.RunSpeedReference.Value} " + $"ArmWindowSeconds={Cfg.ArmWindowSeconds.Value} GiveWaxQuailKey={Cfg.GiveWaxQuailKey.Value} " + $"VerboseLogging={Cfg.VerboseLogging.Value}. " + "Every player needs this mod and the same set of powerup mods."));
}
private bool TryPatch(Type patch)
{
try
{
_harmony.CreateClassProcessor(patch).Patch();
return true;
}
catch (Exception arg)
{
Log.LogError((object)$"Wax Quail disabled: could not patch {patch.Name}. The powerup will not drop. {arg}");
return false;
}
}
}
internal static class WaxQuailPowerup
{
public const string PowerupName = "Wax Quail";
private static FieldRef<PowerupInventory, int[]> _counts;
private static bool _countsFailed;
public static Powerup Definition { get; private set; }
public static int Id { get; internal set; } = -1;
public static void Create()
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
Powerup obj = ScriptableObject.CreateInstance<Powerup>();
obj.name = "Wax Quail";
((Object)obj).name = "Wax Quail";
obj.description = WaxQuailPlugin.Cfg.Scaling().Description();
obj.tier = (PowerTier)1;
obj.sprite = LoadIcon();
((Object)obj).hideFlags = (HideFlags)32;
Definition = obj;
}
public static void RefreshDescription()
{
if (!((Object)(object)Definition == (Object)null) && WaxQuailPlugin.Cfg != null)
{
Definition.description = WaxQuailPlugin.Cfg.Scaling().Description();
}
}
public static int LocalStacks()
{
try
{
PowerupInventory instance = PowerupInventory.Instance;
if ((Object)(object)instance == (Object)null || Id < 0)
{
return 0;
}
int[] array = Counts(instance);
return (array != null && Id < array.Length) ? array[Id] : 0;
}
catch (Exception e)
{
LogCountsFailure(e);
return 0;
}
}
public static void GiveOneLocal()
{
try
{
PowerupInventory instance = PowerupInventory.Instance;
if ((Object)(object)instance == (Object)null || Id < 0)
{
WaxQuailPlugin.Log.LogWarning((object)"Can't give Wax Quail: not in a game yet, or the powerup isn't registered.");
return;
}
int[] array = Counts(instance);
if (array == null || Id >= array.Length)
{
WaxQuailPlugin.Log.LogWarning((object)"Can't give Wax Quail: not in a game yet, or the powerup isn't registered.");
return;
}
array[Id]++;
if ((Object)(object)PowerupUI.Instance != (Object)null)
{
PowerupUI.Instance.AddPowerup(Id);
}
if ((Object)(object)PlayerStatus.Instance != (Object)null)
{
PlayerStatus.Instance.UpdateStats();
}
WaxQuailPlugin.Log.LogInfo((object)$"Debug: Wax Quail stacks now {array[Id]} (local only; the host's copy of your powerups is not updated).");
}
catch (Exception e)
{
LogCountsFailure(e);
}
}
private static int[] Counts(PowerupInventory inventory)
{
if (_counts == null)
{
_counts = AccessTools.FieldRefAccess<PowerupInventory, int[]>("powerups");
}
return _counts.Invoke(inventory);
}
private static void LogCountsFailure(Exception e)
{
if (!_countsFailed)
{
_countsFailed = true;
WaxQuailPlugin.Log.LogError((object)$"Wax Quail could not read powerup stacks: {e}");
}
}
private static Sprite LoadIcon()
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Expected O, but got Unknown
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
try
{
using Stream stream = typeof(WaxQuailPowerup).Assembly.GetManifestResourceStream("MuckWaxQuail.icon.png");
if (stream == null)
{
throw new FileNotFoundException("embedded icon.png missing");
}
using MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
if (!ImageConversion.LoadImage(val, memoryStream.ToArray()))
{
throw new InvalidDataException("icon.png failed to decode");
}
((Object)val).name = "WaxQuailIcon";
((Object)val).hideFlags = (HideFlags)32;
Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
((Object)obj).name = "WaxQuailIcon";
((Object)obj).hideFlags = (HideFlags)32;
return obj;
}
catch (Exception arg)
{
WaxQuailPlugin.Log.LogError((object)$"Failed to load Wax Quail icon: {arg}");
return null;
}
}
}
[HarmonyPatch(typeof(ItemManager), "InitAllPowerups", new Type[] { })]
internal static class UnnumberWaxQuailPatch
{
[HarmonyPriority(600)]
private static void Prefix(ItemManager __instance)
{
try
{
__instance.powerupsBlue = PowerupPool.Without(__instance.powerupsBlue, WaxQuailPowerup.Definition);
}
catch (Exception arg)
{
WaxQuailPlugin.Log.LogError((object)$"Wax Quail unnumber hook failed: {arg}");
}
}
}
[HarmonyPatch(typeof(ItemManager), "InitAllPowerups", new Type[] { })]
internal static class RegisterWaxQuailPatch
{
[HarmonyPriority(600)]
[HarmonyAfter(new string[] { "evan.muck.ukulele", "evan.muck.atgmissile", "evan.muck.crowbar", "evan.muck.stickybomb", "evan.muck.tritipdagger", "evan.muck.willothewisp", "evan.muck.devildeal", "evan.muck.shapedglass", "evan.muck.diosbestfriend" })]
private static void Postfix(ItemManager __instance)
{
try
{
Register(__instance);
}
catch (Exception arg)
{
WaxQuailPlugin.Log.LogError((object)$"Wax Quail registration failed: {arg}");
}
}
private static void Register(ItemManager manager)
{
WaxQuailPowerup.Id = -1;
Dictionary<int, Powerup> allPowerups = manager.allPowerups;
Dictionary<string, int> stringToPowerupId = manager.stringToPowerupId;
Powerup[] powerupsBlue = manager.powerupsBlue;
if (allPowerups == null || stringToPowerupId == null || powerupsBlue == null)
{
WaxQuailPlugin.Log.LogWarning((object)"Wax Quail could not register: ItemManager powerup tables are null.");
return;
}
Powerup definition = WaxQuailPowerup.Definition;
if ((Object)(object)definition == (Object)null)
{
WaxQuailPlugin.Log.LogError((object)"Wax Quail could not register: the powerup was not created.");
return;
}
int value;
bool flag = stringToPowerupId.TryGetValue("Wax Quail", out value);
Powerup value2;
bool existingIsOurs = flag && allPowerups.TryGetValue(value, out value2) && (Object)(object)value2 == (Object)(object)definition;
PowerupClaim powerupClaim = PowerupClaim.Resolve(flag, existingIsOurs, value, allPowerups.Keys);
if (!powerupClaim.Claimed)
{
WaxQuailPlugin.Log.LogError((object)$"The name Wax Quail is already taken (id {value}). Refusing to register.");
return;
}
int countAfterAdd = allPowerups.Count + ((!flag) ? 1 : 0);
if (!PowerupIds.FitsInventory(powerupClaim.Id, countAfterAdd))
{
WaxQuailPlugin.Log.LogError((object)$"Wax Quail id {powerupClaim.Id} is past the inventory size; refusing to register");
return;
}
PickupArt.Apply(definition, powerupsBlue);
if (!flag)
{
allPowerups.Add(powerupClaim.Id, definition);
stringToPowerupId.Add("Wax Quail", powerupClaim.Id);
}
definition.id = powerupClaim.Id;
if (!powerupsBlue.Contains(definition))
{
manager.powerupsBlue = CollectionExtensions.AddToArray<Powerup>(powerupsBlue, definition);
}
WaxQuailPowerup.Id = powerupClaim.Id;
WaxQuailPlugin.Log.LogInfo((object)$"Registered Wax Quail as Blue powerup id {powerupClaim.Id} ({allPowerups.Count} total)");
WaxQuailPlugin.Log.LogInfo((object)("Powerup ids: " + string.Join(", ", from kv in allPowerups
orderby kv.Key
select string.Format("{0}={1}", kv.Key, ((Object)(object)kv.Value != (Object)null) ? kv.Value.name : "?"))));
}
}
internal sealed class WaxQuailRunner : MonoBehaviour
{
private static WaxQuailRunner _instance;
public static void Ensure()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Expected O, but got Unknown
if (!((Object)(object)_instance != (Object)null))
{
GameObject val = new GameObject("MuckWaxQuail")
{
hideFlags = (HideFlags)61
};
Object.DontDestroyOnLoad((Object)val);
_instance = val.AddComponent<WaxQuailRunner>();
}
}
private void Awake()
{
SceneManager.activeSceneChanged += OnSceneChanged;
}
private void OnDestroy()
{
SceneManager.activeSceneChanged -= OnSceneChanged;
if ((Object)(object)_instance == (Object)(object)this)
{
_instance = null;
}
}
private static void OnSceneChanged(Scene previous, Scene next)
{
WaxQuailLauncher.Reset();
}
private void Update()
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
KeyboardShortcut value = WaxQuailPlugin.Cfg.GiveWaxQuailKey.Value;
if (((KeyboardShortcut)(ref value)).IsDown())
{
WaxQuailPowerup.GiveOneLocal();
}
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "MuckWaxQuail";
public const string PLUGIN_NAME = "Wax Quail";
public const string PLUGIN_VERSION = "0.2.1";
}
}