Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Gersemi v0.1.4
plugins/Gersemi.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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 System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GBV.Skeid; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; [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("Guys Being Vikings")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.4.0")] [assembly: AssemblyInformationalVersion("0.1.4+2087dfdd650271e6d7e91d296caf46c449767c69")] [assembly: AssemblyProduct("Gersemi")] [assembly: AssemblyTitle("Gersemi")] [assembly: AssemblyVersion("0.1.4.0")] [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 GBV.Skeid { internal sealed class ObjModel { internal sealed class Part { public string Name; public string Material; public float[] Vertices; public float[] Normals; public float[] Uvs; public int[] Triangles; public float MinX; public float MinY; public float MinZ; public float MaxX; public float MaxY; public float MaxZ; public int VertexCount { get { if (Vertices == null) { return 0; } return Vertices.Length / 3; } } public int TriangleCount { get { if (Triangles == null) { return 0; } return Triangles.Length / 3; } } public float CentreX => (MinX + MaxX) * 0.5f; public float CentreY => (MinY + MaxY) * 0.5f; public float CentreZ => (MinZ + MaxZ) * 0.5f; public float SizeX => MaxX - MinX; public float SizeY => MaxY - MinY; public float SizeZ => MaxZ - MinZ; public static void Tangent(float nx, float ny, float nz, float[] into, int at) { float num = 0f; float num2 = 0f; float num3 = 0f; float num4 = Math.Abs(nx); float num5 = Math.Abs(ny); float num6 = Math.Abs(nz); if (num4 <= num5 && num4 <= num6) { num = 1f; } else if (num5 <= num6) { num2 = 1f; } else { num3 = 1f; } float num7 = ny * num3 - nz * num2; float num8 = nz * num - nx * num3; float num9 = nx * num2 - ny * num; float num10 = (float)Math.Sqrt(num7 * num7 + num8 * num8 + num9 * num9); if (num10 < 1E-06f || float.IsNaN(num10) || float.IsInfinity(num10)) { num7 = 1f; num8 = 0f; num9 = 0f; num10 = 1f; } into[at] = num7 / num10; into[at + 1] = num8 / num10; into[at + 2] = num9 / num10; into[at + 3] = 1f; } public bool LooksLikeAWheel() { if (!IsWheelWord(Name)) { return IsWheelWord(Material); } return true; } public Part SideOf(bool negative) { if (Triangles == null || Vertices == null) { return null; } Dictionary<int, int> remap = new Dictionary<int, int>(); List<float> list = new List<float>(); List<float> list2 = ((Normals != null) ? new List<float>() : null); List<float> list3 = ((Uvs != null) ? new List<float>() : null); List<int> list4 = new List<int>(); for (int i = 0; i + 2 < Triangles.Length; i += 3) { int num = Triangles[i]; int num2 = Triangles[i + 1]; int num3 = Triangles[i + 2]; if ((Vertices[num * 3] + Vertices[num2 * 3] + Vertices[num3 * 3]) / 3f < 0f == negative) { list4.Add(Copy(num, remap, list, list2, list3)); list4.Add(Copy(num2, remap, list, list2, list3)); list4.Add(Copy(num3, remap, list, list2, list3)); } } if (list4.Count == 0) { return null; } Part part = new Part(); part.Name = Name + (negative ? "_L" : "_R"); part.Material = Material; part.Vertices = list.ToArray(); part.Normals = list2?.ToArray(); part.Uvs = list3?.ToArray(); part.Triangles = list4.ToArray(); part.Recalculate(); return part; } private int Copy(int index, Dictionary<int, int> remap, List<float> vs, List<float> ns, List<float> uvs) { if (remap.TryGetValue(index, out var value)) { return value; } int result = (remap[index] = vs.Count / 3); vs.Add(Vertices[index * 3]); vs.Add(Vertices[index * 3 + 1]); vs.Add(Vertices[index * 3 + 2]); if (ns != null) { ns.Add(Normals[index * 3]); ns.Add(Normals[index * 3 + 1]); ns.Add(Normals[index * 3 + 2]); } if (uvs != null) { uvs.Add(Uvs[index * 2]); uvs.Add(Uvs[index * 2 + 1]); } return result; } internal void Recalculate() { MinX = (MinY = (MinZ = float.MaxValue)); MaxX = (MaxY = (MaxZ = float.MinValue)); for (int i = 0; i + 2 < Vertices.Length; i += 3) { float num = Vertices[i]; float num2 = Vertices[i + 1]; float num3 = Vertices[i + 2]; if (num < MinX) { MinX = num; } if (num2 < MinY) { MinY = num2; } if (num3 < MinZ) { MinZ = num3; } if (num > MaxX) { MaxX = num; } if (num2 > MaxY) { MaxY = num2; } if (num3 > MaxZ) { MaxZ = num3; } } } private static bool IsWheelWord(string s) { if (string.IsNullOrEmpty(s)) { return false; } string text = s.ToLowerInvariant(); if (text.Contains("steer") || text.Contains("ster")) { return false; } if (!text.Contains("wheel") && !text.Contains("tyre") && !text.Contains("tire") && !text.Contains("rim")) { return text.Contains("hubcap"); } return true; } } private sealed class Builder { public readonly string Name; public string Material; public readonly List<int> Triangles = new List<int>(); private readonly Dictionary<long, int> _map = new Dictionary<long, int>(); private readonly List<float> _vs = new List<float>(); private readonly List<float> _vns = new List<float>(); private readonly List<float> _vts = new List<float>(); private bool _anyNormals; private bool _anyUvs; public Builder(string name) { Name = name; } public int Resolve(string text, int from, int stop, List<float> vs, List<float> vns, List<float> vts, int lineNumber) { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = from; while (num5 <= stop) { int i; for (i = num5; i < stop && text[i] != '/'; i++) { } if (i > num5) { if (!int.TryParse(text.Substring(num5, i - num5), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { throw new ObjException("line " + lineNumber + " has a face corner that is not a number"); } switch (num4) { case 0: num = result; break; case 1: num2 = result; break; case 2: num3 = result; break; } } num4++; if (i >= stop) { break; } num5 = i + 1; } int num6 = vs.Count / 3; int num7 = vts.Count / 2; int num8 = vns.Count / 3; num = ((num < 0) ? (num6 + num) : (num - 1)); num2 = ((num2 < 0) ? (num7 + num2) : (num2 - 1)); num3 = ((num3 < 0) ? (num8 + num3) : (num3 - 1)); if (num < 0 || num >= num6) { throw new ObjException("line " + lineNumber + " refers to vertex " + (num + 1) + ", and the file only has " + num6); } bool flag = num2 >= 0 && num2 < num7; bool flag2 = num3 >= 0 && num3 < num8; long key = ((long)(num & 0x1FFFFF) << 42) | ((long)((flag ? num2 : 2097151) & 0x1FFFFF) << 21) | ((flag2 ? num3 : 2097151) & 0x1FFFFF); if (_map.TryGetValue(key, out var value)) { return value; } value = _vs.Count / 3; _map[key] = value; _vs.Add(vs[num * 3]); _vs.Add(vs[num * 3 + 1]); _vs.Add(vs[num * 3 + 2]); if (flag2) { _anyNormals = true; _vns.Add(vns[num3 * 3]); _vns.Add(vns[num3 * 3 + 1]); _vns.Add(vns[num3 * 3 + 2]); } else { _vns.Add(0f); _vns.Add(1f); _vns.Add(0f); } if (flag) { _anyUvs = true; _vts.Add(vts[num2 * 2]); _vts.Add(vts[num2 * 2 + 1]); } else { _vts.Add(0f); _vts.Add(0f); } return value; } public Part Build() { Part part = new Part(); part.Name = Name; part.Material = Material; part.Vertices = _vs.ToArray(); part.Triangles = Triangles.ToArray(); part.Normals = (_anyNormals ? _vns.ToArray() : null); part.Uvs = (_anyUvs ? _vts.ToArray() : null); part.Recalculate(); return part; } } public float MinX; public float MinY; public float MinZ; public float MaxX; public float MaxY; public float MaxZ; internal const int MaxTriangles = 200000; private const int MaxCorners = 64; private static readonly int[] FaceBuffer = new int[64]; internal List<Part> Parts { get; } = new List<Part>(); public float SizeX => MaxX - MinX; public float SizeY => MaxY - MinY; public float SizeZ => MaxZ - MinZ; public int VertexCount { get { int num = 0; for (int i = 0; i < Parts.Count; i++) { num += Parts[i].VertexCount; } return num; } } public int TriangleCount { get { int num = 0; for (int i = 0; i < Parts.Count; i++) { num += Parts[i].TriangleCount; } return num; } } internal List<Part> Wheels() { List<Part> list = new List<Part>(); for (int i = 0; i < Parts.Count; i++) { if (Parts[i].LooksLikeAWheel()) { list.Add(Parts[i]); } } return list; } internal List<Part> Body() { List<Part> list = new List<Part>(); for (int i = 0; i < Parts.Count; i++) { if (!Parts[i].LooksLikeAWheel()) { list.Add(Parts[i]); } } return list; } internal static ObjModel Parse(string text) { if (string.IsNullOrEmpty(text)) { throw new ObjException("the file is empty"); } ObjModel objModel = new ObjModel(); List<float> vs = new List<float>(); List<float> vns = new List<float>(); List<float> vts = new List<float>(); Builder current = null; string pendingMaterial = null; int num = 0; int totalTriangles = 0; int num2 = 0; while (num2 <= text.Length) { int num3 = text.IndexOf('\n', num2); if (num3 < 0) { num3 = text.Length; } int num4 = num3; if (num4 > num2 && text[num4 - 1] == '\r') { num4--; } num++; ReadLine(text, num2, num4, vs, vns, vts, ref current, ref pendingMaterial, ref totalTriangles, objModel, num); if (num3 >= text.Length) { break; } num2 = num3 + 1; } Flush(current, objModel); if (objModel.Parts.Count == 0 || objModel.TriangleCount == 0) { throw new ObjException("it has no triangles. Faces are the f lines in an OBJ; a file with only v lines is a point cloud, not a model"); } objModel.Recalculate(); return objModel; } private static void ReadLine(string text, int start, int stop, List<float> vs, List<float> vns, List<float> vts, ref Builder current, ref string pendingMaterial, ref int totalTriangles, ObjModel model, int lineNumber) { while (start < stop && (text[start] == ' ' || text[start] == '\t')) { start++; } if (start >= stop || text[start] == '#') { return; } int i; for (i = start; i < stop && text[i] != ' ' && text[i] != '\t'; i++) { } int num = i - start; if (num == 1 && text[start] == 'v') { ReadFloats(text, i, stop, vs, 3, lineNumber, "v", negateThird: true); } else if (num == 2 && text[start] == 'v' && text[start + 1] == 'n') { ReadFloats(text, i, stop, vns, 3, lineNumber, "vn", negateThird: true); } else if (num == 2 && text[start] == 'v' && text[start + 1] == 't') { ReadFloats(text, i, stop, vts, 2, lineNumber, "vt", negateThird: false); } else if (num == 1 && text[start] == 'f') { if (current == null) { current = new Builder("car"); current.Material = pendingMaterial; } AddFace(text, i, stop, vs, vns, vts, current, ref totalTriangles, lineNumber); } else if (num == 1 && (text[start] == 'o' || text[start] == 'g')) { string text2 = Trimmed(text, i, stop); if (text2.Length != 0) { Flush(current, model); current = new Builder(text2) { Material = pendingMaterial }; } } else { if (num != 6 || string.CompareOrdinal(text, start, "usemtl", 0, 6) != 0) { return; } string text3 = Trimmed(text, i, stop); if (!(text3 == pendingMaterial)) { pendingMaterial = text3; if (current != null && current.Triangles.Count > 0) { Flush(current, model); current = new Builder(current.Name) { Material = text3 }; } else if (current != null) { current.Material = text3; } } } } private static void ReadFloats(string text, int from, int stop, List<float> into, int count, int lineNumber, string what, bool negateThird) { int num = 0; int i = from; while (num < count) { for (; i < stop && (text[i] == ' ' || text[i] == '\t'); i++) { } if (i >= stop) { break; } int j; for (j = i; j < stop && text[j] != ' ' && text[j] != '\t'; j++) { } if (!float.TryParse(text.Substring(i, j - i), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { throw new ObjException("line " + lineNumber + " is a " + what + " with something that is not a number in it"); } if (float.IsNaN(result)) { result = 0f; } into.Add(result); num++; i = j; } if (num < count) { throw new ObjException("line " + lineNumber + " is a " + what + " with only " + num + " numbers; " + count + " are needed"); } if (negateThird && count == 3) { into[into.Count - 1] = 0f - into[into.Count - 1]; } if (count == 2) { into[into.Count - 1] = 1f - into[into.Count - 1]; } } private static void AddFace(string text, int from, int stop, List<float> vs, List<float> vns, List<float> vts, Builder part, ref int totalTriangles, int lineNumber) { int[] faceBuffer = FaceBuffer; int num = 0; int i = from; while (i < stop && num < 64) { for (; i < stop && (text[i] == ' ' || text[i] == '\t'); i++) { } if (i >= stop) { break; } int j; for (j = i; j < stop && text[j] != ' ' && text[j] != '\t'; j++) { } faceBuffer[num++] = part.Resolve(text, i, j, vs, vns, vts, lineNumber); i = j; } if (num < 3) { return; } for (int k = 1; k + 1 < num; k++) { if (++totalTriangles > 200000) { throw new ObjException("it has more than " + 200000 + " triangles, which is far more than a car needs and more than this loads"); } part.Triangles.Add(faceBuffer[0]); part.Triangles.Add(faceBuffer[k + 1]); part.Triangles.Add(faceBuffer[k]); } } private static void Flush(Builder builder, ObjModel model) { if (builder != null && builder.Triangles.Count != 0) { model.Parts.Add(builder.Build()); } } private static string Trimmed(string text, int from, int stop) { while (from < stop && (text[from] == ' ' || text[from] == '\t')) { from++; } while (stop > from && (text[stop - 1] == ' ' || text[stop - 1] == '\t')) { stop--; } if (from >= stop) { return string.Empty; } return text.Substring(from, stop - from); } internal void Recalculate() { MinX = (MinY = (MinZ = float.MaxValue)); MaxX = (MaxY = (MaxZ = float.MinValue)); for (int i = 0; i < Parts.Count; i++) { Part part = Parts[i]; if (part.MinX < MinX) { MinX = part.MinX; } if (part.MinY < MinY) { MinY = part.MinY; } if (part.MinZ < MinZ) { MinZ = part.MinZ; } if (part.MaxX > MaxX) { MaxX = part.MaxX; } if (part.MaxY > MaxY) { MaxY = part.MaxY; } if (part.MaxZ > MaxZ) { MaxZ = part.MaxZ; } } } } internal sealed class ObjException : Exception { internal ObjException(string message) : base(message) { } } internal static class GlbModel { internal struct Shade { public float R; public float G; public float B; public float A; public bool Blend; public float Metallic; public float Roughness; public float Smoothness => 1f - Roughness; } private struct Span { public byte[] Data; public int Offset; public int Stride; } private const uint Magic = 1179937895u; private const uint ChunkJson = 1313821514u; private const uint ChunkBinary = 5130562u; private const int Byte = 5120; private const int UnsignedByte = 5121; private const int Short = 5122; private const int UnsignedShort = 5123; private const int UnsignedInt = 5125; private const int Float = 5126; internal static ObjModel Parse(byte[] bytes, out Dictionary<string, byte[]> textures, out Dictionary<string, Shade> shades) { shades = new Dictionary<string, Shade>(StringComparer.Ordinal); ObjModel result = Parse(bytes, out textures); try { Split(bytes, out var json, out var _); if (json == null) { return result; } Json json2 = Json.Parse(json); for (int i = 0; i < json2["materials"].Count; i++) { Json json3 = json2["materials"][i]; Json json4 = json3["pbrMetallicRoughness"]["baseColorFactor"]; string asText = json3["alphaMode"].AsText; Json json5 = json3["pbrMetallicRoughness"]; Json json6 = json5["metallicFactor"]; Json json7 = json5["roughnessFactor"]; Shade value = new Shade { R = ((json4.Count > 0) ? json4[0].AsFloat : 1f), G = ((json4.Count > 1) ? json4[1].AsFloat : 1f), B = ((json4.Count > 2) ? json4[2].AsFloat : 1f), A = ((json4.Count > 3) ? json4[3].AsFloat : 1f), Metallic = ((json6.Type == Json.Kind.Number) ? json6.AsFloat : 1f), Roughness = ((json7.Type == Json.Kind.Number) ? json7.AsFloat : 1f), Blend = string.Equals(asText, "BLEND", StringComparison.OrdinalIgnoreCase) }; shades[MaterialName(json2, i)] = value; } } catch { } return result; } internal static ObjModel Parse(byte[] bytes, out Dictionary<string, byte[]> textures) { textures = new Dictionary<string, byte[]>(StringComparer.Ordinal); ObjModel result = Parse(bytes); try { Split(bytes, out var json, out var binary); if (json == null) { return result; } Json json2 = Json.Parse(json); for (int i = 0; i < json2["materials"].Count; i++) { int num = json2["materials"][i]["pbrMetallicRoughness"]["baseColorTexture"]["index"].AsInt(); if (num < 0) { continue; } int num2 = json2["textures"][num]["source"].AsInt(); if (num2 < 0) { continue; } int num3 = json2["images"][num2]["bufferView"].AsInt(); if (num3 >= 0 && binary != null) { int num4 = json2["bufferViews"][num3]["byteOffset"].AsInt(0); int num5 = json2["bufferViews"][num3]["byteLength"].AsInt(0); if (num5 > 0 && num4 + num5 <= binary.Length) { byte[] array = new byte[num5]; Array.Copy(binary, num4, array, 0, num5); textures[MaterialName(json2, i)] = array; } } } } catch { } return result; } internal static ObjModel Parse(byte[] bytes) { if (bytes == null || bytes.Length < 12) { throw new ObjException("the model file is empty or far too small to be a GLB."); } if (ReadUInt(bytes, 0) != 1179937895) { throw new ObjException("this is not a binary glTF file - it does not start with 'glTF'. A .gltf file is JSON and needs its .bin alongside it; use the .glb instead."); } Split(bytes, out var json, out var binary); if (string.IsNullOrEmpty(json)) { throw new ObjException("the GLB has no JSON chunk, so there is nothing to read."); } Json json2 = Json.Parse(json); if (json2["skins"].Count > 0) { throw new ObjException("this model is skinned to a rig, and the reader only handles plain meshes. Its vertices are in bind pose and would load in the wrong places."); } return Build(json2, binary); } private static void Split(byte[] bytes, out string json, out byte[] binary) { json = null; binary = null; int num = 12; while (num + 8 <= bytes.Length) { uint num2 = ReadUInt(bytes, num); uint num3 = ReadUInt(bytes, num + 4); int num4 = num + 8; if (num4 + num2 <= bytes.Length) { switch (num3) { case 1313821514u: json = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false).GetString(bytes, num4, (int)num2); break; case 5130562u: binary = new byte[num2]; Array.Copy(bytes, num4, binary, 0, (int)num2); break; } num = num4 + (int)num2; continue; } break; } } private static ObjModel Build(Json root, byte[] binary) { ObjModel objModel = new ObjModel(); Json json = root["nodes"]; Json json2 = root["meshes"]; Dictionary<int, float[]> dictionary = new Dictionary<int, float[]>(); List<int> list = new List<int>(); Json json3 = root["scenes"][root["scene"].AsInt(0)]["nodes"]; for (int i = 0; i < json3.Count; i++) { list.Add(json3[i].AsInt()); } if (list.Count == 0) { for (int j = 0; j < json.Count; j++) { list.Add(j); } } foreach (int item in list) { Walk(json, item, Identity(), dictionary); } foreach (KeyValuePair<int, float[]> item2 in dictionary) { Json json4 = json[item2.Key]; int num = json4["mesh"].AsInt(); if (num < 0) { continue; } string name = Clean(json4["name"].AsText) ?? ("node" + item2.Key); Json json5 = json2[num]["primitives"]; for (int k = 0; k < json5.Count; k++) { ObjModel.Part part = Primitive(root, binary, json5[k], name, item2.Value); if (part != null) { objModel.Parts.Add(part); } } } Bounds(objModel); return objModel; } private static ObjModel.Part Primitive(Json root, byte[] binary, Json primitive, string name, float[] transform) { if (primitive["mode"].AsInt(4) != 4) { return null; } float[] array = ReadFloats(root, binary, primitive["attributes"]["POSITION"], 3); if (array == null || array.Length == 0) { return null; } float[] array2 = ReadFloats(root, binary, primitive["attributes"]["NORMAL"], 3); float[] uvs = ReadFloats(root, binary, primitive["attributes"]["TEXCOORD_0"], 2); int[] triangles = ReadIndices(root, binary, primitive["indices"], array.Length / 3); ObjModel.Part part = new ObjModel.Part { Name = name, Material = MaterialName(root, primitive["material"].AsInt()), Vertices = new float[array.Length], Normals = ((array2 != null && array2.Length == array.Length) ? new float[array2.Length] : null), Uvs = uvs, Triangles = triangles }; for (int i = 0; i + 2 < array.Length; i += 3) { float num = array[i]; float num2 = array[i + 1]; float num3 = array[i + 2]; part.Vertices[i] = transform[0] * num + transform[4] * num2 + transform[8] * num3 + transform[12]; part.Vertices[i + 1] = transform[1] * num + transform[5] * num2 + transform[9] * num3 + transform[13]; part.Vertices[i + 2] = transform[2] * num + transform[6] * num2 + transform[10] * num3 + transform[14]; part.Vertices[i + 2] = 0f - part.Vertices[i + 2]; } if (part.Normals != null) { for (int j = 0; j + 2 < array2.Length; j += 3) { float num4 = array2[j]; float num5 = array2[j + 1]; float num6 = array2[j + 2]; float num7 = transform[0] * num4 + transform[4] * num5 + transform[8] * num6; float num8 = transform[1] * num4 + transform[5] * num5 + transform[9] * num6; float num9 = transform[2] * num4 + transform[6] * num5 + transform[10] * num6; float num10 = (float)Math.Sqrt(num7 * num7 + num8 * num8 + num9 * num9); if (num10 > 1E-09f) { num7 /= num10; num8 /= num10; num9 /= num10; } part.Normals[j] = num7; part.Normals[j + 1] = num8; part.Normals[j + 2] = 0f - num9; } } if (part.Uvs != null) { for (int k = 1; k < part.Uvs.Length; k += 2) { part.Uvs[k] = 1f - part.Uvs[k]; } } for (int l = 0; l + 2 < part.Triangles.Length; l += 3) { int num11 = part.Triangles[l + 1]; part.Triangles[l + 1] = part.Triangles[l + 2]; part.Triangles[l + 2] = num11; } part.Recalculate(); return part; } private static float[] ReadFloats(Json root, byte[] binary, Json accessorIndex, int size) { int num = accessorIndex.AsInt(); if (num < 0) { return null; } Json json = root["accessors"][num]; int num2 = json["count"].AsInt(0); int num3 = json["componentType"].AsInt(5126); if (num2 <= 0) { return null; } float[] array = new float[num2 * size]; Span span = View(root, binary, json); if (span.Data == null) { return null; } int num4 = ((span.Stride > 0) ? span.Stride : (size * SizeOf(num3))); for (int i = 0; i < num2; i++) { int num5 = span.Offset + i * num4; for (int j = 0; j < size; j++) { int num6 = num5 + j * SizeOf(num3); if (num6 + SizeOf(num3) > span.Data.Length) { return array; } array[i * size + j] = ((num3 == 5126) ? BitConverter.ToSingle(span.Data, num6) : Normalised(span.Data, num6, num3)); } } return array; } private static int[] ReadIndices(Json root, byte[] binary, Json accessorIndex, int vertices) { int num = accessorIndex.AsInt(); if (num < 0) { int[] array = new int[vertices]; for (int i = 0; i < vertices; i++) { array[i] = i; } return array; } Json json = root["accessors"][num]; int num2 = json["count"].AsInt(0); int num3 = json["componentType"].AsInt(5123); int[] array2 = new int[num2]; Span span = View(root, binary, json); if (span.Data == null) { return array2; } int num4 = SizeOf(num3); int num5 = ((span.Stride > 0) ? span.Stride : num4); for (int j = 0; j < num2; j++) { int num6 = span.Offset + j * num5; if (num6 + num4 > span.Data.Length) { break; } switch (num3) { case 5125: array2[j] = (int)BitConverter.ToUInt32(span.Data, num6); break; case 5123: array2[j] = BitConverter.ToUInt16(span.Data, num6); break; case 5121: array2[j] = span.Data[num6]; break; default: array2[j] = BitConverter.ToUInt16(span.Data, num6); break; } } return array2; } private static Span View(Json root, byte[] binary, Json accessor) { Span result = default(Span); int num = accessor["bufferView"].AsInt(); if (num < 0) { return result; } Json json = root["bufferViews"][num]; result.Data = binary; result.Offset = json["byteOffset"].AsInt(0) + accessor["byteOffset"].AsInt(0); result.Stride = json["byteStride"].AsInt(0); return result; } private static int SizeOf(int component) { switch (component) { case 5120: case 5121: return 1; case 5122: case 5123: return 2; default: return 4; } } private static float Normalised(byte[] data, int at, int component) { return component switch { 5120 => Math.Max((float)(sbyte)data[at] / 127f, -1f), 5121 => (float)(int)data[at] / 255f, 5122 => Math.Max((float)BitConverter.ToInt16(data, at) / 32767f, -1f), 5123 => (float)(int)BitConverter.ToUInt16(data, at) / 65535f, _ => BitConverter.ToSingle(data, at), }; } private static void Walk(Json nodes, int index, float[] parent, Dictionary<int, float[]> into) { if (index >= 0 && index < nodes.Count && !into.ContainsKey(index)) { Json json = nodes[index]; float[] parent2 = (into[index] = Multiply(parent, Local(json))); Json json2 = json["children"]; for (int i = 0; i < json2.Count; i++) { Walk(nodes, json2[i].AsInt(), parent2, into); } } } private static float[] Local(Json node) { Json json = node["matrix"]; if (json.Count == 16) { float[] array = new float[16]; for (int i = 0; i < 16; i++) { array[i] = json[i].AsFloat; } return array; } float asFloat = node["translation"][0].AsFloat; float asFloat2 = node["translation"][1].AsFloat; float asFloat3 = node["translation"][2].AsFloat; float asFloat4 = node["rotation"][0].AsFloat; float asFloat5 = node["rotation"][1].AsFloat; float asFloat6 = node["rotation"][2].AsFloat; float num = (node.Has("rotation") ? node["rotation"][3].AsFloat : 1f); float num2 = (node.Has("scale") ? node["scale"][0].AsFloat : 1f); float num3 = (node.Has("scale") ? node["scale"][1].AsFloat : 1f); float num4 = (node.Has("scale") ? node["scale"][2].AsFloat : 1f); return new float[16] { (1f - 2f * (asFloat5 * asFloat5 + asFloat6 * asFloat6)) * num2, 2f * (asFloat4 * asFloat5 + asFloat6 * num) * num2, 2f * (asFloat4 * asFloat6 - asFloat5 * num) * num2, 0f, 2f * (asFloat4 * asFloat5 - asFloat6 * num) * num3, (1f - 2f * (asFloat4 * asFloat4 + asFloat6 * asFloat6)) * num3, 2f * (asFloat5 * asFloat6 + asFloat4 * num) * num3, 0f, 2f * (asFloat4 * asFloat6 + asFloat5 * num) * num4, 2f * (asFloat5 * asFloat6 - asFloat4 * num) * num4, (1f - 2f * (asFloat4 * asFloat4 + asFloat5 * asFloat5)) * num4, 0f, asFloat, asFloat2, asFloat3, 1f }; } private static float[] Identity() { float[] array = new float[16]; array[0] = (array[5] = (array[10] = (array[15] = 1f))); return array; } private static float[] Multiply(float[] a, float[] b) { float[] array = new float[16]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { float num = 0f; for (int k = 0; k < 4; k++) { num += a[k * 4 + j] * b[i * 4 + k]; } array[i * 4 + j] = num; } } return array; } private static string MaterialName(Json root, int index) { if (index < 0) { return null; } string text = Clean(root["materials"][index]["name"].AsText); if (!string.IsNullOrEmpty(text)) { return text + "#" + index; } return "material_" + index; } private static string Clean(string text) { if (string.IsNullOrEmpty(text)) { return null; } StringBuilder stringBuilder = new StringBuilder(text.Length); foreach (char c in text) { if (c != '\ufffd' && !char.IsControl(c)) { stringBuilder.Append(c); } } string text2 = stringBuilder.ToString().Trim().TrimStart('.', '_', '-') .Trim(); if (text2.Length != 0) { return text2; } return null; } private static void Bounds(ObjModel model) { model.MinX = (model.MinY = (model.MinZ = float.MaxValue)); model.MaxX = (model.MaxY = (model.MaxZ = float.MinValue)); foreach (ObjModel.Part part in model.Parts) { if (part.MinX < model.MinX) { model.MinX = part.MinX; } if (part.MinY < model.MinY) { model.MinY = part.MinY; } if (part.MinZ < model.MinZ) { model.MinZ = part.MinZ; } if (part.MaxX > model.MaxX) { model.MaxX = part.MaxX; } if (part.MaxY > model.MaxY) { model.MaxY = part.MaxY; } if (part.MaxZ > model.MaxZ) { model.MaxZ = part.MaxZ; } } if (model.Parts.Count == 0) { model.MinX = (model.MinY = (model.MinZ = 0f)); model.MaxX = (model.MaxY = (model.MaxZ = 0f)); } } private static uint ReadUInt(byte[] bytes, int at) { return BitConverter.ToUInt32(bytes, at); } } internal sealed class Json { internal enum Kind { Null, Bool, Number, Text, Array, Object } private bool _bool; private double _number; private string _text; private List<Json> _array; private Dictionary<string, Json> _object; internal static readonly Json Nothing = new Json { Type = Kind.Null }; internal Kind Type { get; private set; } internal bool AsBool { get { if (Type == Kind.Bool) { return _bool; } return false; } } internal double AsNumber { get { if (Type != Kind.Number) { return 0.0; } return _number; } } internal float AsFloat => (float)AsNumber; internal string AsText { get { if (Type != Kind.Text) { return null; } return _text; } } internal int Count { get { if (Type != Kind.Array) { if (Type != Kind.Object) { return 0; } return _object.Count; } return _array.Count; } } internal Json this[string key] { get { if (Type != Kind.Object) { return Nothing; } if (!_object.TryGetValue(key, out var value)) { return Nothing; } return value; } } internal Json this[int index] { get { if (Type != Kind.Array || index < 0 || index >= _array.Count) { return Nothing; } return _array[index]; } } internal int AsInt(int fallback = -1) { if (Type != Kind.Number) { return fallback; } return (int)Math.Round(_number); } internal bool Has(string key) { if (Type == Kind.Object) { return _object.ContainsKey(key); } return false; } internal static Json Parse(string text) { if (string.IsNullOrEmpty(text)) { return Nothing; } int at = 0; return Read(text, ref at) ?? Nothing; } private static Json Read(string s, ref int at) { SkipSpace(s, ref at); if (at >= s.Length) { return Nothing; } switch (s[at]) { case '{': return ReadObject(s, ref at); case '[': return ReadArray(s, ref at); case '"': return new Json { Type = Kind.Text, _text = ReadString(s, ref at) }; default: if (Matches(s, at, "true")) { at += 4; return new Json { Type = Kind.Bool, _bool = true }; } if (Matches(s, at, "false")) { at += 5; return new Json { Type = Kind.Bool, _bool = false }; } if (Matches(s, at, "null")) { at += 4; return Nothing; } return ReadNumber(s, ref at); } } private static Json ReadObject(string s, ref int at) { Json json = new Json { Type = Kind.Object, _object = new Dictionary<string, Json>(StringComparer.Ordinal) }; at++; while (at < s.Length) { SkipSpace(s, ref at); if (at >= s.Length) { break; } if (s[at] == '}') { at++; break; } if (s[at] == ',') { at++; continue; } if (s[at] != '"') { at++; continue; } string key = ReadString(s, ref at); SkipSpace(s, ref at); if (at < s.Length && s[at] == ':') { at++; } json._object[key] = Read(s, ref at); } return json; } private static Json ReadArray(string s, ref int at) { Json json = new Json { Type = Kind.Array, _array = new List<Json>() }; at++; while (at < s.Length) { SkipSpace(s, ref at); if (at >= s.Length) { break; } if (s[at] == ']') { at++; break; } if (s[at] == ',') { at++; } else { json._array.Add(Read(s, ref at)); } } return json; } private static string ReadString(string s, ref int at) { StringBuilder stringBuilder = new StringBuilder(); at++; while (at < s.Length) { char c = s[at++]; switch (c) { default: stringBuilder.Append(c); continue; case '\\': { if (at >= s.Length) { break; } char c2 = s[at++]; switch (c2) { case 'n': stringBuilder.Append('\n'); break; case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case '/': stringBuilder.Append('/'); break; case '\\': stringBuilder.Append('\\'); break; case '"': stringBuilder.Append('"'); break; case 'u': { if (at + 4 <= s.Length && int.TryParse(s.Substring(at, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { stringBuilder.Append((char)result); at += 4; } break; } default: stringBuilder.Append(c2); break; } continue; } case '"': break; } break; } return stringBuilder.ToString(); } private static Json ReadNumber(string s, ref int at) { int num = at; while (at < s.Length) { char c = s[at]; if ((c < '0' || c > '9') && c != '-' && c != '+' && c != '.' && c != 'e' && c != 'E') { break; } at++; } if (!double.TryParse(s.Substring(num, at - num), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return Nothing; } return new Json { Type = Kind.Number, _number = result }; } private static void SkipSpace(string s, ref int at) { while (at < s.Length && char.IsWhiteSpace(s[at])) { at++; } } private static bool Matches(string s, int at, string word) { if (at + word.Length <= s.Length) { return string.CompareOrdinal(s, at, word, 0, word.Length) == 0; } return false; } } } namespace GBV.Gersemi { internal static class Assets { internal static string Override => Path.Combine(Paths.ConfigPath, "Gersemi"); internal static string PluginFolder { get { try { return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } catch { return null; } } } internal static string Find(string name) { if (string.IsNullOrEmpty(name)) { return null; } try { if (Path.IsPathRooted(name)) { return File.Exists(name) ? name : null; } string text = Path.Combine(Override, name); if (File.Exists(text)) { return text; } string pluginFolder = PluginFolder; if (!string.IsNullOrEmpty(pluginFolder)) { string text2 = Path.Combine(pluginFolder, name); if (File.Exists(text2)) { return text2; } } } catch { } return null; } internal static void EnsureOverride() { try { if (!Directory.Exists(Override)) { Directory.CreateDirectory(Override); } } catch (Exception ex) { Safety.Once("Assets.EnsureOverride", "could not create " + Override + " (" + ex.Message + "). The shipped gear still works; you just cannot override the art from there."); } } } internal static class Commands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__1_0; internal void <Register>b__1_0(ConsoleEventArgs args) { Run(args); } } private static bool _registered; internal static void Register() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown if (_registered) { return; } _registered = true; try { object obj = <>c.<>9__1_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { Run(args); }; <>c.<>9__1_0 = val; obj = (object)val; } new ConsoleCommand("gersemi", "Gersemi: probe, fit, shader, give, list. Run it with no argument for help.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } catch (Exception ex) { Safety.Failed("Commands.Register", ex); } } private static void Run(ConsoleEventArgs args) { try { switch ((args.Length > 1) ? args[1].ToLowerInvariant() : "") { case "probe": Probe(args); break; case "fit": Fit(args); break; case "shader": ShaderVerb(args); break; case "fx": Fx(args); break; case "color": case "colour": Colour(args); break; case "give": Give(args); break; case "list": List(args); break; case "rebuild": Rebuild(args); break; default: Say(args, "gersemi probe what was cloned, what it was painted with, and where the embers are\ngersemi list what Gersemi adds, and whether it built\ngersemi give [name] put one in your pack\ngersemi fit ... yaw, pitch, roll, scale, x, y, z - nudge the helm on the head\ngersemi shader <n> repaint a worn helm from a different donor\ngersemi fx [name] borrow the fire off one of the game's own weapons; no name lists them\ngersemi colour <name> Ember, Muspel, Wyrd or Draugr\ngersemi rebuild build the prefabs again from the current settings"); break; } } catch (Exception ex) { Safety.Failed("Commands.Run", ex); Say(args, "that threw; see the log."); } } private static void Probe(ConsoleEventArgs args) { //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Gersemi ").Append("0.1.4").Append('\n'); stringBuilder.Append("shader: ").Append(HelmMaterial.ShaderUsed).Append(" from ") .Append(HelmMaterial.DonorUsed); if (HelmMaterial.ShaderUsed == "Custom/Piece") { stringBuilder.Append(" <<< THIS IS THE FLICKERING FAMILY. It samples noise by world position, and a helmet on a head moves every frame."); } stringBuilder.Append('\n'); foreach (GearPrefab.Built item in GearPrefab.Everything) { if (item == null) { continue; } stringBuilder.Append(item.Def.PrefabName).Append(": cloned from ").Append(item.ClonedFrom ?? "nothing") .Append(", ") .Append(item.Triangles) .Append(" triangles") .Append(", fitted scale ") .Append(F(item.Scale)) .Append(item.Painted ? ", painted" : ", NOT PAINTED - wearing the donor helmet's own textures"); if (item.UsedOverrideModel) { stringBuilder.Append(", USING THE OVERRIDE MODEL"); } stringBuilder.Append('\n'); GameObject prefab = item.Prefab; if ((Object)(object)prefab == (Object)null) { stringBuilder.Append(" (not built)\n"); continue; } int num = 0; Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { num++; } } bool flag = (Object)(object)prefab.GetComponentInChildren<Rigidbody>(true) != (Object)null; stringBuilder.Append(" physics: ").Append(num).Append(" collider(s), ") .Append(flag ? "a rigidbody" : "NO RIGIDBODY"); if (num == 0) { stringBuilder.Append(" <<< IT WILL FALL THROUGH THE WORLD WHEN DROPPED"); } stringBuilder.Append('\n'); MeshFilter[] componentsInChildren2 = prefab.GetComponentsInChildren<MeshFilter>(true); Bounds val2; foreach (MeshFilter val in componentsInChildren2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sharedMesh == (Object)null)) { StringBuilder stringBuilder2 = stringBuilder.Append(" mesh '").Append(((Object)((Component)val).gameObject).name).Append("' under '") .Append(((Object)(object)((Component)val).transform.parent != (Object)null) ? ((Object)((Component)val).transform.parent).name : "?") .Append("' size "); val2 = val.sharedMesh.bounds; StringBuilder stringBuilder3 = stringBuilder2.Append(V(((Bounds)(ref val2)).size)).Append(" centred "); val2 = val.sharedMesh.bounds; stringBuilder3.Append(V(((Bounds)(ref val2)).center)).Append(" xform ").Append(V(((Component)val).transform.localPosition)) .Append(" scale ") .Append(F(((Component)val).transform.localScale.x)) .Append('\n'); } } foreach (KeyValuePair<string, Bounds> donorBound in item.DonorBounds) { StringBuilder stringBuilder4 = stringBuilder.Append(" donor '").Append(donorBound.Key).Append("' size "); val2 = donorBound.Value; StringBuilder stringBuilder5 = stringBuilder4.Append(V(((Bounds)(ref val2)).size)).Append(" centred "); val2 = donorBound.Value; stringBuilder5.Append(V(((Bounds)(ref val2)).center)).Append('\n'); } Transform val3 = prefab.transform.Find("attach"); stringBuilder.Append(" attach: ").Append(((Object)(object)val3 == (Object)null) ? "MISSING" : "present").Append('\n'); if (!((Object)(object)val3 != (Object)null)) { continue; } Transform val4 = val3.Find("equiped"); stringBuilder.Append(" embers: ").Append(((Object)(object)val4 == (Object)null) ? "MISSING - the child must be called 'equiped', with one p, or the game never switches it on" : (((Component)val4).GetComponentsInChildren<ParticleSystem>(true).Length + " systems, " + ((Component)val4).GetComponentsInChildren<Light>(true).Length + " light")).Append('\n'); Renderer[] componentsInChildren3 = ((Component)val3).GetComponentsInChildren<Renderer>(true); foreach (Renderer val5 in componentsInChildren3) { if ((Object)(object)val5 == (Object)null) { continue; } Material[] sharedMaterials = val5.sharedMaterials; foreach (Material val6 in sharedMaterials) { if (!((Object)(object)val6 == (Object)null)) { stringBuilder.Append(" material ").Append(((Object)val6).name).Append(" shader ") .Append(((Object)(object)val6.shader != (Object)null) ? ((Object)val6.shader).name : "none") .Append(" keywords ") .Append(HelmMaterial.Keywords(val6)) .Append(" maps ") .Append(HelmMaterial.Maps(val6)); if (val6.IsKeywordEnabled("_VALUENOISEVERTEX_ON")) { stringBuilder.Append(" <<< WORLD-SPACE VERTEX NOISE, this flickers when worn"); } stringBuilder.Append('\n'); } } } } HelmMesh.Built built = HelmBuild.Geometry(); stringBuilder.Append("horn tips: right ").Append(V(built.RightHornTip)).Append(" left ") .Append(V(built.LeftHornTip)) .Append('\n'); stringBuilder.Append("fit: yaw ").Append(F(GersemiPlugin.FitYaw.Value)).Append(" pitch ") .Append(F(GersemiPlugin.FitPitch.Value)) .Append(" roll ") .Append(F(GersemiPlugin.FitRoll.Value)) .Append(" scale ") .Append(F(GersemiPlugin.FitScale.Value)) .Append(" offset ") .Append(F(GersemiPlugin.FitOffsetX.Value)) .Append(',') .Append(F(GersemiPlugin.FitOffsetY.Value)) .Append(',') .Append(F(GersemiPlugin.FitOffsetZ.Value)) .Append(" flipped ") .Append(GersemiPlugin.FlipWinding.Value); if (GersemiPlugin.FitYaw.Value != 0f || GersemiPlugin.FitPitch.Value != 0f || GersemiPlugin.FitRoll.Value != 0f || GersemiPlugin.FitScale.Value != 1f || GersemiPlugin.FitOffsetX.Value != 0f || GersemiPlugin.FitOffsetY.Value != 0f || GersemiPlugin.FitOffsetZ.Value != 0f || GersemiPlugin.FlipWinding.Value) { stringBuilder.Append(" <<< NOT THE DEFAULTS. If the helm is missing or in the wrong place, gersemi fit reset"); } stringBuilder.Append('\n'); stringBuilder.Append("fire: ").Append(string.IsNullOrEmpty(GersemiPlugin.EmberDonor.Value) ? ("Gersemi's own, palette " + EmberPlan.Chosen) : ("borrowed from " + Kindling.Borrowed)).Append(", drawn with ") .Append(EmberCrown.ShaderUsed); if (EmberCrown.ShaderUsed.IndexOf("additive", StringComparison.OrdinalIgnoreCase) < 0) { stringBuilder.Append(" <<< NOT ADDITIVE, so the fire will look flat rather than hot"); } stringBuilder.Append('\n'); stringBuilder.Append("donors available: "); foreach (string donorName in HelmMaterial.DonorNames) { stringBuilder.Append(donorName).Append(' '); } Say(args, stringBuilder.ToString()); } private static void Fit(ConsoleEventArgs args) { string text = ((args.Length > 2) ? args[2].ToLowerInvariant() : ""); if (text.Length == 0 || (text != "reset" && text != "flip" && args.Length < 4)) { Say(args, "gersemi fit <yaw|pitch|roll|scale|x|y|z> <number>\ngersemi fit flip turn the surfaces inside out\ngersemi fit reset back to the fitted defaults\nfor example: gersemi fit yaw 90"); return; } if (text == "reset") { GersemiPlugin.FitYaw.Value = 0f; GersemiPlugin.FitPitch.Value = 0f; GersemiPlugin.FitRoll.Value = 0f; GersemiPlugin.FitScale.Value = 1f; GersemiPlugin.FitOffsetX.Value = 0f; GersemiPlugin.FitOffsetY.Value = 0f; GersemiPlugin.FitOffsetZ.Value = 0f; GersemiPlugin.FlipWinding.Value = false; Rebuild(args); Say(args, "fit reset.\n" + FitLine()); return; } if (text == "flip") { GersemiPlugin.FlipWinding.Value = !GersemiPlugin.FlipWinding.Value; Rebuild(args); Say(args, "winding flipped to " + GersemiPlugin.FlipWinding.Value + ".\n" + FitLine()); return; } if (!float.TryParse(args[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { Say(args, "'" + args[3] + "' is not a number."); return; } switch (text) { case "yaw": GersemiPlugin.FitYaw.Value = result; break; case "pitch": GersemiPlugin.FitPitch.Value = result; break; case "roll": GersemiPlugin.FitRoll.Value = result; break; case "scale": GersemiPlugin.FitScale.Value = result; break; case "x": GersemiPlugin.FitOffsetX.Value = result; break; case "y": GersemiPlugin.FitOffsetY.Value = result; break; case "z": GersemiPlugin.FitOffsetZ.Value = result; break; default: Say(args, "nudge one of: yaw, pitch, roll, scale, x, y, z, flip, reset."); return; } Rebuild(args); Say(args, FitLine()); } private static string FitLine() { return "paste into BepInEx\\config\\gbv.valheim.gersemi.cfg under [Fit]:\nYaw = " + F(GersemiPlugin.FitYaw.Value) + " Pitch = " + F(GersemiPlugin.FitPitch.Value) + " Roll = " + F(GersemiPlugin.FitRoll.Value) + " Scale = " + F(GersemiPlugin.FitScale.Value) + "\nOffsetX = " + F(GersemiPlugin.FitOffsetX.Value) + " OffsetY = " + F(GersemiPlugin.FitOffsetY.Value) + " OffsetZ = " + F(GersemiPlugin.FitOffsetZ.Value) + " FlipWinding = " + GersemiPlugin.FlipWinding.Value; } private static void ShaderVerb(ConsoleEventArgs args) { int result; string said; if (args.Length < 3) { StringBuilder stringBuilder = new StringBuilder("gersemi shader <n>, where n is:\n"); int num = 0; foreach (string donorName in HelmMaterial.DonorNames) { stringBuilder.Append(" ").Append(num++).Append(' ') .Append(donorName) .Append('\n'); } stringBuilder.Append("currently ").Append(HelmMaterial.ShaderUsed).Append(" from ") .Append(HelmMaterial.DonorUsed); Say(args, stringBuilder.ToString()); } else if (!int.TryParse(args[2], out result)) { Say(args, "'" + args[2] + "' is not a number."); } else if (!HelmMaterial.Rebind(result, out said)) { Say(args, said); } else { Rebuild(args); Say(args, said + "\nRe-equip the helm to see it."); } } private static void Colour(ConsoleEventArgs args) { if (args.Length < 3) { StringBuilder stringBuilder = new StringBuilder("gersemi colour <name>. Currently ").Append(EmberPlan.Chosen).Append(".\n"); EmberPlan.Palette[] array = (EmberPlan.Palette[])Enum.GetValues(typeof(EmberPlan.Palette)); foreach (EmberPlan.Palette palette in array) { stringBuilder.Append(" ").Append(palette).Append('\n'); } stringBuilder.Append("Recolour is ").Append(GersemiPlugin.Recolour.Value ? "on" : "OFF").Append(", so a borrowed effect ") .Append(GersemiPlugin.Recolour.Value ? "follows this too." : "keeps the donor's own colours."); Say(args, stringBuilder.ToString()); } else { EmberPlan.Palette chosen = EmberPlan.ParsePalette(args[2]); GersemiPlugin.EmberColour.Value = chosen.ToString(); EmberPlan.Chosen = chosen; EmberCrown.Reset(); Kindling.Reset(); Rebuild(args); Say(args, "fire is now " + chosen.ToString() + "."); } } private static void Fx(ConsoleEventArgs args) { if (args.Length < 3) { List<string> list = Kindling.Candidates(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("currently burning with: ").Append(string.IsNullOrEmpty(GersemiPlugin.EmberDonor.Value) ? "Gersemi's own fire" : ("borrowed from " + Kindling.Borrowed)).Append('\n'); stringBuilder.Append("gersemi fx <name> borrow that weapon's effect\n"); stringBuilder.Append("gersemi fx any let Gersemi pick the best one here\n"); stringBuilder.Append("gersemi fx off go back to Gersemi's own fire\n"); stringBuilder.Append("gersemi fx dump <name> read out how that effect is built\n"); stringBuilder.Append("gersemi fx drop <layer> leave one layer of it out\n"); stringBuilder.Append("gersemi fx keep <layer> put that layer back\n\n"); stringBuilder.Append(list.Count).Append(" weapons in this world have an effect to lend:\n"); foreach (string item in list) { stringBuilder.Append(" ").Append(item).Append('\n'); } Say(args, stringBuilder.ToString()); return; } StringBuilder stringBuilder2 = new StringBuilder(); for (int i = 2; i < args.Length; i++) { if (stringBuilder2.Length > 0) { stringBuilder2.Append(' '); } stringBuilder2.Append(args[i]); } string text = stringBuilder2.ToString(); if (text.StartsWith("drop", StringComparison.OrdinalIgnoreCase) || text.StartsWith("keep", StringComparison.OrdinalIgnoreCase)) { bool flag = text.StartsWith("drop", StringComparison.OrdinalIgnoreCase); string text2 = ((text.Length > 4) ? text.Substring(4).Trim() : ""); if (text2.Length == 0) { StringBuilder stringBuilder3 = new StringBuilder(); stringBuilder3.Append("gersemi fx drop <layer> leave that layer out\n"); stringBuilder3.Append("gersemi fx keep <layer> put it back\n"); stringBuilder3.Append("currently skipping: ").Append(string.IsNullOrEmpty(GersemiPlugin.SkipLayers.Value) ? "(nothing)" : GersemiPlugin.SkipLayers.Value).Append('\n'); foreach (string item2 in Kindling.Layers(GersemiPlugin.EmberDonor.Value)) { stringBuilder3.Append(" ").Append(item2).Append('\n'); } Say(args, stringBuilder3.ToString()); return; } List<string> list2 = new List<string>(); string[] array = GersemiPlugin.SkipLayers.Value.Split(','); for (int j = 0; j < array.Length; j++) { string text3 = array[j].Trim(); if (text3.Length != 0 && !string.Equals(text3, text2, StringComparison.OrdinalIgnoreCase)) { list2.Add(text3); } } if (flag) { list2.Add(text2); } GersemiPlugin.SkipLayers.Value = string.Join(", ", list2.ToArray()); Kindling.Reset(); Rebuild(args); Say(args, (flag ? "dropped '" : "kept '") + text2 + "'.\nSkipLayers = " + ((GersemiPlugin.SkipLayers.Value.Length == 0) ? "(nothing)" : GersemiPlugin.SkipLayers.Value)); } else if (text.StartsWith("dump", StringComparison.OrdinalIgnoreCase)) { string text4 = ((text.Length > 4) ? text.Substring(4).Trim() : ""); if (text4.Length == 0) { Say(args, "gersemi fx dump <name>, for example: gersemi fx dump Frostfire Dagger"); } else { Say(args, Kindling.Describe(text4)); } } else { if (string.Equals(text, "off", StringComparison.OrdinalIgnoreCase)) { text = ""; } GersemiPlugin.EmberDonor.Value = text; Kindling.Reset(); Rebuild(args); Say(args, ((text.Length == 0) ? "back to Gersemi's own fire." : ("burning with '" + text + "'.")) + "\nRe-equip the helm to see it. Written to BorrowFrom in the config."); } } private static void Give(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Say(args, "you have to be in a world."); return; } string text = ((args.Length > 2) ? args[2] : "GS_Muspelhelm"); GearDef gearDef = Gear.Find(text); if (gearDef == null) { Say(args, "Gersemi has nothing called '" + text + "'. Try gersemi list."); return; } Registry.Sync(); GameObject val = GearPrefab.PrefabFor(gearDef.PrefabName); if ((Object)(object)val == (Object)null) { Say(args, gearDef.PrefabName + " has not been built. Run gersemi probe."); return; } if (!((Humanoid)localPlayer).GetInventory().AddItem(val, 1)) { Say(args, "no room in your pack."); return; } localPlayer.AddKnownItem(val.GetComponent<ItemDrop>().m_itemData); Say(args, "gave you a " + gearDef.DisplayName + "."); } private static void List(ConsoleEventArgs args) { StringBuilder stringBuilder = new StringBuilder(); GearDef[] all = Gear.All; foreach (GearDef gearDef in all) { GearPrefab.Built built = GearPrefab.Of(gearDef.PrefabName); stringBuilder.Append(gearDef.PrefabName).Append(" \"").Append(gearDef.DisplayName) .Append("\" ") .Append(((Object)(object)built?.Prefab != (Object)null) ? ("built from " + built.ClonedFrom) : "NOT BUILT") .Append('\n'); } Say(args, stringBuilder.ToString()); } private static void Rebuild(ConsoleEventArgs args) { Registry.Reapply(); } private static void Say(ConsoleEventArgs args, string message) { Terminal context = args.Context; if (context != null) { context.AddString(message); } ManualLogSource log = GersemiPlugin.Log; if (log != null) { log.LogInfo((object)("Gersemi: " + message)); } } private static string F(float f) { return f.ToString("0.###", CultureInfo.InvariantCulture); } private static string V(float[] v) { if (v != null) { return "(" + F(v[0]) + ", " + F(v[1]) + ", " + F(v[2]) + ")"; } return "none"; } private static string V(Vector3 v) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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) return "(" + F(v.x) + ", " + F(v.y) + ", " + F(v.z) + ")"; } } internal static class EmberCrown { internal const string EquippedChild = "equiped"; internal const int FlipbookCols = 4; internal const int FlipbookRows = 4; private static Material _flameMaterial; private static Material _sparkMaterial; private static Shader _particleShader; internal static string ShaderUsed { get { if (!((Object)(object)_particleShader != (Object)null)) { return "(none yet)"; } return ((Object)_particleShader).name; } } internal static GameObject Build(Transform attach, HelmMesh.Built geometry, float scale, Vector3 seat) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)attach == (Object)null || geometry == null) { return null; } if (Icons.Headless()) { return null; } if (GersemiPlugin.Embers != null && !GersemiPlugin.Embers.Value) { return null; } try { Transform val = attach.Find("equiped"); if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)val).gameObject); } GameObject val2 = new GameObject("equiped"); val2.transform.SetParent(attach, false); val2.transform.localPosition = seat; val2.transform.localRotation = Quaternion.identity; val2.transform.localScale = Vector3.one; EmberPlan.Chosen = EmberPlan.ParsePalette((GersemiPlugin.EmberColour != null) ? GersemiPlugin.EmberColour.Value : null); Mesh val3 = HornMesh(geometry, scale); if ((Object)(object)Kindling.Build(val2.transform, val3, scale) == (Object)null) { Emitter(val2.transform, "Flame", val3, EmberPlan.Plan.Flame()); Emitter(val2.transform, "Sparks", val3, EmberPlan.Plan.Sparks()); } BrowLight(val2.transform, geometry.BrowFront, scale); val2.AddComponent<BrowGlimmer>(); val2.SetActive(false); return val2; } catch (Exception ex) { Safety.Failed("EmberCrown.Build", ex); return null; } } private static Mesh HornMesh(HelmMesh.Built geometry, float scale) { //IL_008e: 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) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown float[] hornSurface = geometry.HornSurface; if (hornSurface == null || hornSurface.Length < 9) { return null; } float num = (geometry.Model.MinX + geometry.Model.MaxX) * 0.5f; float num2 = (geometry.Model.MinZ + geometry.Model.MaxZ) * 0.5f; int num3 = hornSurface.Length / 3; Vector3[] array = (Vector3[])(object)new Vector3[num3]; int[] array2 = new int[num3]; for (int i = 0; i < num3; i++) { array[i] = new Vector3((hornSurface[i * 3] - num) * scale, hornSurface[i * 3 + 1] * scale, (hornSurface[i * 3 + 2] - num2) * scale); array2[i] = i; } Mesh val = new Mesh { name = "GersemiHornEmitter", hideFlags = (HideFlags)61 }; val.SetVertices(array); val.SetTriangles(array2, 0); val.RecalculateNormals(); val.RecalculateBounds(); return val; } private static void Emitter(Transform parent, string name, Mesh shapeMesh, EmberPlan.Plan plan) { //IL_000b: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_0076: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011e: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)shapeMesh == (Object)null)) { GameObject val = new GameObject(name); val.transform.SetParent(parent, false); val.transform.localPosition = HelmBuild.FitOffset(); val.transform.localRotation = HelmBuild.FitRotation(); float num = 1f; ParticleSystem val2 = val.AddComponent<ParticleSystem>(); val2.Stop(true, (ParticleSystemStopBehavior)0); MainModule main = val2.main; ((MainModule)(ref main)).loop = true; ((MainModule)(ref main)).playOnAwake = true; ((MainModule)(ref main)).startLifetime = new MinMaxCurve(plan.LifetimeMin, plan.LifetimeMax); ((MainModule)(ref main)).startSpeed = new MinMaxCurve(plan.RiseMin * num, plan.RiseMax * num); ((MainModule)(ref main)).startSize = new MinMaxCurve(plan.SizeMin * num, plan.SizeMax * num); ((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(Color.white); ((MainModule)(ref main)).gravityModifier = MinMaxCurve.op_Implicit((0f - plan.Gravity) * 0.1f); ((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)0; ((MainModule)(ref main)).maxParticles = 48; ((MainModule)(ref main)).startRotation = new MinMaxCurve(0f, 6.283f); EmissionModule emission = val2.emission; ((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(plan.Rate * Rate()); ShapeModule shape = val2.shape; ((ShapeModule)(ref shape)).enabled = true; ((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)6; ((ShapeModule)(ref shape)).mesh = shapeMesh; ((ShapeModule)(ref shape)).meshShapeType = (ParticleSystemMeshShapeType)2; ((ShapeModule)(ref shape)).useMeshMaterialIndex = false; ((ShapeModule)(ref shape)).radius = plan.EmitterRadius * num; LimitVelocityOverLifetimeModule limitVelocityOverLifetime = val2.limitVelocityOverLifetime; ((LimitVelocityOverLifetimeModule)(ref limitVelocityOverLifetime)).enabled = true; ((LimitVelocityOverLifetimeModule)(ref limitVelocityOverLifetime)).dampen = Mathf.Clamp01(plan.Drag * 0.1f); VelocityOverLifetimeModule velocityOverLifetime = val2.velocityOverLifetime; ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).enabled = true; ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).space = (ParticleSystemSimulationSpace)0; ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).z = new MinMaxCurve((0f - plan.Drift) * num); ((VelocityOverLifetimeModule)(ref velocityOverLifetime)).x = new MinMaxCurve((0f - plan.Spread) * num, plan.Spread * num); NoiseModule noise = val2.noise; ((NoiseModule)(ref noise)).enabled = true; ((NoiseModule)(ref noise)).strength = MinMaxCurve.op_Implicit(plan.Wander * num * 8f); ((NoiseModule)(ref noise)).frequency = plan.WanderRate; ((NoiseModule)(ref noise)).damping = true; ColourOverLife(val2); SizeOverLife(val2); Renderer(val2, plan); if (plan.Stretch > 0.5f) { TextureSheetAnimationModule textureSheetAnimation = val2.textureSheetAnimation; ((TextureSheetAnimationModule)(ref textureSheetAnimation)).enabled = true; ((TextureSheetAnimationModule)(ref textureSheetAnimation)).numTilesX = 4; ((TextureSheetAnimationModule)(ref textureSheetAnimation)).numTilesY = 4; ((TextureSheetAnimationModule)(ref textureSheetAnimation)).animation = (ParticleSystemAnimationType)0; ((TextureSheetAnimationModule)(ref textureSheetAnimation)).timeMode = (ParticleSystemAnimationTimeMode)0; ((TextureSheetAnimationModule)(ref textureSheetAnimation)).cycleCount = 1; ((TextureSheetAnimationModule)(ref textureSheetAnimation)).startFrame = new MinMaxCurve(0f, 16f); } val2.Play(); } } private static void ColourOverLife(ParticleSystem system) { //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_0016: Unknown result type (might be due to invalid IL or missing references) ColorOverLifetimeModule colorOverLifetime = system.colorOverLifetime; ((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = true; ((ColorOverLifetimeModule)(ref colorOverLifetime)).color = new MinMaxGradient(PaletteGradient()); } internal static Gradient PaletteGradient() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown GradientColorKey[] array = (GradientColorKey[])(object)new GradientColorKey[8]; GradientAlphaKey[] array2 = (GradientAlphaKey[])(object)new GradientAlphaKey[8]; for (int i = 0; i < 8; i++) { float num = (float)i / 7f; EmberPlan.ColourAt(num, out var r, out var g, out var b, out var a); array[i] = new GradientColorKey(new Color(r, g, b), num); array2[i] = new GradientAlphaKey(a, num); } Gradient val = new Gradient(); val.SetKeys(array, array2); return val; } private static void SizeOverLife(ParticleSystem system) { //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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) SizeOverLifetimeModule sizeOverLifetime = system.sizeOverLifetime; ((SizeOverLifetimeModule)(ref sizeOverLifetime)).enabled = true; AnimationCurve val = new AnimationCurve(); for (int i = 0; i <= 10; i++) { float num = (float)i / 10f; val.AddKey(num, EmberPlan.SizeAt(num)); } ((SizeOverLifetimeModule)(ref sizeOverLifetime)).size = new MinMaxCurve(1f, val); } private static void Renderer(ParticleSystem system, EmberPlan.Plan plan) { ParticleSystemRenderer component = ((Component)system).GetComponent<ParticleSystemRenderer>(); if (!((Object)(object)component == (Object)null)) { if (plan.Stretch > 0.5f) { component.renderMode = (ParticleSystemRenderMode)1; component.lengthScale = 1f + plan.Stretch; component.velocityScale = 0f; } else { component.renderMode = (ParticleSystemRenderMode)0; component.alignment = (ParticleSystemRenderSpace)0; } ((Renderer)component).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)component).receiveShadows = false; Material val = Additive(plan.Stretch > 0.5f); if ((Object)(object)val != (Object)null) { ((Renderer)component).material = val; } } } internal static Shader ParticleShader() { if ((Object)(object)_particleShader != (Object)null) { return _particleShader; } string[] array = new string[4] { "Torch", "TorchMist", "FireWorkRocket", "Firestaff" }; foreach (string text in array) { GameObject val = Kindling.Find(text); if ((Object)(object)val == (Object)null) { continue; } ParticleSystemRenderer[] componentsInChildren = val.GetComponentsInChildren<ParticleSystemRenderer>(true); foreach (ParticleSystemRenderer val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Renderer)val2).sharedMaterial == (Object)null) && !((Object)(object)((Renderer)val2).sharedMaterial.shader == (Object)null)) { string name = ((Object)((Renderer)val2).sharedMaterial.shader).name; if (name.IndexOf("additive", StringComparison.OrdinalIgnoreCase) >= 0) { _particleShader = ((Renderer)val2).sharedMaterial.shader; GersemiPlugin.Verbose("fire drawn with " + name + ", taken off " + text + "."); return _particleShader; } } } } array = new string[4] { "Legacy Shaders/Particles/Additive", "Particles/Additive", "Legacy Shaders/Particles/Alpha Blended Premultiply", "Sprites/Default" }; foreach (string text2 in array) { Shader val3 = Shader.Find(text2); if (!((Object)(object)val3 == (Object)null)) { _particleShader = val3; GersemiPlugin.Verbose("fire drawn with " + text2 + " (from Shader.Find)."); return _particleShader; } } return null; } private static Material Additive(bool flipbook) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown if (flipbook && (Object)(object)_flameMaterial != (Object)null) { return _flameMaterial; } if (!flipbook && (Object)(object)_sparkMaterial != (Object)null) { return _sparkMaterial; } Shader val = ParticleShader(); if ((Object)(object)val == (Object)null) { Safety.Once("EmberCrown.NoShader", "no particle shader could be found, so the embers will be drawn with whatever Unity defaults to. Run gersemi probe."); return null; } Material val2 = new Material(val) { name = (flipbook ? "GersemiFlame" : "GersemiSpark"), hideFlags = (HideFlags)61 }; Texture2D val3 = (flipbook ? Sheet() : Sprite(EmberSprite.Glyph.Ember)); if ((Object)(object)val3 != (Object)null && val2.HasProperty("_MainTex")) { val2.SetTexture("_MainTex", (Texture)(object)val3); } if (flipbook) { _flameMaterial = val2; } else { _sparkMaterial = val2; } return val2; } private static Texture2D Sheet() { //IL_0025: 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_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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) try { int num = 256; int num2 = 256; float[] array = new float[num * num2 * 4]; EmberSprite.RenderSheet(64, 4, 4, array); Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false) { name = "GersemiFlameSheet", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color[] array2 = (Color[])(object)new Color[num * num2]; for (int i = 0; i < num2; i++) { int num3 = (num2 - 1 - i) * num; for (int j = 0; j < num; j++) { int num4 = (num3 + j) * 4; array2[i * num + j] = new Color(array[num4], array[num4 + 1], array[num4 + 2], array[num4 + 3]); } } val.SetPixels(array2); val.Apply(false); return val; } catch (Exception ex) { Safety.Failed("EmberCrown.Sheet", ex); return null; } } private static Texture2D Sprite(EmberSprite.Glyph glyph) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0043: 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_0053: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) try { float[] array = new float[16384]; EmberSprite.Render(64, glyph, array); Texture2D val = new Texture2D(64, 64, (TextureFormat)4, true) { name = "GersemiEmber_" + glyph, filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Color[] array2 = (Color[])(object)new Color[4096]; for (int i = 0; i < 64; i++) { int num = (63 - i) * 64; for (int j = 0; j < 64; j++) { int num2 = (num + j) * 4; array2[i * 64 + j] = new Color(array[num2], array[num2 + 1], array[num2 + 2], array[num2 + 3]); } } val.SetPixels(array2); val.Apply(true); return val; } catch (Exception ex) { Safety.Failed("EmberCrown.Sprite", ex); return null; } } internal static void Reset() { _particleShader = null; _flameMaterial = null; _sparkMaterial = null; } private static float Rate() { return Mathf.Clamp((GersemiPlugin.EmberRate != null) ? GersemiPlugin.EmberRate.Value : 1f, 0f, 4f); } private static void BrowLight(Transform parent, float[] at, float scale) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_003e: 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_006a: Unknown result type (might be due to invalid IL or missing references) if (at != null && (GersemiPlugin.BrowLight == null || GersemiPlugin.BrowLight.Value)) { GameObject val = new GameObject("BrowLight"); val.transform.SetParent(parent, false); val.transform.localPosition = new Vector3(at[0], at[1], at[2]) * scale; Light obj = val.AddComponent<Light>(); obj.type = (LightType)2; obj.color = new Color(1f, 0.62f, 0.26f); obj.range = Mathf.Clamp((GersemiPlugin.BrowLightRange != null) ? GersemiPlugin.BrowLightRange.Value : 1.6f, 0.2f, 6f); obj.intensity = 0.85f; obj.shadows = (LightShadows)0; obj.renderMode = (LightRenderMode)2; } } } internal sealed class BrowGlimmer : MonoBehaviour { private Light _light; private Material _trim; private Color _base; private bool _looked; private float _offset; private void Update() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) try { if (!_looked) { Look(); } float num = EmberPlan.Glimmer(Time.time + _offset); if ((Object)(object)_trim != (Object)null && _trim.HasProperty("_EmissionColor")) { _trim.SetColor("_EmissionColor", _base * Depth() * (0.15f + 0.85f * num)); } if ((Object)(object)_light != (Object)null) { _light.intensity = 0.55f + 0.5f * num; } } catch (Exception ex) { Safety.Failed("BrowGlimmer.Update", ex); ((Behaviour)this).enabled = false; } } private static float Depth() { return Mathf.Clamp((GersemiPlugin.BrowGlow != null) ? GersemiPlugin.BrowGlow.Value : 0.14f, 0f, 1f); } private void Look() { //IL_00b6: 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) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) _looked = true; _offset = Random.value * 10f; _light = ((Component)this).GetComponentInChildren<Light>(true); Transform parent = ((Component)this).transform.parent; if ((Object)(object)parent == (Object)null) { return; } Renderer[] componentsInChildren = ((Component)parent).GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Material[] materials = val.materials; foreach (Material val2 in materials) { if (!((Object)(object)val2 == (Object)null) && ((Object)val2).name != null && ((Object)val2).name.IndexOf("GersemiHotTrim", StringComparison.Ordinal) >= 0) { _trim = val2; _base = (val2.HasProperty("_EmissionColor") ? HelmMaterial.ColourFor("GersemiHotTrim") : Color.white); return; } } } } } internal static class EmberPlan { internal enum Palette { Ember, Muspel, Wyrd, Draugr } internal sealed class Plan { public string Name = "layer"; public float Stretch; public float Intensity = 1f; public float Rate = 16f; public float LifetimeMin = 0.45f; public float LifetimeMax = 0.9f; public float SizeMin = 0.007f; public float SizeMax = 0.017f; public float RiseMin = 0.13f; public float RiseMax = 0.29f; public float Spread = 0.075f; public float Drift = 0.08f; public float Drag = 2.4f; public float Gravity = -0.26f; public float EmitterRadius = 0.01f; public float Wander = 0.03f; public float WanderRate = 1.7f; internal static Plan Flame() { return new Plan { Name = "flame", Rate = 210f, LifetimeMin = 0.2f, LifetimeMax = 0.44f, SizeMin = 0.016f, SizeMax = 0.036f, RiseMin = 0.26f, RiseMax = 0.58f, Spread = 0.05f, Drift = 0.04f, Drag = 4.4f, Gravity = -0.04f, EmitterRadius = 0.006f, Wander = 0.014f, WanderRate = 3.4f, Stretch = 0.85f, Intensity = 1.3f }; } internal static Plan Sparks() { return new Plan { Name = "sparks", Rate = 70f, LifetimeMin = 0.55f, LifetimeMax = 1.25f, SizeMin = 0.007f, SizeMax = 0.019f, RiseMin = 0.34f, RiseMax = 0.78f, Spread = 0.1f, Drift = 0.09f, Drag = 2.1f, Gravity = -0.3f, EmitterRadius = 0.013f, Wander = 0.042f, WanderRate = 2.1f, Stretch = 0.25f, Intensity = 1f }; } } internal sealed class Surface { private readonly float[] _tris; private readonly float[] _cumulative; internal float TotalArea { get; } internal int TriangleCount => _tris.Length / 9; internal Surface(float[] triangles) { _tris = triangles ?? new float[0]; int num = _tris.Length / 9; _cumulative = new float[num]; float num2 = 0f; for (int i = 0; i < num; i++) { num2 += Area(i); _cumulative[i] = num2; } TotalArea = num2; } private float Area(int tri) { int num = tri * 9; float num2 = _tris[num + 3] - _tris[num]; float num3 = _tris[num + 4] - _tris[num + 1]; float num4 = _tris[num + 5] - _tris[num + 2]; float num5 = _tris[num + 6] - _tris[num]; float num6 = _tris[num + 7] - _tris[num + 1]; float num7 = _tris[num + 8] - _tris[num + 2]; float num8 = num3 * num7 - num4 * num6; float num9 = num4 * num5 - num2 * num7; float num10 = num2 * num6 - num3 * num5; return 0.5f * (float)Math.Sqrt(num8 * num8 + num9 * num9 + num10 * num10); } internal void PointAt(float pick, float u, float v, out float x, out float y, out float z) { x = (y = (z = 0f)); if (_cumulative.Length == 0 || TotalArea <= 0f) { return; } float num = pick * TotalArea; int num2 = 0; int num3 = _cumulative.Length - 1; while (num2 < num3) { int num4 = (num2 + num3) / 2; if (_cumulative[num4] < num) { num2 = num4 + 1; } else { num3 = num4; } } if (u + v > 1f) { u = 1f - u; v = 1f - v; } int num5 = num2 * 9; float num6 = 1f - u - v; x = _tris[num5] * num6 + _tris[num5 + 3] * u + _tris[num5 + 6] * v; y = _tris[num5 + 1] * num6 + _tris[num5 + 4] * u + _tris[num5 + 7] * v; z = _tris[num5 + 2] * num6 + _tris[num5 + 5] * u + _tris[num5 + 8] * v; } } internal struct Particle { public float X; public float Y; public float Z; public float Size; public float Age; public float R; public float G; public float B; public float A; public float Stretch; public float VX; public float VY; public float VZ; } internal static Palette Chosen; internal static void ColourAt(float age, out float r, out float g, out float b, out float a) { ColourAt(age, Chosen, out r, out g, out b, out a); } internal static void ColourAt(float age, Palette palette, out float r, out float g, out float b, out float a) { if (age < 0f) { age = 0f; } if (age > 1f) { age = 1f; } float[] array = Stops(palette); if (age < 0.18f) { float t = age / 0.18f; r = Lerp(array[0], array[3], t); g = Lerp(array[1], array[4], t); b = Lerp(array[2], array[5], t); } else if (age < 0.52f) { float t2 = (age - 0.18f) / 0.34f; r = Lerp(array[3], array[6], t2); g = Lerp(array[4], array[7], t2); b = Lerp(array[5], array[8], t2); } else { float t3 = (age - 0.52f) / 0.48f; r = Lerp(array[6], array[9], t3); g = Lerp(array[7], array[10], t3); b = Lerp(array[8], array[11], t3); } float num = ((age < 0.04f) ? (age / 0.04f) : 1f); float num2 = ((age > 0.62f) ? (1f - (age - 0.62f) / 0.38f) : 1f); a = num * num2; if (a < 0f) { a = 0f; } } private static float[] Stops(Palette palette) { return palette switch { Palette.Muspel => new float[12] { 1f, 1f, 1f, 0.72f, 0.9f, 1f, 0.26f, 0.48f, 1f, 0.1f, 0.06f, 0.42f }, Palette.Wyrd => new float[12] { 1f, 1f, 0.92f, 0.72f, 1f, 0.58f, 0.2f, 0.88f, 0.26f, 0.02f, 0.24f, 0.06f }, Palette.Draugr => new float[12] { 1f, 0.96f, 1f, 0.86f, 0.62f, 1f, 0.54f, 0.16f, 0.92f, 0.14f, 0.02f, 0.28f }, _ => new float[12] { 1f, 0.85f, 0.45f, 1f, 0.52f, 0.11f, 0.94f, 0.24f, 0.03f, 0.32f, 0.04f, 0.01f }, }; } internal static Palette ParsePalette(string name) { if (string.IsNullOrEmpty(name)) { return Palette.Ember; } Palette[] array = (Palette[])Enum.GetValues(typeof(Palette)); for (int i = 0; i < array.Length; i++) { Palette result = array[i]; if (string.Equals(result.ToString(), name.Trim(), StringComparison.OrdinalIgnoreCase)) { return result; } } return Palette.Ember; } internal static float SizeAt(float age) { if (age < 0f) { age = 0f; } if (age > 1f) { age = 1f; } if (age < 0.12f) { return Lerp(0.55f, 1f, age / 0.12f); } float num = (age - 0.12f) / 0.88f; return Lerp(1f, 0.12f, num * num); } internal static List<Particle> Simulate(float time, Surface surface, int seed, Plan plan, float rateScale = 1f) { List<Particle> result = new List<Particle>(); if (plan == null || surface == null || surface.TriangleCount == 0 || time <= 0f) { return result; } return Simulate(time, null, surface, seed, plan, rateScale); } internal static List<Particle> Simulate(float time, float[] emitter, int seed, Plan plan, float rateScale = 1f) { return Simulate(time, emitter, null, seed, plan, rateScale); } private static List<Particle> Simulate(float time, float[] emitter, Surface surface, int seed, Plan plan, float rateScale) { List<Particle> list = new List<Particle>(); if (plan == null || time <= 0f) { return list; } if (emitter == null && surface == null) { return list; } float num = Math.Max(0.01f, plan.Rate * Math.Max(0f, rateScale)); float num2 = 1f / num; int num3 = (int)Math.Floor(time / num2); int num4 = (int)Math.Floor((time - plan.LifetimeMax) / num2) - 1; if (num4 < 0) { num4 = 0; } for (int i = num4; i <= num3; i++) { float num5 = (float)i * num2; float num6 = Lerp(plan.LifetimeMin, plan.LifetimeMax, Rand(i, seed, 1)); float num7 = time - num5; if (!(num7 < 0f) && !(num7 > num6)) { float num8 = num7 / num6; float x; float y; float z; if (surface != null) { surface.PointAt(Rand(i, seed, 10), Rand(i, seed, 11), Rand(i, seed, 12), out x, out y, out z); } else { x = emitter[0]; y = emitter[1]; z = emitter[2]; } float num9 = (Rand(i, seed, 2) - 0.5f) * 2f * plan.EmitterRadius; float num10 = (Rand(i, seed, 3) - 0.5f) * 2f * plan.EmitterRadius; float num11 = (Rand(i, seed, 4) - 0.5f) * 2f * plan.EmitterRadius; float num12 = (Rand(i, seed, 5) - 0.5f) * 2f * plan.Spread; float num13 = Lerp(plan.RiseMin, plan.RiseMax, Rand(i, seed, 6)); float num14 = (Rand(i, seed, 7) - 0.5f) * 2f * plan.Spread - plan.Drift; float num15 = ((plan.Drag > 0.0001f) ? ((1f - (float)Math.Exp((0f - plan.Drag) * num7)) / plan.Drag) : num7); float num16 = x + num9 + num12 * num15; float y2 = y + num10 + num13 * num15 + 0.5f * plan.Gravity * num7 * num7; float num17 = z + num11 + num14 * num15; float num18 = Rand(i, seed, 8) * (MathF.PI * 2f); num16 += (float)Math.Sin(num18 + num7 * plan.WanderRate * (MathF.PI * 2f)) * plan.Wander * num8; num17 += (float)Math.Cos(num18 * 1.7f + num7 * plan.WanderRate * 5.1f) * plan.Wander * num8; ColourAt(num8, out var r, out var g, out var b, out var a); float num19 = (float)Math.Exp((0f - plan.Drag) * num7); list.Add(new Particle { X = num16, Y = y2, Z = num17, Size = Lerp(plan.SizeMin, plan.SizeMax, Rand(i, seed, 9)) * SizeAt(num8), Age = num8, R = r * plan.Intensity, G = g * plan.Intensity, B = b * plan.Intensity, A = a, Stretch = plan.Stretch, VX = num12 * num19, VY = num13 * num19 + plan.Gravity * num7, VZ = num14 * num19 }); } } return list; } internal static float Glimmer(float time) { float num = (float)Math.Sin(time * 1.55f); float num2 = (float)Math.Sin(time * 2.63f + 1.1f); return 0.5f + 0.34f * num + 0.16f * num2; } private static float Lerp(float a, float b, float t) { return a + (b - a) * t; } private static float Rand(int index, int seed, int channel) { int num = (index * 73856093) ^ (seed * 19349663) ^ (channel * 83492791); int num2 = (num ^ (num >>> 16)) * 2146121005; int num3 = (num2 ^ (num2 >>> 15)) * -2073254261; return (float)(uint)((num3 ^ (num3 >>> 16)) & 0xFFFFFF) / 16777216f; } } internal static class EmberSprite { internal enum Glyph { Ember, Spark, Flame } private const float BellyRadius = 0.3f; private const float BellyY = -0.36f; private const float ApexY = 0.66f; private const float Bloom = 0.26f; internal static void Render(int size, Glyph glyph, float[] rgba) { if (size <= 0) { throw new ArgumentOutOfRangeException("size"); } if (rgba == null || rgba.Length < size * size * 4) { throw new ArgumentException("rgba must hold size * size * 4 floats", "rgba"); } for (int i = 0; i < size; i++) { float y = 1f - 2f * (((float)i + 0.5f) / (float)size); for (int j = 0; j < size; j++) { float x = 2f * (((float)j + 0.5f) / (float)size) - 1f; float num = ((glyph == Glyph.Ember) ? EmberAt(x, y) : SparkAt(x, y)); if (num < 0f) { num = 0f; } if (num > 1f) { num = 1f; } int num2 = (i * size + j) * 4; rgba[num2] = 1f; rgba[num2 + 1] = 1f; rgba[num2 + 2] = 1f; rgba[num2 + 3] = num; } } } internal static void RenderSheet(int size, int cols, int rows, float[] rgba) { if (size <= 0 || cols <= 0 || rows <= 0) { throw new ArgumentOutOfRangeException(); } int num = size * cols; int num2 = size * rows; if (rgba == null || rgba.Length < num * num2 * 4) { throw new ArgumentException("rgba must hold cols*rows tiles", "rgba"); } int num3 = cols * rows; for (int i = 0; i < num3; i++) { int num4 = i % cols * size; int num5 = i / cols * size; float phase = (float)i / (float)num3; for (int j = 0; j < size; j++) { float y = 1f - 2f * (((float)j + 0.5f) / (float)size); for (int k = 0; k < size; k++) { float num6 = FlameAt(2f * (((float)k + 0.5f) / (float)size) - 1f, y, phase); if (num6 < 0f) { num6 = 0f; } if (num6 > 1f) { num6 = 1f; } int num7 = ((num5 + j) * num + (num4 + k)) * 4; rgba[num7] = 1f; rgba[num7 + 1] = 1f; rgba[num7 + 2] = 1f; rgba[num7 + 3] = num6; } } } } private static float EmberAt(float x, float y) { float num = RoundCone(Math.Abs(x), y - -0.36f, 0.3f, 0f, 1.02f); if (num <= 0f) { float num2 = (0f - num) / 0.3f; if (num2 > 1f) { num2 = 1f; } return 0.34f + 0.66f * (float)Math.Pow(num2, 0.55); } return 0.4f * (float)Math.Exp((0f - num) / 0.078f); } private static float FlameAt(float x, float y, float phase) { float num = 0.09f * (float)Math.Sin(MathF.PI * 2f * phase); float h = 0.42f * (1f + 0.16f * (float)Math.Sin(MathF.PI * 2f * phase + 0.9f)); float num2 = RoundCone(Math.Abs(x - num * 0.35f), y - -0.36f, 0.3f, 0f, h); for (int i = 0; i < 3; i++) { float num3 = MathF.PI * 2f * (phase + (float)i * 0.29f); float num4 = (float)(i - 1) * 0.145f + 0.105f * (float)Math.Sin(num3); float num5 = -0.18f + 0.09f * (float)Math.Sin(num3 * 1.3f + 0.4f); float h2 = 0.22f + 0.48f * (0.5f + 0.5f * (float)Math.Sin(num3)); float num6 = RoundCone(Math.Abs(x - num4), y - num5, 0.17f - 0.025f * (float)i, 0f, h2); if (num6 < num2) { num2 = num6; } } if (num2 <= 0f) { float num7 = (0f - num2) / 0.3f; if (num7 > 1f) { num7 = 1f; } float num8 = 1f - 0.3f * Clamp01((y - -0.36f) / Math.Max(0.01f, 1.02f)); return Clamp01((0.3f + 0.7f * (float)Math.Pow(num7, 0.5)) * num8); } return 0.36f * (float)Math.Exp((0f - num2) / 0.078f); } private static float Clamp01(float v) { if (!(v < 0f)) { if (!(v > 1f)) { return v; } return 1f; } return 0f; } private static float SparkAt(float x, float y) { float num = (float)Math.Sqrt(x * x + y * y) / 0.62f; if (num >= 1f) { return 0f; } return (float)Math.Pow(1f - num, 1.65); } private static float RoundCone(float x, float y, float r1, float r2, float h) { if (h <= 1E-06f) { return (float)Math.Sqrt(x * x + y * y) - Math.Max(r1, r2); } float num = (r1 - r2) / h; float num2 = 1f - num * num; if (num2 <= 0f) { return (float)Math.Sqrt(x * x + y * y) - Math.Max(r1, r2); } float num3 = (float)Math.Sqrt(num2); float num4 = x * (0f - num) + y * num3; if (num4 < 0f) { return (float)Math.Sqrt(x * x + y * y) - r1; } if (num4 > num3 * h) { return (float)Math.Sqrt(x * x + (y - h) * (y - h)) - r2; } return x * num3 + y * num - r1; } } internal sealed class GearDef { public string PrefabName; public string DisplayName; public string Description; public string Icon; public string[] CloneFrom; public int Armor; public float Weight; public bool HidesHair; } internal static class Gear { internal const string MuspelhelmPrefab = "GS_Muspelhelm"; internal static readonly GearDef[] All = new GearDef[1] { new GearDef { PrefabName = "GS_Muspelhelm", DisplayName = "Muspelhelm", Description = "Forged for the first landing of the Guys Being Vikings, in the year 2026.\nThe horns still carry sparks from the fire the world was lit with.\n\nWorth nothing in a fight. Worth something to whoever was there.", Icon = "gersemi_muspelhelm.png", CloneFrom = new string[4] { "HelmetDrake", "HelmetBronze", "HelmetIron", "HelmetPadded" }, Armor = 1, Weight = 2f, HidesHair = true } }; internal static GearDef Find(string token) { if (string.IsNullOrEmpty(token)) { return null; } GearDef[] all = All; foreach (GearDef gearDef in all) { if (string.Equals(gearDef.PrefabName, token, StringComparison.OrdinalIgnoreCase)) { return gearDef; } if (string.Equals(gearDef.DisplayName, token, StringComparison.OrdinalIgnoreCase)) { return gearDef; } } return null; } } internal static class GearPrefab { internal sealed class Built { public GearDef Def; public GameObject Prefab; public string ClonedFrom; public float Scale = 1f; public Vector3 Seat; public int Triangles; public bool UsedOverrideModel; public GameObject Source; public bool Painted; public HelmBuild.Result Mesh; public readonly Dictionary<string, Bounds> DonorBounds = new Dictionary<string, Bounds>(StringComparer.Ordinal); } internal const string ModelChild = "GersemiModel"; private static readonly Dictionary<string, Built> Made = new Dictionary<string, Built>(StringComparer.Ordinal); private static readonly Dictionary<string, Mesh> Baked = new Dictionary<string, Mesh>(StringComparer.Ordinal); internal static IEnumerable<Built> Everything => Made.Values; internal static Built Of(string prefabName) { if (!Made.TryGetValue(prefabName, out var value)) { return null; } return value; } internal static GameObject PrefabFor(string prefabName) { return Of(prefabName)?.Prefab; } internal static void EnsureBuilt() { GearDef[] all = Gear.All; foreach (GearDef gearDef in all) { if (!Made.TryGetValue(gearDef.PrefabName, out var value) || !((Object)(object)value.Prefab != (Object)null)) { Built built = Build(gearDef); if (built != null) { Made[gearDef.PrefabName] = built; } } } } internal static void Repaint() { foreach (Built value in Made.Values) { if (value == null || value.Painted || (Object)(object)value.Prefab == (Object)null || value.Mesh == null) { continue; } Material[] array = HelmMaterial.For(value.Mesh.Materials, value.Source); i