{"TotalCount":94,"Files":[{"Ident":"vendetta.characterkit","Path":"CharacterKit/AutomaticWardrobeFit.cs","FileName":"AutomaticWardrobeFit.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\nusing System.IO;\r\nusing System.Text;\r\nusing System.Runtime.InteropServices;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003EFits loaded human clothes against shared body surfaces, without garment authoring files.\u003C/summary\u003E\r\npublic static class AutomaticWardrobeFit\r\n{\r\n\tconst string ReferencePath = \u0022models/characterkit/wardrobe-reference.bin\u0022;\r\n\tpublic const string Morph = \u0022ce_body_female\u0022;\r\n\t// Cache by resource identity and authored cut, never by the current slider value.\r\n\tstatic readonly Dictionary\u003C(Model, bool, bool), Model\u003E fittedModels = new();\r\n\tstatic readonly Dictionary\u003C(Model, bool, bool), string\u003E fittingFailures = new();\r\n\tstatic Surface originalMale, unifiedMale, unifiedFemale;\r\n\tstatic Surface headSurface;\r\n\tstatic Model replacementNeck;\r\n\tpublic static int CachedModels =\u003E fittedModels.Count;\r\n\r\n\tpublic static void ClearCache()\r\n\t{\r\n\t\tfittedModels.Clear();\r\n\t\tfittingFailures.Clear();\r\n\t\treplacementNeck = null;\r\n\t\theadSurface = null;\r\n\t}\r\n\r\n\t/// \u003Csummary\u003EKeep the lower neck when clothing replaces the head bodygroup.\u003C/summary\u003E\r\n\tpublic static Model ReplacementNeck()\r\n\t{\r\n\t\tif (replacementNeck is not null) return replacementNeck;\r\n\t\tReferences();\r\n\t\treturn replacementNeck = Build(Model.Load(UnifiedHuman.ModelPath), false, neckOnly: true);\r\n\t}\r\n\r\n\tpublic static bool IsFitted(Model model) =\u003E model?.Name.StartsWith(\u0022automatic-human-fit:\u0022, StringComparison.Ordinal) == true;\r\n\tpublic static Model Fit(Model source, bool female, bool fitFace = true)\r\n\t{\r\n\t\tif (source is null || source.IsError || IsFitted(source) || source.BoneCount == 0 || source.Bounds.Mins.z \u003E 60)\r\n\t\t\treturn source;\r\n\t\tif (fittedModels.TryGetValue((source, female, fitFace), out var ready))\r\n\t\t\treturn ready;\r\n\t\tif (fittingFailures.TryGetValue((source, female, fitFace), out var problem))\r\n\t\t\tthrow new InvalidOperationException(problem);\r\n\t\ttry\r\n\t\t{\r\n\t\t\tReferences();\r\n\t\t\tvar fitted = Build(source, female, fitFace: fitFace);\r\n\t\t\tfittedModels[(source, female, fitFace)] = fitted;\r\n\t\t\treturn fitted;\r\n\t\t}\r\n\t\tcatch (Exception error)\r\n\t\t{\r\n\t\t\tvar message = \u0022Cannot automatically fit \u0022 \u002B source.Name \u002B \u0022: \u0022 \u002B error.Message;\r\n\t\t\tfittingFailures[(source, female, fitFace)] = message;\r\n\t\t\tthrow new InvalidOperationException(message, error);\r\n\t\t}\r\n\t}\r\n\r\n\tstatic void References()\r\n\t{\r\n\t\tif (unifiedMale is not null)\r\n\t\t\treturn;\r\n\t\tusing var stream = new MemoryStream(FileSystem.Mounted.ReadAllBytes(ReferencePath).ToArray());\r\n\t\tusing var r = new BinaryReader(stream);\r\n\t\tif (r.ReadInt32() != 1)\r\n\t\t\tthrow new InvalidOperationException(\u0022Unsupported human fitting reference.\u0022);\r\n\t\toriginalMale = ReadSurface(r);\r\n\t\tunifiedMale = ReadSurface(r);\r\n\t\tunifiedFemale = new(unifiedMale.Female, unifiedMale.Female, unifiedMale.Triangles, unifiedMale.Names, unifiedMale.Weights);\r\n\t}\r\n\r\n\tstatic Surface HeadSurface()\r\n\t{\r\n\t\tif (headSurface is not null) return headSurface;\r\n\t\tvar source = Model.Load(UnifiedHuman.ModelPath);\r\n\t\tvar buffers = ReadBuffers(source);\r\n\t\tvar points = new List\u003CVector3\u003E();\r\n\t\tvar triangles = new List\u003Cint\u003E();\r\n\t\tfor (int mi = 0; mi \u003C buffers.Count; mi\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar info = source.MeshInfo.Meshes[mi];\r\n\t\t\tif (!info.Name.StartsWith(\u0022head\u0022, StringComparison.OrdinalIgnoreCase)) continue;\r\n\t\t\tvar buffer = buffers[mi];\r\n\t\t\tint callIndex = 0;\r\n\t\t\tforeach (var call in info.DrawCalls)\r\n\t\t\t{\r\n\t\t\t\tint vb = buffer.Vertices.Length == 1 ? 0 : callIndex;\r\n\t\t\t\tint ib = buffer.Indices.Length == 1 ? 0 : callIndex;\r\n\t\t\t\tvar vertices = Vertices(buffer.Vertices[vb], null, buffer.Bones.Length);\r\n\t\t\t\tvar indices = Indices(buffer.Indices[ib]);\r\n\t\t\t\tint offset = points.Count;\r\n\t\t\t\tpoints.AddRange(vertices.Select(v =\u003E v.Position));\r\n\t\t\t\ttriangles.AddRange(indices.Skip(call.StartIndex).Take(call.Indices).Select(i =\u003E i \u002B call.BaseVertex \u002B offset));\r\n\t\t\t\tcallIndex\u002B\u002B;\r\n\t\t\t}\r\n\t\t}\r\n\t\tvar p = points.ToArray();\r\n\t\treturn headSurface = new(p, p, triangles.ToArray(), Array.Empty\u003Cstring\u003E(), Array.Empty\u003C(int, float)[]\u003E());\r\n\t}\r\n\r\n\tstatic Vector3 V(BinaryReader r) =\u003E new(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());\r\n\tstatic Surface ReadSurface(BinaryReader r)\r\n\t{\r\n\t\tvar names = new string[r.ReadInt32()];\r\n\t\tfor (int i = 0; i \u003C names.Length; i\u002B\u002B)\r\n\t\t\tnames[i] = Encoding.UTF8.GetString(r.ReadBytes(r.ReadInt32()));\r\n\t\tvar count = r.ReadInt32();\r\n\t\tvar triangles = new int[r.ReadInt32() * 3];\r\n\t\tvar p = new Vector3[count];\r\n\t\tvar f = new Vector3[count];\r\n\t\tvar weights = new (int, float)[count][];\r\n\t\tfor (int i = 0; i \u003C count; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tp[i] = V(r);\r\n\t\t\tf[i] = V(r);\r\n\t\t\tweights[i] = new (int, float)[r.ReadInt32()];\r\n\t\t\tfor (int j = 0; j \u003C weights[i].Length; j\u002B\u002B)\r\n\t\t\t\tweights[i][j] = (r.ReadInt32(), r.ReadSingle());\r\n\t\t}\r\n\r\n\t\tfor (int i = 0; i \u003C triangles.Length; i\u002B\u002B)\r\n\t\t\ttriangles[i] = r.ReadInt32();\r\n\t\treturn new(p, f, triangles, names, weights);\r\n\t}\r\n\r\n\t[StructLayout(LayoutKind.Sequential)]\r\n\tstruct SkinVertex\r\n\t{\r\n\t\t[VertexLayout.Position]\r\n\t\tpublic Vector3 Position;\r\n\t\t[VertexLayout.Normal]\r\n\t\tpublic Vector3 Normal;\r\n\t\t[VertexLayout.Tangent]\r\n\t\tpublic Vector4 Tangent;\r\n\t\t[VertexLayout.TexCoord(0)]\r\n\t\tpublic Vector4 UV;\r\n\t\t[VertexLayout.TexCoord(1)]\r\n\t\tpublic Vector4 UV2;\r\n\t\t[VertexLayout.Color]\r\n\t\tpublic Color32 Color;\r\n\t\t[VertexLayout.BlendIndices]\r\n\t\tpublic Color32 Indices;\r\n\t\t[VertexLayout.BlendWeight]\r\n\t\tpublic Color32 Weights;\r\n\t}\r\n\r\n\treadonly record struct Attribute(string Name, int Index, int Format, int Offset);\r\n\tsealed record Buffer(int Count, int Stride, Attribute[] Attributes, byte[] Data);\r\n\tsealed record Buffers(Buffer[] Vertices, Buffer[] Indices, string[] Bones);\r\n\t// SaveToVmdl emits unpacked MBUF data, even when the installed resource uses\r\n\t// compressed buffers. Reading that public serialization retains authored weights.\r\n\tstatic List\u003CBuffers\u003E ReadBuffers(Model source)\r\n\t{\r\n\t\tvar data = source.SaveToVmdl();\r\n\t\tusing var r = new BinaryReader(new MemoryStream(data));\r\n\t\tr.BaseStream.Position = 8;\r\n\t\tvar table = 8 \u002B r.ReadInt32();\r\n\t\tvar blocks = r.ReadInt32();\r\n\t\tvar result = new List\u003CBuffers\u003E();\r\n\t\tfor (int i = 0; i \u003C blocks; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tr.BaseStream.Position = table \u002B i * 12;\r\n\t\t\tvar tag = Encoding.ASCII.GetString(r.ReadBytes(4));\r\n\t\t\tvar anchor = (int)r.BaseStream.Position;\r\n\t\t\tvar offset = r.ReadInt32();\r\n\t\t\tvar size = r.ReadInt32();\r\n\t\t\tif (tag == \u0022MDAT\u0022)\r\n\t\t\t{\r\n\t\t\t\tr.BaseStream.Position = anchor \u002B offset;\r\n\t\t\t\tvar bones = SerializedMeshBones.Read(r.ReadBytes(size));\r\n\t\t\t\tresult[^1] = result[^1] with\r\n\t\t\t\t{\r\n\t\t\t\t\tBones = bones\r\n\t\t\t\t};\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tif (tag != \u0022MBUF\u0022)\r\n\t\t\t\tcontinue;\r\n\t\t\tvar start = anchor \u002B offset;\r\n\t\t\tr.BaseStream.Position = start;\r\n\t\t\tvar vo = r.ReadInt32();\r\n\t\t\tvar vc = r.ReadInt32();\r\n\t\t\tvar io = r.ReadInt32();\r\n\t\t\tvar ic = r.ReadInt32();\r\n\t\t\tresult.Add(new(Enumerable.Range(0, vc).Select(j =\u003E ReadBuffer(r, start \u002B vo \u002B j * 24)).ToArray(), Enumerable.Range(0, ic).Select(j =\u003E ReadBuffer(r, start \u002B 8 \u002B io \u002B j * 24)).ToArray(), null));\r\n\t\t}\r\n\r\n\t\tif (result.Count != source.MeshCount)\r\n\t\t\tthrow new InvalidOperationException($\u0022{source.Name}: missing serialized mesh buffers.\u0022);\r\n\t\treturn result;\r\n\t}\r\n\r\n\tstatic Buffer ReadBuffer(BinaryReader r, int position)\r\n\t{\r\n\t\tr.BaseStream.Position = position;\r\n\t\tvar count = r.ReadInt32();\r\n\t\tvar stride = r.ReadInt32() \u0026 0x3ffffff;\r\n\t\tvar ap = (int)r.BaseStream.Position;\r\n\t\tvar ao = r.ReadInt32();\r\n\t\tvar ac = r.ReadInt32();\r\n\t\tvar dp = (int)r.BaseStream.Position;\r\n\t\tvar offset = r.ReadInt32();\r\n\t\tvar bytes = r.ReadInt32();\r\n\t\tif (count \u003C 0 || count \u003E 2000000 || stride \u003C 1 || stride \u003E 1024 || bytes != count * stride)\r\n\t\t\tthrow new InvalidOperationException(\u0022Unexpected serialized human clothing buffer.\u0022);\r\n\t\tvar attributes = new Attribute[ac];\r\n\t\tfor (int i = 0; i \u003C ac; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tr.BaseStream.Position = ap \u002B ao \u002B i * 56;\r\n\t\t\tvar name = Encoding.ASCII.GetString(r.ReadBytes(32)).TrimEnd(\u0027\\0\u0027);\r\n\t\t\tattributes[i] = new(name, r.ReadInt32(), r.ReadInt32(), r.ReadInt32());\r\n\t\t}\r\n\r\n\t\tr.BaseStream.Position = dp \u002B offset;\r\n\t\treturn new(count, stride, attributes, r.ReadBytes(bytes));\r\n\t}\r\n\r\n\tstatic float F(byte[] b, int p) =\u003E BitConverter.ToSingle(b, p);\r\n\tstatic ushort U16(byte[] b, int p) =\u003E BitConverter.ToUInt16(b, p);\r\n\tstatic uint U32(byte[] b, int p) =\u003E BitConverter.ToUInt32(b, p);\r\n\tstatic Color32 Bytes(byte[] b, int p) =\u003E new(b[p], b[p \u002B 1], b[p \u002B 2], b[p \u002B 3]);\r\n\tstatic Vector4 Values(byte[] b, int p, int format)\r\n\t{\r\n\t\tfloat HalfAt(int i) =\u003E (float)BitConverter.UInt16BitsToHalf(U16(b, p \u002B i * 2));\r\n\t\treturn format switch\r\n\t\t{\r\n\t\t\t2 =\u003E new(F(b, p), F(b, p \u002B 4), F(b, p \u002B 8), F(b, p \u002B 12)),\r\n\t\t\t6 =\u003E new(F(b, p), F(b, p \u002B 4), F(b, p \u002B 8), 0),\r\n\t\t\t16 =\u003E new(F(b, p), F(b, p \u002B 4), 0, 0),\r\n\t\t\t10 =\u003E new(HalfAt(0), HalfAt(1), HalfAt(2), HalfAt(3)),\r\n\t\t\t34 =\u003E new(HalfAt(0), HalfAt(1), 0, 0),\r\n\t\t\t28 =\u003E new(b[p] / 255f, b[p \u002B 1] / 255f, b[p \u002B 2] / 255f, b[p \u002B 3] / 255f),\r\n\t\t\t_ =\u003E throw new InvalidOperationException(\u0022Unsupported clothing vertex format \u0022 \u002B format)};\r\n\t}\r\n\r\n\tstatic SkinVertex[] Vertices(Buffer b, Vertex[] native, int boneCount)\r\n\t{\r\n\t\tvar vertices = new SkinVertex[b.Count];\r\n\t\tfor (int i = 0; i \u003C b.Count; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar v = new SkinVertex\r\n\t\t\t{\r\n\t\t\t\tColor = new(255, 255, 255, 255),\r\n\t\t\t\tWeights = new(255, 0, 0, 0)\r\n\t\t\t};\r\n\t\t\tforeach (var a in b.Attributes)\r\n\t\t\t{\r\n\t\t\t\tvar p = i * b.Stride \u002B a.Offset;\r\n\t\t\t\tswitch (a.Name)\r\n\t\t\t\t{\r\n\t\t\t\t\tcase \u0022POSITION\u0022:\r\n\t\t\t\t\t\tvar xyz = Values(b.Data, p, a.Format);\r\n\t\t\t\t\t\tv.Position = new(xyz.x, xyz.y, xyz.z);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \u0022TEXCOORD\u0022:\r\n\t\t\t\t\t\tif (a.Index == 0)\r\n\t\t\t\t\t\t\tv.UV = Values(b.Data, p, a.Format);\r\n\t\t\t\t\t\telse if (a.Index == 1)\r\n\t\t\t\t\t\t\tv.UV2 = Values(b.Data, p, a.Format);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \u0022BLENDINDICES\u0022:\r\n\t\t\t\t\t\tif (a.Format == 30)\r\n\t\t\t\t\t\t\tv.Indices = Bytes(b.Data, p);\r\n\t\t\t\t\t\telse if (a.Format == 12)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar ids = Enumerable.Range(0, 4).Select(j =\u003E (int)U16(b.Data, p \u002B j * 2)).ToArray();\r\n\t\t\t\t\t\t\tif (ids.Any(j =\u003E j \u003E= boneCount || j \u003E 255))\r\n\t\t\t\t\t\t\t\tthrow new InvalidOperationException(\u0022Clothing bone index exceeds fitting layout.\u0022);\r\n\t\t\t\t\t\t\tv.Indices = new((byte)ids[0], (byte)ids[1], (byte)ids[2], (byte)ids[3]);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tthrow new InvalidOperationException(\u0022Unsupported clothing bone-index format \u0022 \u002B a.Format);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \u0022BLENDWEIGHT\u0022:\r\n\t\t\t\t\t\tif (a.Format != 28)\r\n\t\t\t\t\t\t\tthrow new InvalidOperationException(\u0022Unsupported clothing weight format \u0022 \u002B a.Format);\r\n\t\t\t\t\t\tv.Weights = Bytes(b.Data, p);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \u0022COLOR\u0022:\r\n\t\t\t\t\t\tif (a.Format == 28)\r\n\t\t\t\t\t\t\tv.Color = Bytes(b.Data, p);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \u0022NORMAL\u0022:\r\n\t\t\t\t\t\tif (a.Format == 6)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar n = Values(b.Data, p, a.Format);\r\n\t\t\t\t\t\t\tv.Normal = new(n.x, n.y, n.z);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase \u0022TANGENT\u0022:\r\n\t\t\t\t\t\tv.Tangent = Values(b.Data, p, a.Format);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (v.Normal.LengthSquared \u003C .01f \u0026\u0026 native is not null \u0026\u0026 native.Length == b.Count \u0026\u0026 (native[i].Position - v.Position).Length \u003C .001f)\r\n\t\t\t{\r\n\t\t\t\tv.Normal = native[i].Normal;\r\n\t\t\t\tv.Tangent = native[i].Tangent;\r\n\t\t\t}\r\n\r\n\t\t\tif (new[]\r\n\t\t\t{\r\n\t\t\t\tv.Indices.r,\r\n\t\t\t\tv.Indices.g,\r\n\t\t\t\tv.Indices.b,\r\n\t\t\t\tv.Indices.a\r\n\t\t\t}.Any(j =\u003E j \u003E= boneCount))\r\n\t\t\t\tthrow new InvalidOperationException(\u0022Serialized garment bone map does not match its skeleton.\u0022);\r\n\t\t\tvertices[i] = v;\r\n\t\t}\r\n\r\n\t\treturn vertices;\r\n\t}\r\n\r\n\tstatic int[] Indices(Buffer b) =\u003E Enumerable.Range(0, b.Count).Select(i =\u003E b.Stride == 2 ? (int)U16(b.Data, i * 2) : checked((int)U32(b.Data, i * 4))).ToArray();\r\n\tstatic Vector3[] Normals(Vector3[] p, int[] indices)\r\n\t{\r\n\t\tvar n = new Vector3[p.Length];\r\n\t\tfor (int i = 0; i \u002B 2 \u003C indices.Length; i \u002B= 3)\r\n\t\t{\r\n\t\t\tvar a = indices[i];\r\n\t\t\tvar b = indices[i \u002B 1];\r\n\t\t\tvar c = indices[i \u002B 2];\r\n\t\t\tvar cross = Vector3.Cross(p[b] - p[a], p[c] - p[a]);\r\n\t\t\tn[a] \u002B= cross;\r\n\t\t\tn[b] \u002B= cross;\r\n\t\t\tn[c] \u002B= cross;\r\n\t\t}\r\n\r\n\t\treturn n.Select(v =\u003E v.Normal).ToArray();\r\n\t}\r\n\r\n\tstatic Vector4[] Tangents(SkinVertex[] vertices, Vector3[] p, Vector3[] normals, int[] indices)\r\n\t{\r\n\t\tvar t = new Vector3[p.Length];\r\n\t\tvar b = new Vector3[p.Length];\r\n\t\tfor (int i = 0; i \u002B 2 \u003C indices.Length; i \u002B= 3)\r\n\t\t{\r\n\t\t\tint a = indices[i], c = indices[i \u002B 1], d = indices[i \u002B 2];\r\n\t\t\tvar e = p[c] - p[a];\r\n\t\t\tvar f = p[d] - p[a];\r\n\t\t\tvar uv = vertices[c].UV - vertices[a].UV;\r\n\t\t\tvar uw = vertices[d].UV - vertices[a].UV;\r\n\t\t\tfloat determinant = uv.x * uw.y - uv.y * uw.x;\r\n\t\t\tif (MathF.Abs(determinant) \u003C 1e-8f)\r\n\t\t\t\tcontinue;\r\n\t\t\tvar tangent = (e * uw.y - f * uv.y) / determinant;\r\n\t\t\tvar bitangent = (f * uv.x - e * uw.x) / determinant;\r\n\t\t\tforeach (int id in new[]\r\n\t\t\t{\r\n\t\t\t\ta,\r\n\t\t\t\tc,\r\n\t\t\t\td\r\n\t\t\t}\r\n\r\n\t\t\t)\r\n\t\t\t{\r\n\t\t\t\tt[id] \u002B= tangent;\r\n\t\t\t\tb[id] \u002B= bitangent;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn Enumerable.Range(0, p.Length).Select(i =\u003E\r\n\t\t{\r\n\t\t\tvar n = normals[i];\r\n\t\t\tvar tangent = (t[i] - n * Vector3.Dot(n, t[i])).Normal;\r\n\t\t\treturn new Vector4(tangent, Vector3.Dot(Vector3.Cross(n, tangent), b[i]) \u003C 0 ? -1 : 1);\r\n\t\t}).ToArray();\r\n\t}\r\n\r\n\tstatic Model Build(Model source, bool female, bool neckOnly = false, bool fitFace = true)\r\n\t{\r\n\t\tvar buffers = ReadBuffers(source);\r\n\t\tvar builder = Model.Builder.WithName(\u0022automatic-human-fit:\u0022 \u002B source.Name \u002B \u0022:\u0022 \u002B (neckOnly ? \u0022replacement-neck\u0022 : female ? \u0022female\u0022 : \u0022male\u0022));\r\n\t\tfor (int i = 0; i \u003C source.BoneCount; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar transform = source.GetBoneTransform(i);\r\n\t\t\tvar parent = source.GetBoneParent(i);\r\n\t\t\tbuilder.AddBone(source.GetBoneName(i), transform.Position, transform.Rotation, parent \u003C 0 ? null : source.GetBoneName(parent));\r\n\t\t}\r\n\r\n\t\tvar native = source.GetVertices();\r\n\t\tvar baseMaterials = (source.MaterialGroupCount \u003E 0 ? source.GetMaterials(0) : source.Materials).ToArray();\r\n\t\tvar meshMaterials = new List\u003CMaterial\u003E();\r\n\t\tvar work = new List\u003CAction\u003E();\r\n\t\tvar clothMale = new List\u003CVector3\u003E();\r\n\t\tvar clothFemale = new List\u003CVector3\u003E();\r\n\t\tvar clothIndices = new List\u003Cint\u003E();\r\n\t\tvar head = neckOnly || !fitFace ? null : HeadSurface();\r\n\t\tfor (int mi = 0; mi \u003C buffers.Count; mi\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar info = source.MeshInfo.Meshes[mi];\r\n\t\t\tif (neckOnly \u0026\u0026 !info.Name.StartsWith(\u0022head\u0022, StringComparison.OrdinalIgnoreCase)) continue;\r\n\t\t\tvar buffer = buffers[mi];\r\n\t\t\tvar boneMap = buffer.Bones.Select(name =\u003E Enumerable.Range(0, source.BoneCount).First(i =\u003E source.GetBoneName(i) == name)).ToArray();\r\n\t\t\tint callIndex = 0;\r\n\t\t\tforeach (var call in info.DrawCalls)\r\n\t\t\t{\r\n\t\t\t\t// SaveToVmdl emits individual buffers in draw-call order when a mesh uses\r\n\t\t\t\t// separate streams (its metadata currently labels each stream as zero).\r\n\t\t\t\tint vb = buffer.Vertices.Length == 1 ? 0 : callIndex;\r\n\t\t\t\tint ib = buffer.Indices.Length == 1 ? 0 : callIndex;\r\n\t\t\t\tif (vb \u003E= buffer.Vertices.Length || ib \u003E= buffer.Indices.Length)\r\n\t\t\t\t\tthrow new InvalidOperationException(\u0022Unexpected garment draw-call buffer mapping.\u0022);\r\n\t\t\t\tvar verts = Vertices(buffer.Vertices[vb], mi == 0 \u0026\u0026 buffer.Vertices.Length == 1 ? native : null, buffer.Bones.Length);\r\n\t\t\t\tvar indices = Indices(buffer.Indices[ib]);\r\n\t\t\t\tfor (int i = 0; i \u003C verts.Length; i\u002B\u002B)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar ids = verts[i].Indices;\r\n\t\t\t\t\tverts[i].Indices = new((byte)boneMap[ids.r], (byte)boneMap[ids.g], (byte)boneMap[ids.b], (byte)boneMap[ids.a]);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcallIndex\u002B\u002B;\r\n\t\t\t\tvar selectedIndices = indices.Skip(call.StartIndex).Take(call.Indices).Select(i =\u003E i \u002B call.BaseVertex).ToArray();\r\n\t\t\t\tif (neckOnly)\r\n\t\t\t\t\tselectedIndices = Enumerable.Range(0, selectedIndices.Length / 3)\r\n\t\t\t\t\t\t.Where(t =\u003E Enumerable.Range(0, 3).All(j =\u003E verts[selectedIndices[t * 3 \u002B j]].Position.z \u003C= 60))\r\n\t\t\t\t\t\t.SelectMany(t =\u003E selectedIndices.Skip(t * 3).Take(3)).ToArray();\r\n\t\t\t\tif (selectedIndices.Length == 0) continue;\r\n\t\t\t\tvar used = selectedIndices.Distinct().ToArray();\r\n\t\t\t\tvar lookup = used.Select((id, i) =\u003E (id, i)).ToDictionary(x =\u003E x.id, x =\u003E x.i);\r\n\t\t\t\tvar localIndices = selectedIndices.Select(i =\u003E lookup[i]).ToArray();\r\n\t\t\t\tvar v = used.Select(i =\u003E verts[i]).ToArray();\r\n\t\t\t\tvar original = v.Select(x =\u003E x.Position).ToArray();\r\n\t\t\t\tvar male = new Vector3[v.Length];\r\n\t\t\t\tvar target = new Vector3[v.Length];\r\n\t\t\t\tbool skin = neckOnly || call.Material?.Name.Contains(\u0022/bodies/\u0022, StringComparison.OrdinalIgnoreCase) == true;\r\n\t\t\t\tfor (int i = 0; i \u003C v.Length; i\u002B\u002B)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar p = original[i];\r\n\t\t\t\t\tif (!skin \u0026\u0026 !neckOnly \u0026\u0026 fitFace \u0026\u0026 p.z \u003E= 57 \u0026\u0026 p.z \u003C= 73)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar hit = head.Nearest(p);\r\n\t\t\t\t\t\tif (hit.Distance \u003C 1.2f \u0026\u0026 hit.Distance \u003C originalMale.Nearest(p).Distance)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar normal = head.FaceNormal(hit.Triangle);\r\n\t\t\t\t\t\t\tfloat clearance = Vector3.Dot(p - hit.Point, normal);\r\n\t\t\t\t\t\t\t// Close face coverings need space for the difference in falloff\r\n\t\t\t\t\t\t\t// between the skin and fabric under inherited facial deformers.\r\n\t\t\t\t\t\t\tfloat margin = .60f * Math.Clamp((62.2f - p.z) / 1.0f, 0, 1);\r\n\t\t\t\t\t\t\tif (margin \u003E 0) p \u002B= normal * Math.Clamp(margin - clearance, 0, .85f);\r\n\t\t\t\t\t\t\tmale[i] = target[i] = p;\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (p.z \u003E 60)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tmale[i] = p;\r\n\t\t\t\t\t\ttarget[i] = p;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tvar from = (neckOnly ? unifiedMale : female ? unifiedFemale : originalMale).Nearest(p);\r\n\t\t\t\t\tvar mapped = female || neckOnly ? from : unifiedMale.Nearest(from.Point);\r\n\t\t\t\t\tvar m = unifiedMale.Interpolate(mapped.Triangle, mapped.Barycentric);\r\n\t\t\t\t\tvar f = unifiedFemale.Interpolate(mapped.Triangle, mapped.Barycentric);\r\n\t\t\t\t\t// Preserve authored clearance, including compressed skin under clothing.\r\n\t\t\t\t\tvar offset = p - from.Point;\r\n\t\t\t\t\tbool snap = skin \u0026\u0026 offset.Length \u003C .12f;\r\n\t\t\t\t\tmale[i] = m \u002B (snap ? Vector3.Zero : offset);\r\n\t\t\t\t\ttarget[i] = f \u002B (snap ? Vector3.Zero : offset);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tint clothOffset = clothMale.Count;\r\n\t\t\t\tif (!skin)\r\n\t\t\t\t{\r\n\t\t\t\t\tclothIndices.AddRange(localIndices.Select(i =\u003E i \u002B clothOffset));\r\n\t\t\t\t\tclothMale.AddRange(male);\r\n\t\t\t\t\tclothFemale.AddRange(target);\r\n\t\t\t\t}\r\n\r\n\t\t\t\twork.Add(() =\u003E\r\n\t\t\t\t{\r\n\t\t\t\t\tif (!skin)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor (int i = 0; i \u003C v.Length; i\u002B\u002B)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tmale[i] = clothMale[clothOffset \u002B i];\r\n\t\t\t\t\t\t\ttarget[i] = clothFemale[clothOffset \u002B i];\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tvar originalNormals = Normals(original, localIndices);\r\n\t\t\t\t\tvar maleNormals = Normals(male, localIndices);\r\n\t\t\t\t\tvar femaleNormals = Normals(target, localIndices);\r\n\t\t\t\t\tvar tangents = Tangents(v, male, maleNormals, localIndices);\r\n\t\t\t\t\tvar morph = new MorphDelta[v.Length];\r\n\t\t\t\t\tfor (int i = 0; i \u003C v.Length; i\u002B\u002B)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (v[i].Normal.LengthSquared \u003C .01f)\r\n\t\t\t\t\t\t\tv[i].Normal = originalNormals[i];\r\n\t\t\t\t\t\tv[i].Normal = (v[i].Normal \u002B maleNormals[i] - originalNormals[i]).Normal;\r\n\t\t\t\t\t\tv[i].Tangent = tangents[i];\r\n\t\t\t\t\t\tv[i].Position = male[i];\r\n\t\t\t\t\t\tmorph[i] = new(i, target[i] - male[i], femaleNormals[i] - maleNormals[i]);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tvar mesh = new Mesh(call.Material, (MeshPrimitiveType)call.PrimitiveType);\r\n\t\t\t\t\tmesh.UvDensity = call.UvDensity;\r\n\t\t\t\t\tmesh.CreateVertexBuffer(v.Length, v.ToList());\r\n\t\t\t\t\tmesh.CreateIndexBuffer(localIndices.Length, localIndices.ToList());\r\n\t\t\t\t\tmesh.AddMorph(Morph, morph);\r\n\t\t\t\t\tmesh.Bounds = new BBox(male.Concat(target).Aggregate((a, b) =\u003E Vector3.Min(a, b)), male.Concat(target).Aggregate((a, b) =\u003E Vector3.Max(a, b)));\r\n\t\t\t\t\tvar lods = info.LodMask;\r\n\t\t\t\t\tif (source.MeshInfo.LodCount == 0)\r\n\t\t\t\t\t\tbuilder.AddMesh(mesh);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tfor (int lod = 0; lod \u003C source.MeshInfo.LodCount; lod\u002B\u002B)\r\n\t\t\t\t\t\t\tif ((lods \u0026 (1 \u003C\u003C lod)) != 0)\r\n\t\t\t\t\t\t\t\tbuilder.AddMesh(mesh, lod);\r\n\t\t\t\t\tmeshMaterials.Add(call.Material);\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (clothMale.Count \u003E 0)\r\n\t\t{\r\n\t\t\tvar m = clothMale.ToArray();\r\n\t\t\tvar f = clothFemale.ToArray();\r\n\t\t\tvar t = clothIndices.ToArray();\r\n\t\t\tClearTriangles(unifiedMale, m, t);\r\n\t\t\tClearTriangles(unifiedFemale, f, t);\r\n\t\t\tclothMale = m.ToList();\r\n\t\t\tclothFemale = f.ToList();\r\n\t\t}\r\n\r\n\t\tforeach (var build in work)\r\n\t\t\tbuild();\r\n\t\tfor (int group = 0; group \u003C source.MaterialGroupCount; group\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar materials = source.GetMaterials(group).ToArray();\r\n\t\t\tvar g = builder.AddMaterialGroup(source.GetMaterialGroupName(group));\r\n\t\t\tforeach (var material in meshMaterials)\r\n\t\t\t{\r\n\t\t\t\tvar index = Array.IndexOf(baseMaterials, material);\r\n\t\t\t\tg.AddMaterial(index \u003E= 0 \u0026\u0026 index \u003C materials.Length ? materials[index] : material);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfor (int lod = 1; lod \u003C source.MeshInfo.LodSwitchDistances.Length; lod\u002B\u002B)\r\n\t\t\tbuilder.WithLodDistance(lod, source.MeshInfo.LodSwitchDistances[lod]);\r\n\t\treturn builder.Create();\r\n\t}\r\n\r\n\tstatic void ClearTriangles(Surface body, Vector3[] points, int[] triangles)\r\n\t{\r\n\t\t// A smooth spatial clearance field moves folded/layered fabric together.\r\n\t\t// Local per-triangle pushes can pull an inner layer through its outer flap.\r\n\t\tvar source = points.ToArray();\r\n\t\tvar min = points.Aggregate((a, b) =\u003E Vector3.Min(a, b)) - Vector3.One * 1.2f;\r\n\t\tvar max = points.Aggregate((a, b) =\u003E Vector3.Max(a, b)) \u002B Vector3.One * 1.2f;\r\n\t\tvar candidates = body.Points.Where(p =\u003E p.x \u003E= min.x \u0026\u0026 p.x \u003C= max.x \u0026\u0026 p.y \u003E= min.y \u0026\u0026 p.y \u003C= max.y \u0026\u0026 p.z \u003E= min.z \u0026\u0026 p.z \u003C= max.z).ToArray();\r\n\t\tvar pushes = new List\u003C(Vector3 Point, Vector3 Push)\u003E();\r\n\t\tvar fabric = new Surface(points, points, triangles, Array.Empty\u003Cstring\u003E(), Array.Empty\u003C(int, float)[]\u003E());\r\n\t\tforeach (var p in candidates)\r\n\t\t{\r\n\t\t\tvar hit = fabric.Nearest(p);\r\n\t\t\tif (hit.Distance \u003E 1.2f)\r\n\t\t\t\tcontinue;\r\n\t\t\tvar w = hit.Barycentric;\r\n\t\t\tif (MathF.Min(w.x, MathF.Min(w.y, w.z)) \u003C .015f)\r\n\t\t\t\tcontinue;\r\n\t\t\tvar n = body.FaceNormal(body.Nearest(p).Triangle);\r\n\t\t\tif (MathF.Abs(Vector3.Dot(n, fabric.FaceNormal(hit.Triangle))) \u003C .25f)\r\n\t\t\t\tcontinue;\r\n\t\t\tvar penetration = Vector3.Dot(p - hit.Point, n) \u002B .14f;\r\n\t\t\tif (penetration \u003C= 0 || penetration \u003E 1.2f)\r\n\t\t\t\tcontinue;\r\n\t\t\tpushes.Add((hit.Point, n * MathF.Min(.7f, penetration \u002B .04f)));\r\n\t\t}\r\n\r\n\t\t// Spatial buckets bound the search and apply identical displacement to UV\r\n\t\t// duplicates, overlapping material sections, and neighbouring LOD vertices.\r\n\t\tconst float radius = 2.5f;\r\n\t\t(int, int, int) Cell(Vector3 p) =\u003E ((int)MathF.Floor(p.x / radius), (int)MathF.Floor(p.y / radius), (int)MathF.Floor(p.z / radius));\r\n\t\tvar buckets = pushes.GroupBy(p =\u003E Cell(p.Point)).ToDictionary(g =\u003E g.Key, g =\u003E g.ToArray());\r\n\t\tfor (int i = 0; i \u003C points.Length; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar cell = Cell(source[i]);\r\n\t\t\tVector3 best = default;\r\n\t\t\tfor (int x = -1; x \u003C= 1; x\u002B\u002B)\r\n\t\t\t\tfor (int y = -1; y \u003C= 1; y\u002B\u002B)\r\n\t\t\t\t\tfor (int z = -1; z \u003C= 1; z\u002B\u002B)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (!buckets.TryGetValue((cell.Item1 \u002B x, cell.Item2 \u002B y, cell.Item3 \u002B z), out var values))\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\tforeach (var p in values)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tvar d = (source[i] - p.Point).Length / radius;\r\n\t\t\t\t\t\t\tif (d \u003E= 1)\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\tvar falloff = 1 - d * d * (3 - 2 * d);\r\n\t\t\t\t\t\t\tvar push = p.Push * falloff;\r\n\t\t\t\t\t\t\tif (push.LengthSquared \u003E best.LengthSquared)\r\n\t\t\t\t\t\t\t\tbest = push;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\tpoints[i] \u002B= best;\r\n\t\t}\r\n\t}\r\n\r\n\treadonly record struct Hit(int Triangle, Vector3 Barycentric, Vector3 Point, float Distance);\r\n\tsealed class Surface\r\n\t{\r\n\t\tpublic readonly Vector3[] Points, Female;\r\n\t\tpublic readonly int[] Triangles;\r\n\t\tpublic readonly string[] Names;\r\n\t\tpublic readonly (int, float)[][] Weights;\r\n\t\treadonly Node tree;\r\n\t\tsealed class Node\r\n\t\t{\r\n\t\t\tpublic Vector3 Min, Max;\r\n\t\t\tpublic Node Left, Right;\r\n\t\t\tpublic int[] Faces;\r\n\t\t}\r\n\r\n\t\tpublic Surface(Vector3[] p, Vector3[] f, int[] t, string[] names, (int, float)[][] weights)\r\n\t\t{\r\n\t\t\tPoints = p;\r\n\t\t\tFemale = f;\r\n\t\t\tTriangles = t;\r\n\t\t\tNames = names;\r\n\t\t\tWeights = weights;\r\n\t\t\ttree = BuildTree(Enumerable.Range(0, t.Length / 3).ToArray());\r\n\t\t}\r\n\r\n\t\tVector3 A(int t, int corner) =\u003E Points[Triangles[t * 3 \u002B corner]];\r\n\t\tNode BuildTree(int[] faces)\r\n\t\t{\r\n\t\t\tvar p = faces.SelectMany(i =\u003E new[] { A(i, 0), A(i, 1), A(i, 2) }).ToArray();\r\n\t\t\tvar node = new Node\r\n\t\t\t{\r\n\t\t\t\tMin = p.Aggregate((a, b) =\u003E Vector3.Min(a, b)),\r\n\t\t\t\tMax = p.Aggregate((a, b) =\u003E Vector3.Max(a, b))\r\n\t\t\t};\r\n\t\t\tif (faces.Length \u003C= 12)\r\n\t\t\t{\r\n\t\t\t\tnode.Faces = faces;\r\n\t\t\t\treturn node;\r\n\t\t\t}\r\n\r\n\t\t\tvar extent = node.Max - node.Min;\r\n\t\t\tint axis = extent.x \u003E extent.y ? (extent.x \u003E extent.z ? 0 : 2) : (extent.y \u003E extent.z ? 1 : 2);\r\n\t\t\tfloat Coord(Vector3 v) =\u003E axis == 0 ? v.x : axis == 1 ? v.y : v.z;\r\n\t\t\tvar sorted = faces.OrderBy(i =\u003E Coord(A(i, 0) \u002B A(i, 1) \u002B A(i, 2))).ToArray();\r\n\t\t\tint half = sorted.Length / 2;\r\n\t\t\tnode.Left = BuildTree(sorted.Take(half).ToArray());\r\n\t\t\tnode.Right = BuildTree(sorted.Skip(half).ToArray());\r\n\t\t\treturn node;\r\n\t\t}\r\n\r\n\t\tpublic Vector3 Interpolate(int t, Vector3 w) =\u003E A(t, 0) * w.x \u002B A(t, 1) * w.y \u002B A(t, 2) * w.z;\r\n\t\tpublic Vector3 FaceNormal(int t) =\u003E Vector3.Cross(A(t, 1) - A(t, 0), A(t, 2) - A(t, 0)).Normal;\r\n\t\tstatic float Distance(Node n, Vector3 p) =\u003E (p - Vector3.Max(n.Min, Vector3.Min(n.Max, p))).LengthSquared;\r\n\t\tpublic Hit Nearest(Vector3 p)\r\n\t\t{\r\n\t\t\tfloat best = float.MaxValue;\r\n\t\t\tint face = 0;\r\n\t\t\tVector3 bary = default, point = default;\r\n\t\t\tvoid Search(Node node)\r\n\t\t\t{\r\n\t\t\t\tif (Distance(node, p) \u003E best)\r\n\t\t\t\t\treturn;\r\n\t\t\t\tif (node.Faces is null)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar a = node.Left;\r\n\t\t\t\t\tvar b = node.Right;\r\n\t\t\t\t\tif (Distance(a, p) \u003E Distance(b, p))\r\n\t\t\t\t\t\t(a, b) = (b, a);\r\n\t\t\t\t\tSearch(a);\r\n\t\t\t\t\tSearch(b);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tforeach (var t in node.Faces)\r\n\t\t\t\t{\r\n\t\t\t\t\tvar w = Closest(p, A(t, 0), A(t, 1), A(t, 2));\r\n\t\t\t\t\tvar q = Interpolate(t, w);\r\n\t\t\t\t\tvar distance = (p - q).LengthSquared;\r\n\t\t\t\t\tif (distance \u003C best)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbest = distance;\r\n\t\t\t\t\t\tface = t;\r\n\t\t\t\t\t\tbary = w;\r\n\t\t\t\t\t\tpoint = q;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tSearch(tree);\r\n\t\t\treturn new(face, bary, point, MathF.Sqrt(best));\r\n\t\t}\r\n\r\n\t\tstatic Vector3 Closest(Vector3 p, Vector3 a, Vector3 b, Vector3 c)\r\n\t\t{\r\n\t\t\tvar ab = b - a;\r\n\t\t\tvar ac = c - a;\r\n\t\t\tvar ap = p - a;\r\n\t\t\tfloat d1 = Vector3.Dot(ab, ap), d2 = Vector3.Dot(ac, ap);\r\n\t\t\tif (d1 \u003C= 0 \u0026\u0026 d2 \u003C= 0)\r\n\t\t\t\treturn new(1, 0, 0);\r\n\t\t\tvar bp = p - b;\r\n\t\t\tfloat d3 = Vector3.Dot(ab, bp), d4 = Vector3.Dot(ac, bp);\r\n\t\t\tif (d3 \u003E= 0 \u0026\u0026 d4 \u003C= d3)\r\n\t\t\t\treturn new(0, 1, 0);\r\n\t\t\tfloat vc = d1 * d4 - d3 * d2;\r\n\t\t\tif (vc \u003C= 0 \u0026\u0026 d1 \u003E= 0 \u0026\u0026 d3 \u003C= 0)\r\n\t\t\t{\r\n\t\t\t\tvar v = d1 / (d1 - d3);\r\n\t\t\t\treturn new(1 - v, v, 0);\r\n\t\t\t}\r\n\r\n\t\t\tvar cp = p - c;\r\n\t\t\tfloat d5 = Vector3.Dot(ab, cp), d6 = Vector3.Dot(ac, cp);\r\n\t\t\tif (d6 \u003E= 0 \u0026\u0026 d5 \u003C= d6)\r\n\t\t\t\treturn new(0, 0, 1);\r\n\t\t\tfloat vb = d5 * d2 - d1 * d6;\r\n\t\t\tif (vb \u003C= 0 \u0026\u0026 d2 \u003E= 0 \u0026\u0026 d6 \u003C= 0)\r\n\t\t\t{\r\n\t\t\t\tvar w = d2 / (d2 - d6);\r\n\t\t\t\treturn new(1 - w, 0, w);\r\n\t\t\t}\r\n\r\n\t\t\tfloat va = d3 * d6 - d5 * d4;\r\n\t\t\tif (va \u003C= 0 \u0026\u0026 d4 - d3 \u003E= 0 \u0026\u0026 d5 - d6 \u003E= 0)\r\n\t\t\t{\r\n\t\t\t\tvar w = (d4 - d3) / (d4 - d3 \u002B d5 - d6);\r\n\t\t\t\treturn new(0, 1 - w, w);\r\n\t\t\t}\r\n\r\n\t\t\tfloat sum = va \u002B vb \u002B vc;\r\n\t\t\tif (MathF.Abs(sum) \u003C 1e-12f)\r\n\t\t\t\treturn new(1, 0, 0);\r\n\t\t\treturn new(va / sum, vb / sum, vc / sum);\r\n\t\t}\r\n\t}\r\n}// Runtime fitting integration.\r\n\r\n"},{"Ident":"vendetta.characterkit","Path":"CharacterKit/CharacterAppearance.cs","FileName":"CharacterAppearance.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\n\r\nnamespace CharacterEditor;\r\npublic static class CharacterAppearance\r\n{\r\n\tpublic static string ValidateForApply(CharacterAppearanceData data)\r\n\t{\r\n\t\tif (CharacterAppearanceData.Validate(data)is string error)\r\n\t\t\treturn error;\r\n\t\tvar skin = ClothingResources.Get(UnifiedHuman.PresentationSkin(data));\r\n\t\tif (skin?.HasHumanSkin != true)\r\n\t\t\treturn \u0022The selected native skin resource is unavailable.\u0022;\r\n\t\tvar body = Model.Load(data.BaseShape is null ? HumanMarkingModels.ModelPath(skin.HumanSkinModel, data.Makeup) : UnifiedHuman.ModelPath);\r\n\t\tif (!body.IsValid || body.IsError)\r\n\t\t\treturn \u0022The selected native human model is unavailable.\u0022;\r\n\t\tvar clothes = new List\u003CClothing\u003E\r\n\t\t{\r\n\t\t\tskin\r\n\t\t};\r\n\t\tforeach (var path in new[]\r\n\t\t{\r\n\t\t\tdata.HairPath,\r\n\t\t\tdata.FacialHairPath\r\n\t\t}.Where(x =\u003E !string.IsNullOrEmpty(x)))\r\n\t\t{\r\n\t\t\tvar clothing = ClothingResources.Get(path);\r\n\t\t\tif (clothing is null)\r\n\t\t\t\treturn \u0022A selected native hairstyle resource is unavailable.\u0022;\r\n\t\t\tif (path == data.HairPath \u0026\u0026 !ClothingResources.IsHair(clothing) || path == data.FacialHairPath \u0026\u0026 !ClothingResources.IsFacialHair(clothing))\r\n\t\t\t\treturn \u0022The selected item does not belong in this hair slot.\u0022;\r\n\t\t\tclothes.Add(clothing);\r\n\t\t}\r\n\r\n\t\tvar tags = new TagSet(skin.HumanSkinTags.TryGetAll());\r\n\t\ttags.Add(\u0022human\u0022);\r\n\t\tforeach (var clothing in clothes.Skip(1))\r\n\t\t{\r\n\t\t\tvar path = clothing.GetModel(clothes, tags);\r\n\t\t\tif (string.IsNullOrEmpty(path))\r\n\t\t\t\treturn \u0022A selected hairstyle does not support this native human body.\u0022;\r\n\t\t\tvar model = Model.Load(path);\r\n\t\t\tif (!model.IsValid || model.IsError)\r\n\t\t\t\treturn \u0022A selected native hairstyle model is unavailable.\u0022;\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"CharacterKit/CharacterCreatorPanel.razor","FileName":"CharacterCreatorPanel.razor","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"@using Sandbox\r\n\r\n@using Sandbox.UI\r\n\r\n@using System\r\n\r\n@using System.Text.Json\r\n\r\n@using System.Threading.Tasks\r\n\r\n@namespace CharacterEditor\r\n\r\n@inherits Panel\r\n\r\n\u003Croot class=\u0022interactive creatoroverlay\u0022\u003E\r\n\r\n @if(true)\r\n\r\n {\r\n\r\n  \u003Cdiv class=\u0022creatorwindow\u0022\u003E\r\n\r\n   \u003Cdiv class=\u0022creatorheader\u0022\u003E\u003Cdiv\u003E\u003Cdiv class=\u0022creatorbreadcrumb\u0022\u003E\u003Cspan\u003E@ProductLabel\u003C/span\u003E\u003Cspan class=\u0022material-icons\u0022\u003Echevron_right\u003C/span\u003E\u003Cspan\u003E@LocationLabel\u003C/span\u003E\u003C/div\u003E\u003Cdiv class=\u0022creatortitle\u0022\u003E@Title\u003C/div\u003E\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton quiet cancelcreation\u0022 onclick=@Back\u003E\u003Cspan class=\u0022material-icons\u0022\u003Earrow_back\u003C/span\u003EBack\u003C/button\u003E\u003C/div\u003E\r\n\r\n   \u003Cdiv class=\u0022creatorbody\u0022\u003E\r\n\r\n    \u003Cdiv class=\u0022creatorcontrols\u0022\u003E\r\n\r\n     \u003Cdiv class=\u0022creatortabs\u0022\u003E@foreach(var tab in Tabs){\u003Cbutton class=@($\u0022creatorbutton creatortab {(Tab==tab?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChangeTab(tab))\u003E@tab\u003C/button\u003E}\u003C/div\u003E\r\n\r\n     \u003Cdiv class=@($\u0022creatoroptions {(Tab==\u0022Voice\u0022?\u0022voiceoptions\u0022:\u0022\u0022)}\u0022)\u003E\r\n\r\n      \u003CReferenceFacePanel style=@(Tab==\u0022Photos\u0022?\u0022display: flex;\u0022:\u0022display: none;\u0022) Appearance=@Appearance Generated=@AddPhotoFace /\u003E\r\n      @if(Tab==\u0022Presets\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EChoose a starting look\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EStart with a preset, then make it your own.\u003C/div\u003E\u003Cdiv class=\u0022presetgrid\u0022\u003E\r\n\r\n        @foreach(var preset in AppearanceCatalog.Presets){\u003Cbutton class=@($\u0022creatorbutton presetchoice {(ChosenPreset==preset.Id?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EApplyPreset(preset.Id))\u003E\u003Cdiv class=\u0022presetportrait\u0022\u003E\u003CCharacterCreatorPreview Appearance=@preset.Appearance Portrait=@true /\u003E\u003C/div\u003E\u003Cdiv class=\u0022presetlabel\u0022\u003E@preset.Label\u003C/div\u003E\u003C/button\u003E}\r\n\r\n       \u003C/div\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Face \u0026 body\u0022)\r\n\r\n      {\r\n\r\n       @if(Appearance.BaseShape is not null)\r\n       {\r\n        \u003Cdiv class=\u0022optionheading\u0022\u003EBlend your base face\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EOverall sets the whole face. In regional tabs, blend that result with individual faces for that area. Refine the result in Shape; choose textures in Skin.\u003C/div\u003E\r\n        \u003Cdiv class=\u0022blendtabs\u0022\u003E@foreach(var region in new[]{\u0022overall\u0022}.Concat(FaceRegions.Ids)){\u003Cbutton class=@($\u0022creatorbutton blendtab blend-tab-{region} {(BlendRegion==region?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EBlendRegion=region)\u003E@FaceRegions.Label(region)\u003C/button\u003E}\u003C/div\u003E\r\n        @if(BlendRegion!=\u0022overall\u0022)\r\n        {\r\n         \u003Cdiv class=\u0022regionstatus\u0022\u003E\u003Cspan\u003EMix the live Overall blend with individual faces. Relative weights determine each share.\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet reset-region\u0022 onclick=@ResetRegion\u003EReset region\u003C/button\u003E\u003C/div\u003E\r\n         \u003Cdiv class=\u0022overallblendrow\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EOverall blend\u003C/span\u003E\u003Cspan\u003E@($\u0022{MathF.Round(OverallWeight*100)} influence \u00B7 {MathF.Round(FaceRegions.OverallShare(Appearance.BaseShape,BlendRegion)*100)}% of region\u0022)\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider region-overall-weight\u0022 Value=@OverallWeight OnValueChanged=@SetOverallWeight Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EFollows changes made in the Overall tab.\u003C/div\u003E\u003C/div\u003E\r\n        }\r\n        @for(var i=0;i\u003C7;i\u002B\u002B)\r\n        {\r\n         var index=i;\r\n         \u003Cdiv class=\u0022shaperow\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EFace @(index\u002B1)\u003C/span\u003E\u003Cspan\u003E@BlendWeightLabel(index)\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider head-weight-{index\u002B1}\u0022) Value=@HeadWeight(index) OnValueChanged=@((float value)=\u003ESetHeadWeight(index,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n        }\r\n        @foreach(var face in Appearance.BaseShape.PhotoFaces)\r\n        {\r\n         \u003Cdiv class=\u0022photoblendcard\u0022\u003E\r\n          \u003Cdiv class=\u0022photoblendheader\u0022\u003E\u003Cspan class=\u0022photoblendname\u0022\u003E@face.Name\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet delete-photo-face\u0022 tooltip=\u0022Delete this custom face from the character and all regions\u0022 onclick=@(()=\u003EFaceRegions.RemovePhoto(Appearance.BaseShape,face))\u003E\u003Cspan class=\u0022material-icons\u0022\u003Edelete_outline\u003C/span\u003E\u003C/button\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EBlend influence\u003C/span\u003E\u003Cspan\u003E@($\u0022{MathF.Round(PhotoWeight(face)*100)} influence \u00B7 {MathF.Round(FaceRegions.PhotoEntryShare(Appearance.BaseShape,face,BlendRegion)*100)}% of blend\u0022)\u003C/span\u003E\u003C/div\u003E\r\n          \u003CSliderControl class=\u0022shapeslider photo-face-weight\u0022 Value=@PhotoWeight(face) OnValueChanged=@((float value)=\u003ESetPhotoWeight(face,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\r\n          @if(BlendRegion==\u0022overall\u0022)\r\n          {\r\n           \u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EPhoto detail strength\u003C/span\u003E\u003Cspan\u003E@MathF.Round(face.Strength*100)%\u003C/span\u003E\u003C/div\u003E\r\n           \u003CSliderControl class=\u0022shapeslider photo-face-strength\u0022 Value=@face.Strength OnValueChanged=@((float value)=\u003E{face.Strength=Math.Clamp(value,0,2);}) Min=@(0f) Max=@(2f) Step=@(.01f) ShowTextEntry=@false /\u003E\r\n           \u003Cdiv class=\u0022optiondescription\u0022\u003E100% is the fitted shape. Higher values exaggerate its slider changes, within normal Shape limits. This also affects regions using this face.\u003C/div\u003E\r\n          }\r\n         \u003C/div\u003E\r\n        }\r\n        @if(Appearance.BaseShape.TotalInfluence==0){\u003Cdiv class=\u0022optiondescription\u0022\u003EAll influences are zero: using Face 3 as the neutral base.\u003C/div\u003E}\r\n        \u003Cdiv class=\u0022optionheading\u0022\u003EBase body\u003C/div\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@MathF.Round((1-Appearance.BaseShape.MaleBody)*100)% female\u003C/span\u003E\u003Cspan\u003E@MathF.Round(Appearance.BaseShape.MaleBody*100)% male\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider base-body\u0022 Value=@Appearance.BaseShape.MaleBody OnValueChanged=@SetBodyBlend Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\r\n        @foreach(var variant in new[]{\u0022male\u0022,\u0022female\u0022})\r\n        {\r\n         \u003Cbutton class=@($\u0022creatorbutton quiet clothing-prefer-{variant}\u0022) role=\u0022checkbox\u0022 aria-checked=@(Appearance.BaseShape.ClothingVariant==variant?\u0022true\u0022:\u0022false\u0022) onclick=@(()=\u003EToggleClothingVariant(variant))\u003E\u003Cspan class=\u0022material-icons\u0022\u003E@(Appearance.BaseShape.ClothingVariant==variant?\u0022check_box\u0022:\u0022check_box_outline_blank\u0022)\u003C/span\u003EPrefer @variant clothing version\u003C/button\u003E\r\n        }\r\n        \u003Cdiv class=\u0022optiondescription\u0022\u003ENeither selected: use the body blend. Available versions still fit your body shape.\u003C/div\u003E\r\n        \u003Cdiv class=\u0022optiondescription\u0022\u003EBody blend is independent of face and skin. Breast adjustments fade with the female amount; bulge adjustments fade with the male amount.\u003C/div\u003E\r\n       }\r\n       else\r\n       {\r\n        \u003Cdiv class=\u0022optionheading\u0022\u003ENative base\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EThis saved character uses its original native face and body.\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton\u0022 onclick=@EnableBlending\u003EEnable weighted base blending\u003C/button\u003E\r\n        \u003Cdiv class=\u0022bodychoices\u0022\u003E\u003Cbutton class=\u0022creatorbutton\u0022 onclick=@(()=\u003EChooseBody(\u0022male\u0022))\u003EMasculine\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton\u0022 onclick=@(()=\u003EChooseBody(\u0022female\u0022))\u003EFeminine\u003C/button\u003E\u003C/div\u003E\u003Cdiv class=\u0022skingrid\u0022\u003E@foreach(var skin in AppearanceCatalog.Skins.Where(x=\u003Ex.BodyType==BodyType)){\u003Cbutton class=\u0022creatorbutton skinchoice\u0022 onclick=@(()=\u003EChooseSkin(skin))\u003E@FaceLabel(skin)\u003C/button\u003E}\u003C/div\u003E\r\n       }\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EEye color\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EColor the iris while keeping the pupil and whites of the eyes.\u003C/div\u003E\r\n       \u003Cdiv class=\u0022colorchoices\u0022\u003E\u003Cbutton class=@($\u0022creatorbutton colorchoice eyecolorchoice {(!Appearance.CustomEyeColor?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseEyeColor(null))\u003ENative\u003C/button\u003E@foreach(var color in EyeColors){\u003Cbutton class=@($\u0022creatorbutton colorchoice eyecolorchoice {(Appearance.CustomEyeColor \u0026\u0026 Appearance.EyeColor==color.Color?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseEyeColor(color.Color))\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n       \u003Cbutton class=\u0022creatorbutton quiet eyecustomtoggle\u0022 onclick=@(()=\u003EChooseEyeColor(Appearance.EyeColor))\u003E\u003Cspan class=\u0022material-icons\u0022\u003Epalette\u003C/span\u003ECustom eye color\u003C/button\u003E\r\n       @if(Appearance.CustomEyeColor){@foreach(var channel in new[]{\u0022Red\u0022,\u0022Green\u0022,\u0022Blue\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@channel\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider eye-{channel.ToLowerInvariant()}\u0022) Value=@EyeChannel(channel) OnValueChanged=@((float value)=\u003ESetEyeChannel(channel,value)) Min=@(0f) Max=@(1f) Step=@(1f/255) ShowTextEntry=@false /\u003E\u003C/div\u003E}}\r\n      }\r\n\r\n      else if(Tab==\u0022Skin\u0022)\r\n\r\n      {\r\n\r\n       @if(Appearance.BaseShape is not null){\u003Cdiv class=\u0022optionheading\u0022\u003ESkin details\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EChoose the native texture set: pigment, pores, freckles and other details. This does not change your face weights or body shape.\u003C/div\u003E\u003Cdiv class=\u0022skingrid\u0022\u003E@foreach(var skin in AppearanceCatalog.Skins){\u003Cbutton class=@($\u0022creatorbutton skinchoice {(Appearance.SkinPath==skin.ResourcePath?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseSkin(skin))\u003ESkin @skin.Id.Replace(\u0022human\u0022,\u0022\u0022)\u003C/button\u003E}\u003C/div\u003E}\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003ESkin color\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EChoose a complexion independently of face and body. Native restores the selected skin\u0027s original color.\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E\u003Cbutton class=@($\u0022creatorbutton skinpalette {(SkinPalette==\u0022Human\u0022?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESkinPalette=\u0022Human\u0022)\u003EHuman\u003C/button\u003E\u003Cbutton class=@($\u0022creatorbutton skinpalette {(SkinPalette==\u0022Fantasy\u0022?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESkinPalette=\u0022Fantasy\u0022)\u003EFantasy \u0026amp; custom\u003C/button\u003E\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022colorchoices\u0022\u003E@foreach(var color in SkinPalette==\u0022Human\u0022?AppearanceCatalog.HumanSkinColors:AppearanceCatalog.FantasySkinColors){\u003Cbutton class=@($\u0022creatorbutton colorchoice skincolorchoice skincolor-{color.Id} {(Appearance.SkinColor==color.Color \u0026\u0026 Appearance.NormalizeSkinTone==color.NormalizeSkinTone?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseSkinColor(color.Color,color.NormalizeSkinTone)) tooltip=@color.Label\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n\r\n       @if(SkinPalette==\u0022Fantasy\u0022)\r\n\r\n       {\r\n\r\n        \u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003ECustom color\u003C/div\u003E\u003Cdiv class=\u0022skincolorpreview\u0022\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(Appearance.SkinColor)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@($\u0022RGB {MathF.Round(Appearance.SkinColorR*255)} \u00B7 {MathF.Round(Appearance.SkinColorG*255)} \u00B7 {MathF.Round(Appearance.SkinColorB*255)}\u0022)\u003C/span\u003E\u003C/div\u003E\r\n\r\n        @foreach(var channel in new[]{\u0022Red\u0022,\u0022Green\u0022,\u0022Blue\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@channel\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider skin-{channel.ToLowerInvariant()}\u0022) Value=@SkinChannel(channel) OnValueChanged=@((float value)=\u003ESetSkinChannel(channel,value)) Min=@(0f) Max=@(1f) Step=@(1f/255) ShowTextEntry=@false /\u003E\u003C/div\u003E}\r\n\r\n       }\r\n\r\n       \u003Cbutton class=\u0022creatorbutton quiet resetshape resetskincolor\u0022 onclick=@(()=\u003EChooseSkinColor(Color.White))\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003EReset skin color\u003C/button\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Makeup\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EMakeup \u0026amp; markings\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EMake it yours. Choose None to leave any makeup layer bare.\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E@foreach(var layer in MakeupCatalog.Layers){\u003Cbutton class=@($\u0022creatorbutton makeuplayer {(MakeupLayer==layer?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMakeupLayer(layer))\u003E@(layer==\u0022Tattoo\u0022?\u0022Markings\u0022:layer)\u003C/button\u003E}\u003C/div\u003E\r\n\r\n       @if(MakeupLayer==\u0022Tattoo\u0022)\r\n\r\n       {\r\n\r\n        \u003Cdiv class=\u0022markinglayers\u0022\u003E\u003Cdiv class=\u0022markinglist\u0022\u003E@for(var i=0;i\u003CAppearance.Makeup.Markings.Count;i\u002B\u002B){var index=i;var mark=Appearance.Makeup.Markings[i];\u003Cbutton class=@($\u0022creatorbutton markinglayer {(SelectedMarking==i?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESelectMarking(index))\u003E@($\u0022{i\u002B1} \u00B7 {MarkingCatalog.Style(mark.Style).Label}\u0022)\u003C/button\u003E}\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton addmarking\u0022 onclick=@AddMarking tooltip=\u0022Add an independent marking layer\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eadd\u003C/span\u003EAdd marking\u003C/button\u003E\u003C/div\u003E\r\n\r\n        @if(CurrentMarking is null)\r\n\r\n        {\u003Cdiv class=\u0022markingempty\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Ebrush\u003C/span\u003E\u003Cdiv class=\u0022fieldlabel\u0022\u003ENone\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EAdd a marking to choose its design, location and ink. Combine up to six.\u003C/div\u003E\u003C/div\u003E}\r\n\r\n        else\r\n\r\n        {\r\n\r\n         \u003Cdiv class=\u0022markingeditorheading\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet inspectmarking\u0022 onclick=@InspectMarking tooltip=\u0022Focus the preview on this marking\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Ecenter_focus_strong\u003C/span\u003E@MarkingCatalog.Region(CurrentMarking.Region).Label\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet deletemarking\u0022 onclick=@DeleteMarking tooltip=\u0022Delete this marking layer\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Edelete_outline\u003C/span\u003EDelete layer\u003C/button\u003E\u003C/div\u003E\r\n         \u003Cdiv class=\u0022markingsections\u0022\u003E@foreach(var section in new[]{\u0022Design\u0022,\u0022Placement\u0022,\u0022Ink\u0022}){\u003Cbutton class=@($\u0022creatorbutton markingsection {(MarkingSection==section?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EMarkingSection=section)\u003E@section\u003C/button\u003E}\u003C/div\u003E\r\n\r\n         @if(MarkingSection==\u0022Design\u0022)\r\n\r\n         {\r\n\r\n          \u003Cdiv class=\u0022markingdesigns\u0022\u003E@foreach(var style in MarkingCatalog.Styles){\u003Cbutton class=@($\u0022creatorbutton markingstyle style-{style.Id} {(CurrentMarking.Style==style.Id?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMarkingStyle(style.Id))\u003E\u003Cspan class=\u0022material-icons\u0022\u003E@MarkingSymbol(style.Id)\u003C/span\u003E\u003Cspan\u003E@style.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n\r\n          \u003Cdiv class=\u0022fieldlabel markinglocationlabel\u0022\u003ELocation\u003C/div\u003E\u003Cdiv class=\u0022markinglocations\u0022\u003E@foreach(var region in MarkingCatalog.Locations(CurrentMarking.Style)){\u003Cbutton class=@($\u0022creatorbutton markinglocation location-{region.Id} {(CurrentMarking.Region==region.Id?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMarkingRegion(region.Id))\u003E@region.Label\u003C/button\u003E}\u003C/div\u003E\r\n\r\n         }\r\n\r\n         else if(MarkingSection==\u0022Placement\u0022)\r\n\r\n         {\r\n\r\n          \u003Cdiv class=\u0022optiondescription\u0022\u003E@(MarkingCatalog.Style(CurrentMarking.Style).Band?\u0022This band wraps around the selected region. Slide it along the body and adjust its size and barb spacing.\u0022:\u0022Move, turn and resize this marking within the selected region.\u0022)\u003C/div\u003E\r\n\r\n          \u003Cdiv class=\u0022markingplacement\u0022\u003E@foreach(var control in MarkingCatalog.Style(CurrentMarking.Style).Band?new[]{\u0022Vertical\u0022,\u0022Scale\u0022,\u0022Spacing\u0022}:new[]{\u0022Horizontal\u0022,\u0022Vertical\u0022,\u0022Scale\u0022,\u0022Rotation\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@(control==\u0022Vertical\u0022 \u0026\u0026 MarkingCatalog.Style(CurrentMarking.Style).Band?\u0022Position along region\u0022:control==\u0022Scale\u0022?\u0022Size\u0022:control==\u0022Spacing\u0022?\u0022Barb spacing\u0022:control)\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider marking-{control.ToLowerInvariant()}\u0022) Value=@MarkingValue(control) OnValueChanged=@((float value)=\u003ESetMarkingValue(control,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E}\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton quiet resetmarkingplacement\u0022 onclick=@ResetMarkingPlacement\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003EReset placement\u003C/button\u003E\r\n\r\n         }\r\n\r\n        }\r\n\r\n       }\r\n\r\n       @if(MakeupLayer!=\u0022Tattoo\u0022 || CurrentMarking is not null \u0026\u0026 MarkingSection==\u0022Ink\u0022)\r\n\r\n       {\r\n\r\n        \u003Cdiv class=\u0022colorchoices\u0022\u003E\r\n\r\n         @if(MakeupLayer!=\u0022Tattoo\u0022){\u003Cbutton class=@($\u0022creatorbutton colorchoice makeupnone {(!CurrentMakeupActive?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@ChooseNoMakeup\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eblock\u003C/span\u003E\u003Cspan\u003ENone\u003C/span\u003E\u003C/button\u003E}\r\n\r\n         @foreach(var color in MakeupCatalog.Colors(MakeupLayer)){\u003Cbutton class=@($\u0022creatorbutton colorchoice makeupcolor makeup-{color.Id} {(CurrentMakeupActive \u0026\u0026 CurrentMakeup.Color==color.Color?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMakeupColor(color.Color)) tooltip=@color.Label\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\r\n\r\n        \u003C/div\u003E\r\n\r\n        \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@(MakeupLayer==\u0022Eyeliner\u0022?\u0022Upper eyelid strength\u0022:\u0022Strength\u0022)\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(CurrentMakeup.Amount*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider makeupamount\u0022 Value=@CurrentMakeup.Amount OnValueChanged=@SetMakeupAmount Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n        @if(MakeupLayer==\u0022Eyeliner\u0022)\r\n        {\r\n         \u003Cdiv class=\u0022optiondescription\u0022\u003EUpper and lower liner share one color. Wings follow the upper liner strength; inner-corner liner is optional.\u003C/div\u003E\r\n         @foreach(var part in new[]{\u0022Lower eyelid\u0022,\u0022Inner corner\u0022})\r\n         {\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@part strength\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(EyelinerStrength(part)*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider eyeliner-{(part==\u0022Lower eyelid\u0022?\u0022lower\u0022:\u0022inner\u0022)}\u0022) Value=@EyelinerStrength(part) OnValueChanged=@((float value)=\u003ESetEyelinerStrength(part,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n         }\r\n         \u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003EWing\u003C/div\u003E\r\n         \u003Cdiv class=\u0022choicegrid\u0022\u003E@foreach(var wing in new[]{\u0022none\u0022,\u0022wing\u0022}){\u003Cbutton class=@($\u0022creatorbutton eyelinerwing {((wing==\u0022none\u0022)== (Appearance.Makeup.EyelinerWing==\u0022none\u0022)?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseEyelinerWing(wing))\u003E@(wing==\u0022none\u0022?\u0022No wing\u0022:\u0022Wing\u0022)\u003C/button\u003E}\u003C/div\u003E\r\n         @if(Appearance.Makeup.EyelinerWing!=\u0022none\u0022)\r\n         {\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing strength\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(Appearance.Makeup.EyelinerWingStrength*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing\u0022 Value=@Appearance.Makeup.EyelinerWingStrength OnValueChanged=@((float value)=\u003ESetEyelinerStrength(\u0022Wing\u0022,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing tip horizontal\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(Appearance.Makeup.WingTipHorizontal*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing-horizontal\u0022 Value=@Appearance.Makeup.WingTipHorizontal OnValueChanged=@((float value)=\u003ESetWingTip(false,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing tip vertical\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(Appearance.Makeup.WingTipVertical==0?\u0022Level\u0022:$\u0022{MathF.Round(MathF.Abs(Appearance.Makeup.WingTipVertical)*100)}% {(Appearance.Makeup.WingTipVertical\u003E0?\u0022up\u0022:\u0022down\u0022)}\u0022)\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing-vertical\u0022 Value=@Appearance.Makeup.WingTipVertical OnValueChanged=@((float value)=\u003ESetWingTip(true,value)) Min=@(-1f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing thickness\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(Appearance.Makeup.EyelinerWingThickness*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing-thickness\u0022 Value=@Appearance.Makeup.EyelinerWingThickness OnValueChanged=@SetWingThickness Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n         }\r\n        }\r\n\r\n        \u003Cbutton class=\u0022creatorbutton quiet makeupcustomtoggle\u0022 onclick=@(()=\u003ECustomMakeup=!CustomMakeup)\u003E\u003Cspan class=\u0022material-icons\u0022\u003Epalette\u003C/span\u003E@(CustomMakeup?\u0022Hide custom color\u0022:\u0022Custom color\u0022)\u003C/button\u003E\r\n\r\n        @if(CustomMakeup){\u003Cdiv class=\u0022makeupcustomgrid\u0022\u003E@foreach(var channel in new[]{\u0022Red\u0022,\u0022Green\u0022,\u0022Blue\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@channel\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider makeup-{channel.ToLowerInvariant()}\u0022) Value=@MakeupChannel(channel) OnValueChanged=@((float value)=\u003ESetMakeupChannel(channel,value)) Min=@(0f) Max=@(1f) Step=@(1f/255) ShowTextEntry=@false /\u003E\u003C/div\u003E}\u003C/div\u003E}\r\n\r\n       }\r\n\r\n       \u003Cdiv class=\u0022makeupresets\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet clearmakeup\u0022 onclick=@ClearMakeup\u003EClear all makeup \u0026amp; markings\u003C/button\u003E\u003C/div\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Clothing\u0022)\r\n      {\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EYour wardrobe\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EClick an item to wear it, click again to remove it. Outfits replace tops and bottoms; jackets and accessories can be layered.\u003C/div\u003E\r\n       \u003Cbutton class=\u0022creatorbutton quiet browsecloud\u0022 onclick=@(()=\u003ECloudWardrobeOpen=!CloudWardrobeOpen)\u003E@(CloudWardrobeOpen?\u0022Close cloud browser\u0022:\u0022Browse cloud clothing \u0026 purchased cosmetics\u0022)\u003C/button\u003E\r\n       @if(CloudWardrobeOpen){\u003CCloudClothingBrowser Selected=@ChooseCloudClothing Appearance=@Appearance /\u003E}\r\n       \u003CTextEntry class=\u0022wardrobesearch\u0022 Value=@WardrobeSearch OnTextEdited=@((string value)=\u003E{WardrobeSearch=value;}) Placeholder=\u0022Search clothing\u0022 /\u003E\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E@foreach(var category in WardrobeCatalog.Categories){\u003Cbutton class=@($\u0022creatorbutton {(WardrobeCategory==category?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EWardrobeCategory=category)\u003E@category\u003C/button\u003E}\u003C/div\u003E\r\n       \u003Cdiv class=\u0022fieldlabel\u0022\u003EWearing @WardrobeCatalog.Paths(Appearance).Count() items\u003C/div\u003E\u003Cdiv class=\u0022choicegrid\u0022\u003E@foreach(var path in WardrobeCatalog.Paths(Appearance).ToArray()){var worn=ClothingResources.Available.FirstOrDefault(x=\u003Ex.Path==path);@if(worn is not null){\u003Cbutton class=\u0022creatorbutton chosen\u0022 onclick=@(()=\u003EChooseClothing(worn))\u003E@($\u0022{worn.Name} \u00D7\u0022)\u003C/button\u003E}}\u003C/div\u003E\r\n       \u003Cdiv class=\u0022fieldlabel\u0022\u003E@($\u0022{WardrobeCategory} \u00B7 {VisibleWardrobe.Count()} choices\u0022)\u003C/div\u003E\u003Cdiv class=\u0022choicegrid thumbnailgrid\u0022\u003E@foreach(var item in VisibleWardrobe){\u003Cbutton class=@($\u0022creatorbutton wardrobechoice {((WardrobeCatalog.Paths(Appearance).Contains(item.Path)||Appearance.HairPath==item.Path||Appearance.FacialHairPath==item.Path)?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseClothing(item))\u003E\u003Cdiv class=\u0022thumbnailframe\u0022\u003E@if(ClothingThumbnails.For(item.Path) is { Length: \u003E 0 } icon){\u003Cdiv class=\u0022itemthumbnail\u0022 style=@($\u0022background-image: url(\u0027{icon}\u0027);\u0022)\u003E\u003C/div\u003E}else{\u003Cspan class=\u0022material-icons thumbnailplaceholder\u0022\u003Eblock\u003C/span\u003E}\u003C/div\u003E\u003Cspan class=\u0022itemlabel\u0022\u003E@item.Name\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n       @if(!VisibleWardrobe.Any()){\u003Cdiv class=\u0022optiondescription\u0022\u003ENo clothing matches your search in this category.\u003C/div\u003E}\r\n       \u003Cdiv class=\u0022choicegrid\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@ClearClothing\u003ERemove clothing\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@ResetClothing\u003EDefault outfit\u003C/button\u003E\u003C/div\u003E\r\n      }\r\n      else if(Tab==\u0022Hair\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EHair\u003C/div\u003E\u003Cdiv class=\u0022choicegrid thumbnailgrid\u0022\u003E@foreach(var hair in AvailableHair){\u003Cbutton class=@($\u0022creatorbutton hairchoice {(Appearance.HairPath==hair.ResourcePath?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseHair(hair))\u003E\u003Cdiv class=\u0022thumbnailframe\u0022\u003E@if(ClothingThumbnails.For(hair.ResourcePath) is { Length: \u003E 0 } icon){\u003Cdiv class=\u0022itemthumbnail\u0022 style=@($\u0022background-image: url(\u0027{icon}\u0027);\u0022)\u003E\u003C/div\u003E}else{\u003Cspan class=\u0022material-icons thumbnailplaceholder\u0022\u003Eblock\u003C/span\u003E}\u003C/div\u003E\u003Cspan class=\u0022itemlabel\u0022\u003E@hair.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003EHair color\u003C/div\u003E\u003Cdiv class=\u0022colorchoices\u0022\u003E@foreach(var color in AppearanceCatalog.Colors){\u003Cbutton class=@($\u0022creatorbutton colorchoice {(SameColor(color.Color)?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseColor(color)) tooltip=@color.Label\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003EFacial hair\u003C/div\u003E\u003Cdiv class=\u0022choicegrid thumbnailgrid\u0022\u003E@foreach(var facial in AvailableFacialHair.Where(x=\u003Ex.BodyType==\u0022\u0022||x.BodyType==BodyType)){\u003Cbutton class=@($\u0022creatorbutton facialchoice {(Appearance.FacialHairPath==facial.ResourcePath?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseFacial(facial))\u003E\u003Cdiv class=\u0022thumbnailframe\u0022\u003E@if(ClothingThumbnails.For(facial.ResourcePath) is { Length: \u003E 0 } icon){\u003Cdiv class=\u0022itemthumbnail\u0022 style=@($\u0022background-image: url(\u0027{icon}\u0027);\u0022)\u003E\u003C/div\u003E}else{\u003Cspan class=\u0022material-icons thumbnailplaceholder\u0022\u003Eblock\u003C/span\u003E}\u003C/div\u003E\u003Cspan class=\u0022itemlabel\u0022\u003E@facial.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Shape\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003ERefine your look\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003ENeutral is 50%; ear taper and belly start at 0%.\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E@foreach(var category in AppearanceCatalog.Sliders.Select(x=\u003Ex.Group).Distinct()){\u003Cbutton class=@($\u0022creatorbutton shapecategory {(ShapeCategory==category?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChangeShapeCategory(category))\u003E@category\u003C/button\u003E}\u003C/div\u003E\r\n\r\n       @if(ShapeCategory==\u0022Body\u0022 \u0026\u0026 Appearance.BaseShape is not null)\r\n       {\r\n        @foreach(var breast in new[]{true,false}){\u003Cdiv class=\u0022shaperow\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@(breast?\u0022Breast size\u0022:\u0022Bulge size\u0022)\u003C/span\u003E\u003Cspan\u003E@MathF.Round((breast?1-Appearance.BaseShape.MaleBody:Appearance.BaseShape.MaleBody)*100)% effect\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@(()=\u003ESetBodyFeature(breast,.5f))\u003EReset\u003C/button\u003E\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider {(breast?\u0022breast\u0022:\u0022bulge\u0022)}-size\u0022) Value=@(breast?Appearance.BaseShape.BreastSize:Appearance.BaseShape.BulgeSize) OnValueChanged=@((float value)=\u003ESetBodyFeature(breast,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E}\r\n       }\r\n       \u003Cdiv class=\u0022shapegrid\u0022\u003E@foreach(var slider in AppearanceCatalog.Sliders.Where(x=\u003Ex.Group==ShapeCategory \u0026\u0026 (Appearance.BaseShape is null || x.Id!=\u0022bodyfeature\u0022)))\r\n\r\n       {\r\n\r\n        \u003Cdiv class=@($\u0022shaperow shape-{slider.Id}\u0022)\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@slider.LabelFor(Appearance)\u003C/span\u003E\u003Cdiv class=\u0022shapeactions\u0022\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(slider.Get(Appearance)*100))%\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet resetslider\u0022 onclick=@(()=\u003ESetShape(slider,slider.Neutral)) tooltip=@($\u0022Reset {slider.LabelFor(Appearance).ToLowerInvariant()} to {MathF.Round(slider.Neutral*100)}%\u0022)\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003E\u003C/button\u003E\u003C/div\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider\u0022 Value=@slider.Get(Appearance) OnValueChanged=@((float value)=\u003ESetShape(slider,value)) Min=@slider.Min Max=@slider.Max Step=@(0.01f) ShowTextEntry=@false /\u003E@if(slider.Id==\u0022columella\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003EWidth between the nostrils\u003C/div\u003E}@if(slider.Id==\u0022philtrum\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003EWidth between nose and upper lip\u003C/div\u003E}@if(slider.Id==\u0022eartaper\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ENormal \u00B7 Pointed\u003C/div\u003E}@if(slider.Id==\u0022fitness\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ESoft \u00B7 Neutral \u00B7 Muscular\u003C/div\u003E}@if(slider.Id==\u0022nostrils\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003EWidth of the nostril wings\u003C/div\u003E}@if(slider.Id==\u0022nostrilfullness\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ESmaller \u00B7 Neutral \u00B7 Fuller wings\u003C/div\u003E}@if(slider.Id==\u0022noseheight\u0022 || slider.Id==\u0022chinheight\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ELower \u00B7 Neutral \u00B7 Higher\u003C/div\u003E}\u003C/div\u003E\r\n\r\n       }\u003C/div\u003E\r\n\r\n       \u003Cbutton class=\u0022creatorbutton quiet resetshape\u0022 onclick=@ResetShapes\u003EReset body \u0026amp; face\u003C/button\u003E\r\n\r\n      }\r\n\r\n      @if(ExtraTabs.Contains(Tab) \u0026\u0026 ExtraContent is not null){@ExtraContent(this)}\r\n\r\n     \u003C/div\u003E\r\n\r\n     \u003Cdiv class=\u0022creatorutility\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@Randomize\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eshuffle\u003C/span\u003ERandomize\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@Reset\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003EReset appearance\u003C/button\u003E\u003C/div\u003E\r\n\r\n    \u003C/div\u003E\r\n\r\n    \u003Cdiv class=\u0022creatorpresentation\u0022\u003E\u003Cdiv class=\u0022previewheading\u0022\u003E\u003Cdiv class=\u0022previewname\u0022\u003E@(string.IsNullOrWhiteSpace(CharacterName)?\u0022Your character\u0022:CharacterName.Trim())\u003C/div\u003E\u003Cdiv class=\u0022previewcaption\u0022\u003E@(PreviewPan.Length\u003E0.01f || MathF.Abs(PreviewPitch)\u003E0.01f?\u0022Free inspection\u0022:Zoom\u003ECharacterCreatorPreview.FaceZoom\u002B0.001f?\u0022Face detail\u0022:Portrait?\u0022Face preview\u0022:\u0022Character preview\u0022)\u003C/div\u003E\u003C/div\u003E\u003CCharacterCreatorPreview @ref=Preview class=\u0022mainpreview\u0022 Hero=@true Appearance=@Appearance Portrait=@Portrait Zoom=@Zoom Yaw=@Yaw Pitch=@PreviewPitch PitchChanged=@SetPitch PanOffset=@PreviewPan PanChanged=@SetPan Interactive=@(!LoadingCatalog) OrbitChanged=@SetYaw ZoomChanged=@SetZoom ShowClothing=@ShowClothing /\u003E\u003Cdiv class=\u0022previewcontrols\u0022\u003E\u003Cdiv class=\u0022previewviewrow\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet rotateleft\u0022 onclick=@(()=\u003ERotate(-30)) tooltip=\u0022Turn left\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erotate_left\u003C/span\u003E\u003C/button\u003E\u003Cbutton class=@($\u0022creatorbutton bodyview {(Zoom==0?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESetView(0))\u003EFull body\u003C/button\u003E\u003Cbutton class=@($\u0022creatorbutton previewzoom {(MathF.Abs(Zoom-CharacterCreatorPreview.FaceZoom)\u003C0.001f?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESetView(CharacterCreatorPreview.FaceZoom))\u003EFace view\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet rotateright\u0022 onclick=@(()=\u003ERotate(30)) tooltip=\u0022Turn right\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erotate_right\u003C/span\u003E\u003C/button\u003E\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton quiet clothingtoggle\u0022 onclick=@ToggleClothing\u003E\u003Cspan class=\u0022material-icons\u0022\u003E@(ShowClothing?\u0022visibility_off\u0022:\u0022visibility\u0022)\u003C/span\u003E@(ShowClothing?\u0022Hide clothing\u0022:\u0022Show clothing\u0022)\u003C/button\u003E\u003C/div\u003E\u003Cdiv class=\u0022previewzoomrow\u0022\u003E\u003Cspan\u003EZoom\u003C/span\u003E\u003CSliderControl class=\u0022customzoom\u0022 Value=@Zoom OnValueChanged=@SetZoom Min=@(0f) Max=@CharacterCreatorPreview.MaxZoom Step=@(0.005f) ShowTextEntry=@false /\u003E\u003C/div\u003E\u003Cdiv class=\u0022previewrecenter\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet recenterpreview\u0022 onclick=@RecenterPreview\u003E\u003Cspan class=\u0022material-icons\u0022\u003Ecenter_focus_strong\u003C/span\u003ERecenter preview\u003C/button\u003E\u003C/div\u003E\u003Cdiv class=\u0022previewhint\u0022\u003ELeft drag: turn \u00B7 Right: tilt \u00B7 Middle: pan \u00B7 Scroll: zoom\u003C/div\u003E\u003C/div\u003E\r\n\r\n   \u003C/div\u003E\r\n\r\n   \u003Cdiv class=\u0022creatorfooter\u0022\u003E\u003Cdiv class=\u0022creatoridentity\u0022\u003E\u003Cdiv class=\u0022identityfields\u0022\u003E\u003Cdiv class=\u0022namefield\u0022\u003E\u003Cdiv class=\u0022fieldlabel\u0022\u003ECharacter name\u003C/div\u003E\u003CTextEntry class=\u0022charactername\u0022 Value=@CharacterName OnTextEdited=@SetCharacterName MaxLength=@(40) Placeholder=\u0022Your character\u0027s name\u0022 /\u003E\u003C/div\u003E@FooterContent\u003C/div\u003E\u003C/div\u003E\u003Cdiv class=\u0022creatorfeedback\u0022\u003E@(Feedback??(LoadingCatalog?\u0022Loading character options\u2026\u0022:Preview?.Error)??(\u0022\u0022))\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton primary startplaythrough\u0022 disabled=@Disabled onclick=@Start\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eplay_arrow\u003C/span\u003E@(SaveLabel)\u003C/button\u003E\u003C/div\u003E\r\n\r\n  \u003C/div\u003E\r\n\r\n }\r\n\r\n\u003C/root\u003E\r\n\r\n@code {\r\n\t/// \u003Csummary\u003EConsumer-specific pages; built-in categories remain owned by Character Kit.\u003C/summary\u003E\r\n\tpublic IReadOnlyList\u003Cstring\u003E ExtraTabs { get; set; } = Array.Empty\u003Cstring\u003E();\r\n\tpublic IReadOnlyList\u003Cstring\u003E HiddenTabs { get; set; } = Array.Empty\u003Cstring\u003E();\r\n\tpublic RenderFragment\u003CCharacterCreatorPanel\u003E ExtraContent { get; set; }\r\n\tpublic RenderFragment FooterContent { get; set; }\r\n\tpublic Action StopPreview { get; set; }\r\n\tpublic bool Disabled { get; set; }\r\n\tpublic string ProductLabel { get; set; } = \u0022Character Editor\u0022;\r\n\tpublic string LocationLabel { get; set; } = \u0022Studio\u0022;\r\n\tpublic string Title { get; set; } = \u0022Create your character\u0022;\r\n\tpublic CharacterAppearanceData EditableAppearance =\u003E Appearance;\r\n\tpublic SkinnedModelRenderer PreviewRenderer =\u003E Preview?.Body;\r\n\tpublic string SelectedTab =\u003E Tab;\r\n\tpublic string SaveLabel { get; set; } = \u0022Save character\u0022;\r\n\tpublic Func\u003Cstring, CharacterAppearanceData, string\u003E Saved { get; set; }\r\n\tpublic Action Closed { get; set; }\r\n\tpublic CharacterPreset InitialPreset { get; set; }\r\n\r\n\tCharacterPreset loadedPreset;\r\n\tpublic CharacterAppearanceData DraftAppearance =\u003E Appearance.DeepCopy();\r\n\tpublic string DraftCharacterName =\u003E CharacterName;\r\n\r\n\tpublic void LoadDraft(string name, CharacterAppearanceData data)\r\n\t{\r\n\t\tCharacterName = name;\r\n\t\tAppearance = data.DeepCopy();\r\n\t\tChosenPreset = null;\r\n\t\tTab = \u0022Face \u0026 body\u0022;\r\n\t}\r\n\r\n\tpublic bool AppearanceReady =\u003E CatalogReady \u0026\u0026 Preview?.Loading != true \u0026\u0026 CharacterAppearance.ValidateForApply(Appearance)is null;\r\n\r\n\tIReadOnlyList\u003Cstring\u003E Tabs =\u003E new[]\r\n\t{\r\n\t\t\u0022Presets\u0022,\r\n\t\t\u0022Skin\u0022,\r\n\t\t\u0022Face \u0026 body\u0022,\r\n\t\t\u0022Photos\u0022,\r\n\t\t\u0022Hair\u0022,\r\n\t\t\u0022Clothing\u0022,\r\n\t\t\u0022Shape\u0022,\r\n\t\t\u0022Makeup\u0022\r\n\t}.Where(x =\u003E !HiddenTabs.Contains(x)).Concat(ExtraTabs).Distinct().ToArray();\r\n\r\n\tbool CloudWardrobeOpen;\r\n\tstring WardrobeSearch = \u0022\u0022, WardrobeCategory = \u0022Tops\u0022;\r\n\tIEnumerable\u003CWardrobeItem\u003E VisibleWardrobe =\u003E ClothingResources.Available.Where(x =\u003E x.Category == WardrobeCategory \u0026\u0026 (string.IsNullOrWhiteSpace(WardrobeSearch) || x.Name.Contains(WardrobeSearch, StringComparison.OrdinalIgnoreCase))).OrderBy(x =\u003E x.Name);\r\n\r\n\tvoid ChooseCloudClothing(WardrobeItem item)\r\n\t{\r\n\t\tWardrobeCategory = item.Category;\r\n\t\tWardrobeSearch = \u0022\u0022;\r\n\t\tChooseClothing(item);\r\n\t\tAppearance.Version = 2;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t\tStateHasChanged();\r\n\t}\r\n\r\n\tvoid ChooseClothing(WardrobeItem item)\r\n\t{\r\n\t\tvar clothing = ClothingResources.Get(item.Path);\r\n\t\tif (clothing is not null \u0026\u0026 ClothingResources.IsHair(clothing))\r\n\t\t\tAppearance.HairPath = Appearance.HairPath == item.Path || ClothingResources.Get(Appearance.HairPath) == clothing ? \u0022\u0022 : item.Path;\r\n\t\telse if (clothing is not null \u0026\u0026 ClothingResources.IsFacialHair(clothing))\r\n\t\t\tAppearance.FacialHairPath = Appearance.FacialHairPath == item.Path || ClothingResources.Get(Appearance.FacialHairPath) == clothing ? \u0022\u0022 : item.Path;\r\n\t\telse\r\n\t\t\tWardrobeCatalog.Toggle(Appearance, item);\r\n\t\tAppearance.Version = 2;\r\n\t\tShowClothing = true;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ClearClothing()\r\n\t{\r\n\t\tAppearance.ClothingPaths = Array.Empty\u003Cstring\u003E();\r\n\t\tAppearance.Version = 2;\r\n\t\tShowClothing = true;\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ResetClothing()\r\n\t{\r\n\t\tAppearance.ClothingPaths = WardrobeCatalog.DefaultPaths.ToArray();\r\n\t\tAppearance.Version = 2;\r\n\t\tShowClothing = true;\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tCharacterAppearanceData Appearance = AppearanceCatalog.CreateDefault();\r\n\tstring CharacterName = \u0022Rowan\u0022, PlaythroughName = \u0022A new adventure\u0022, Tab = \u0022Presets\u0022, ShapeCategory = \u0022Body\u0022, ChosenPreset = \u0022traveller\u0022, Feedback;\r\n\tstring BlendRegion = \u0022overall\u0022;\r\n\tbool CatalogReady, LoadingCatalog;\r\n\tstring SkinPalette = \u0022Human\u0022;\r\n\tstring MakeupLayer = \u0022Lipstick\u0022;\r\n\tbool CustomMakeup;\r\n\tint SelectedMarking;\r\n\tstring MarkingSection = \u0022Design\u0022;\r\n\tCharacterMarkingData CurrentMarking =\u003E Appearance.Makeup.Markings.Count == 0 ? null : Appearance.Makeup.Markings[Math.Clamp(SelectedMarking, 0, Appearance.Makeup.Markings.Count - 1)];\r\n\r\n\tMakeupLayerData CurrentMakeup =\u003E MakeupLayer == \u0022Tattoo\u0022 ? CurrentMarking?.Ink ?? Appearance.Makeup.Tattoo : MakeupCatalog.Layer(Appearance.Makeup, MakeupLayer);\r\n\r\n\tvoid ChooseMakeupLayer(string layer)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tMakeupLayer = layer;\r\n\t\tif (layer == \u0022Tattoo\u0022)\r\n\t\t\tAppearance.Makeup.EditMarkings();\r\n\t}\r\n\r\n\tvoid SelectMarking(int index)\r\n\t{\r\n\t\tif (LoadingCatalog || index \u003C 0 || index \u003E= Appearance.Makeup.Markings.Count)\r\n\t\t\treturn;\r\n\t\tSelectedMarking = index;\r\n\t}\r\n\r\n\tvoid AddMarking()\r\n\t{\r\n\t\tif (LoadingCatalog || Appearance.Makeup.Markings.Count \u003E= MarkingCatalog.Maximum)\r\n\t\t{\r\n\t\t\tFeedback = \u0022You can combine up to six marking layers.\u0022;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tvar marking = new CharacterMarkingData();\r\n\t\tMarkingCatalog.ResetPosition(marking);\r\n\t\tAppearance.Makeup.Markings.Add(marking);\r\n\t\tSelectedMarking = Appearance.Makeup.Markings.Count - 1;\r\n\t\tMarkingSection = \u0022Design\u0022;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid DeleteMarking()\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tAppearance.Makeup.Markings.Remove(CurrentMarking);\r\n\t\tSelectedMarking = Math.Clamp(SelectedMarking, 0, Math.Max(0, Appearance.Makeup.Markings.Count - 1));\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tstring MarkingSymbol(string style) =\u003E style switch\r\n\t{\r\n\t\t\u0022crescent\u0022 =\u003E \u0022brightness_2\u0022,\r\n\t\t\u0022teardrop\u0022 =\u003E \u0022water_drop\u0022,\r\n\t\t\u0022star\u0022 =\u003E \u0022star_outline\u0022,\r\n\t\t\u0022rune\u0022 =\u003E \u0022gesture\u0022,\r\n\t\t\u0022barbed-band\u0022 =\u003E \u0022link\u0022,\r\n\t\t_ =\u003E \u0022block\u0022\r\n\t};\r\n\tvoid ChooseMarkingStyle(string style)\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tMarkingCatalog.ChangeStyle(CurrentMarking, style);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ChooseMarkingRegion(string region)\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tMarkingCatalog.ChangeRegion(CurrentMarking, region);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tfloat MarkingValue(string control) =\u003E control switch\r\n\t{\r\n\t\t\u0022Horizontal\u0022 =\u003E CurrentMarking.Horizontal,\r\n\t\t\u0022Vertical\u0022 =\u003E CurrentMarking.Vertical,\r\n\t\t\u0022Scale\u0022 =\u003E CurrentMarking.Scale,\r\n\t\t\u0022Spacing\u0022 =\u003E CurrentMarking.Spacing,\r\n\t\t_ =\u003E CurrentMarking.Rotation\r\n\t};\r\n\tvoid SetMarkingValue(string control, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (MarkingCatalog.Style(CurrentMarking.Style).Band \u0026\u0026 control is not \u0022Vertical\u0022 and not \u0022Scale\u0022 and not \u0022Spacing\u0022)\r\n\t\t\treturn;\r\n\t\tif (control == \u0022Horizontal\u0022)\r\n\t\t\tCurrentMarking.Horizontal = value;\r\n\t\telse if (control == \u0022Vertical\u0022)\r\n\t\t\tCurrentMarking.Vertical = value;\r\n\t\telse if (control == \u0022Scale\u0022)\r\n\t\t\tCurrentMarking.Scale = value;\r\n\t\telse if (control == \u0022Spacing\u0022)\r\n\t\t\tCurrentMarking.Spacing = value;\r\n\t\telse if (control == \u0022Rotation\u0022)\r\n\t\t\tCurrentMarking.Rotation = value;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ResetMarkingPlacement()\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tMarkingCatalog.ResetPosition(CurrentMarking);\r\n\t\tCurrentMarking.Scale = .5f;\r\n\t\tCurrentMarking.Rotation = .5f;\r\n\t\tCurrentMarking.Spacing = .5f;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid InspectMarking()\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tvar region = MarkingCatalog.Region(CurrentMarking.Region);\r\n\t\tvar center = MarkingCatalog.Focus(CurrentMarking);\r\n\t\tvar outward = MarkingCatalog.Outward(CurrentMarking);\r\n\t\tYaw = -MathF.Atan2(outward.y, outward.x) * 180 / MathF.PI;\r\n\t\tSetZoom(region.Head ? 1.55f : 1.15f);\r\n\t\tvar rotated = Rotation.FromYaw(Yaw) * center;\r\n\t\tvar targetHeight = 62 \u002B 1.5f * MathF.Max(0, Zoom - 1);\r\n\t\tPreviewPan = new(rotated.x, rotated.y, rotated.z - targetHeight);\r\n\t\tPreviewPitch = 0;\r\n\t}\r\n\r\n\tvoid ChangedCosmetics()\r\n\t{\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseNoMakeup()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tCurrentMakeup.Amount = 0;\r\n\t\tif (MakeupLayer == \u0022Eyeliner\u0022) MakeupCatalog.Reset(Appearance.Makeup, \u0022Eyeliner\u0022);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ChooseMakeupColor(Color color)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tCurrentMakeup.Color = color;\r\n\t\tif (!CurrentMakeupActive)\r\n\t\t\tCurrentMakeup.Amount = MakeupLayer == \u0022Eyeliner\u0022 ? 1f : .65f;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid SetMakeupAmount(float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tCurrentMakeup.Amount = Math.Clamp(value, 0, 1);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tbool CurrentMakeupActive =\u003E MakeupLayer == \u0022Eyeliner\u0022 ? Appearance.Makeup.EyelinerActive : CurrentMakeup.Amount \u003E 0;\r\n\r\n\tfloat EyelinerStrength(string part) =\u003E part == \u0022Lower eyelid\u0022 ? Appearance.Makeup.EyelinerLowerStrength : part == \u0022Wing\u0022 ? Appearance.Makeup.EyelinerWingStrength : Appearance.Makeup.EyelinerInnerStrength;\r\n\tvoid SetEyelinerStrength(string part, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value)) return;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (part == \u0022Lower eyelid\u0022) Appearance.Makeup.EyelinerLowerStrength = value;\r\n\t\telse if (part == \u0022Wing\u0022) Appearance.Makeup.EyelinerWingStrength = value;\r\n\t\telse Appearance.Makeup.EyelinerInnerStrength = value;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid PrepareAdjustableWing()\r\n\t{\r\n\t\tvar makeup = Appearance.Makeup;\r\n\t\tmakeup.EyelinerWingHorizontal = makeup.WingTipHorizontal;\r\n\t\tmakeup.EyelinerWingVertical = makeup.WingTipVertical;\r\n\t\tmakeup.EyelinerWing = \u0022wing\u0022;\r\n\t}\r\n\r\n\tvoid SetWingTip(bool vertical, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value)) return;\r\n\t\tPrepareAdjustableWing();\r\n\t\tif (vertical) Appearance.Makeup.EyelinerWingVertical = Math.Clamp(value, -1, 1);\r\n\t\telse Appearance.Makeup.EyelinerWingHorizontal = Math.Clamp(value, 0, 1);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid SetWingThickness(float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value)) return;\r\n\t\tPrepareAdjustableWing();\r\n\t\tAppearance.Makeup.EyelinerWingThickness = Math.Clamp(value, 0, 1);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ChooseEyelinerWing(string wing)\r\n\t{\r\n\t\tif (LoadingCatalog) return;\r\n\t\tif (wing == \u0022wing\u0022) PrepareAdjustableWing();\r\n\t\telse Appearance.Makeup.EyelinerWing = \u0022none\u0022;\r\n\t\tif (wing != \u0022none\u0022 \u0026\u0026 CurrentMakeup.Amount == 0) CurrentMakeup.Amount = 1;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tfloat MakeupChannel(string channel) =\u003E channel == \u0022Red\u0022 ? CurrentMakeup.Red : channel == \u0022Green\u0022 ? CurrentMakeup.Green : CurrentMakeup.Blue;\r\n\tvoid SetMakeupChannel(string channel, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (channel == \u0022Red\u0022)\r\n\t\t\tCurrentMakeup.Red = value;\r\n\t\telse if (channel == \u0022Green\u0022)\r\n\t\t\tCurrentMakeup.Green = value;\r\n\t\telse\r\n\t\t\tCurrentMakeup.Blue = value;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ClearMakeup()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.Makeup = new();\r\n\t\tSelectedMarking = 0;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tbool Portrait = true, ShowClothing = true;\r\n\tfloat Yaw, Zoom = CharacterCreatorPreview.FaceZoom;\r\n\tCharacterCreatorPreview Preview;\r\n\tVector3 PreviewPan;\r\n\tfloat PreviewPitch;\r\n\tstring BodyType =\u003E AppearanceCatalog.IsFemale(Appearance) ? \u0022female\u0022 : \u0022male\u0022;\r\n\r\n\tIEnumerable\u003CAppearanceOption\u003E AvailableHair =\u003E AppearanceCatalog.Hair.Concat(ClothingResources.Available.Where(x=\u003Ex.Category==\u0022Hair\u0022).Select(x=\u003Enew AppearanceOption(x.Path,x.Name,x.Path))).DistinctBy(x=\u003Ex.ResourcePath);\r\n IEnumerable\u003CAppearanceOption\u003E AvailableFacialHair =\u003E AppearanceCatalog.FacialHair.Concat(ClothingResources.Available.Where(x=\u003Ex.Category==\u0022Facial hair\u0022).Select(x=\u003Enew AppearanceOption(x.Path,x.Name,x.Path))).DistinctBy(x=\u003Ex.ResourcePath);\r\n string HairLabel =\u003E AppearanceCatalog.Hair.FirstOrDefault(x =\u003E x.ResourcePath == Appearance.HairPath)?.Label ?? \u0022Hair\u0022;\r\n\r\n\tstring FaceLabel(AppearanceOption option) =\u003E option.Label.Replace(\u0022 \u00B7 male\u0022, \u0022\u0022).Replace(\u0022 \u00B7 female\u0022, \u0022\u0022);\r\n\tstring ColorCss(Color color) =\u003E $\u0022rgb({(int)(color.r * 255)},{(int)(color.g * 255)},{(int)(color.b * 255)})\u0022;\r\n\tvoid SetCharacterName(string value)\r\n\t{\r\n\t\tCharacterName = value;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid SetPlaythroughName(string value)\r\n\t{\r\n\t\tPlaythroughName = value;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tbool SameColor(Color color) =\u003E MathF.Abs(Appearance.HairColorR - color.r) \u003C 0.001f \u0026\u0026 MathF.Abs(Appearance.HairColorG - color.g) \u003C 0.001f \u0026\u0026 MathF.Abs(Appearance.HairColorB - color.b) \u003C 0.001f;\r\n\tvoid ChangeTab(string tab)\r\n\t{\r\n\t\tif (tab != Tab)\n\t\t\tStopVoicePreview();\r\n\t\tTab = tab;\r\n\t\tif (tab == \u0022Clothing\u0022)\r\n\t\t\tSetZoom(0);\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid StopVoicePreview()\r\n\t{\r\n\t\tStopPreview?.Invoke();\r\n\t}\r\n\r\n\tvoid ChangeShapeCategory(string category)\r\n\t{\r\n\t\tShapeCategory = category;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ApplyPreset(string id)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tvar voice = Appearance.VoiceId;\r\n\t\tbool unified = Appearance.BaseShape is not null;\r\n\t\tAppearance = AppearanceCatalog.ApplyPreset(id);\r\n\t\tif (unified)\r\n\t\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tAppearance.VoiceId = voice;\r\n\t\tChosenPreset = id;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid EnableBlending()\r\n\t{\r\n\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid AddPhotoFace(PhotoFace face)\r\n\t{\r\n\t\tif (!face.Valid)\r\n\t\t\treturn;\r\n\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tif (Appearance.BaseShape.PhotoFaces.Count \u003E= 4)\r\n\t\t\treturn;\r\n\t\tArray.Fill(Appearance.BaseShape.HeadWeights, 0f);\r\n\t\tforeach (var old in Appearance.BaseShape.PhotoFaces)\r\n\t\t\told.Influence = 0;\r\n\t\tAppearance.BaseShape.PhotoFaces.Add(face);\r\n\t\tChosenPreset = null;\r\n\t\tTab = \u0022Face \u0026 body\u0022;\r\n\t\tBlendRegion = \u0022overall\u0022;\r\n\t\tSetView(CharacterCreatorPreview.FaceZoom);\r\n\t\tFeedback = \u0022Custom base added. Blend it here or refine it in Shape, then save your character.\u0022;\r\n\t}\r\n\r\n\tFaceRegionBlend Region(bool create = false)\r\n\t{\r\n\t\tif (BlendRegion == \u0022overall\u0022)\r\n\t\t\treturn null;\r\n\t\tif (!Appearance.BaseShape.Regions.TryGetValue(BlendRegion, out var value) \u0026\u0026 create)\r\n\t\t{\r\n\t\t\tvalue = new()\r\n\t\t\t{\r\n\t\t\t\tOverallInfluence = .5f\r\n\t\t\t};\r\n\t\t\tAppearance.BaseShape.Regions.Add(BlendRegion, value);\r\n\t\t}\r\n\r\n\t\treturn value;\r\n\t}\r\n\r\n\tfloat OverallWeight =\u003E Region()?.OverallInfluence ?? .5f;\r\n\r\n\tvoid SetOverallWeight(float value)\r\n\t{\r\n\t\tif (!float.IsFinite(value) || BlendRegion == \u0022overall\u0022)\r\n\t\t\treturn;\r\n\t\tRegion(true).OverallInfluence = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tfloat HeadWeight(int index) =\u003E BlendRegion == \u0022overall\u0022 ? Appearance.BaseShape.HeadWeights[index] : Region()?.HeadWeights[index] ?? 0;\r\n\tfloat PhotoWeight(PhotoFace face) =\u003E BlendRegion == \u0022overall\u0022 ? face.Influence : Region()?.PhotoWeights.GetValueOrDefault(face.Id) ?? 0;\r\n\tstring BlendWeightLabel(int index)\r\n\t{\r\n\t\tfloat share = FaceRegions.NativeShare(Appearance.BaseShape, index, BlendRegion);\r\n\t\treturn $\u0022{MathF.Round(HeadWeight(index) * 100)} influence \u00B7 {MathF.Round(share * 100)}% of blend\u0022;\r\n\t}\r\n\r\n\tvoid SetHeadWeight(int index, float value)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\t(BlendRegion == \u0022overall\u0022 ? Appearance.BaseShape.HeadWeights : Region(true).HeadWeights)[index] = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid SetPhotoWeight(PhotoFace face, float value)\r\n\t{\r\n\t\tif (!float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tif (BlendRegion == \u0022overall\u0022)\r\n\t\t\tface.Influence = Math.Clamp(value, 0, 1);\r\n\t\telse\r\n\t\t\tRegion(true).PhotoWeights[face.Id] = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ResetRegion()\r\n\t{\r\n\t\tif (BlendRegion != \u0022overall\u0022)\r\n\t\t\tAppearance.BaseShape.Regions.Remove(BlendRegion);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid SetBodyBlend(float value)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tAppearance.BaseShape.MaleBody = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ToggleClothingVariant(string variant)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null)\r\n\t\t\treturn;\r\n\t\tAppearance.BaseShape.ClothingVariant = Appearance.BaseShape.ClothingVariant == variant ? null : variant;\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid SetBodyFeature(bool breast, float value)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tif (breast)\r\n\t\t\tAppearance.BaseShape.BreastSize = Math.Clamp(value, 0, 1);\r\n\t\telse\r\n\t\t\tAppearance.BaseShape.BulgeSize = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ChooseBody(string body)\r\n\t{\r\n\t\tvar skin = AppearanceCatalog.Skins.FirstOrDefault(x =\u003E x.BodyType == body);\r\n\t\tif (skin is not null)\r\n\t\t\tChooseSkin(skin);\r\n\t}\r\n\r\n\tvoid ChooseSkin(AppearanceOption skin)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.SkinPath = skin.ResourcePath;\r\n\t\tif (Appearance.BaseShape is null \u0026\u0026 !AppearanceCatalog.FacialHair.Any(x =\u003E x.ResourcePath == Appearance.FacialHairPath \u0026\u0026 (x.BodyType == \u0022\u0022 || x.BodyType == skin.BodyType)))\r\n\t\t\tAppearance.FacialHairPath = \u0022\u0022;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseHair(AppearanceOption hair)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.HairPath = hair.ResourcePath;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseFacial(AppearanceOption facial)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.FacialHairPath = facial.ResourcePath;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseColor(AppearanceColorOption color)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.HairColor = color.Color;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseSkinColor(Color color, bool normalize = false)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.SkinColor = color;\r\n\t\tAppearance.NormalizeSkinTone = normalize;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tstatic IReadOnlyList\u003CAppearanceColorOption\u003E EyeColors =\u003E new[]\r\n\t{\r\n\t\tnew AppearanceColorOption(\u0022brown\u0022, \u0022Brown\u0022, new(.24f, .12f, .055f)),\r\n\t\tnew(\u0022hazel\u0022, \u0022Hazel\u0022, new(.34f, .3f, .11f)),\r\n\t\tnew(\u0022amber\u0022, \u0022Amber\u0022, new(.58f, .34f, .075f)),\r\n\t\tnew(\u0022green\u0022, \u0022Green\u0022, new(.13f, .34f, .19f)),\r\n\t\tnew(\u0022blue\u0022, \u0022Blue\u0022, new(.09f, .3f, .55f)),\r\n\t\tnew(\u0022grey\u0022, \u0022Grey\u0022, new(.32f, .36f, .4f)),\r\n\t\tnew(\u0022violet\u0022, \u0022Violet\u0022, new(.32f, .16f, .46f))\r\n\t};\r\n\r\n\tvoid ChooseEyeColor(Color? color)\r\n\t{\r\n\t\tif (LoadingCatalog || LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.CustomEyeColor = color.HasValue;\r\n\t\tif (color.HasValue)\r\n\t\t\tAppearance.EyeColor = color.Value;\r\n\t}\r\n\r\n\tfloat EyeChannel(string channel) =\u003E channel == \u0022Red\u0022 ? Appearance.EyeColorR : channel == \u0022Green\u0022 ? Appearance.EyeColorG : Appearance.EyeColorB;\r\n\tvoid SetEyeChannel(string channel, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.CustomEyeColor = true;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (channel == \u0022Red\u0022)\r\n\t\t\tAppearance.EyeColorR = value;\r\n\t\telse if (channel == \u0022Green\u0022)\r\n\t\t\tAppearance.EyeColorG = value;\r\n\t\telse\r\n\t\t\tAppearance.EyeColorB = value;\r\n\t}\r\n\r\n\tfloat SkinChannel(string channel) =\u003E channel == \u0022Red\u0022 ? Appearance.SkinColorR : channel == \u0022Green\u0022 ? Appearance.SkinColorG : Appearance.SkinColorB;\r\n\tvoid SetSkinChannel(string channel, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tAppearance.NormalizeSkinTone = true;\r\n\t\tif (channel == \u0022Red\u0022)\r\n\t\t\tAppearance.SkinColorR = value;\r\n\t\telse if (channel == \u0022Green\u0022)\r\n\t\t\tAppearance.SkinColorG = value;\r\n\t\telse\r\n\t\t\tAppearance.SkinColorB = value;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid SetShape(AppearanceSlider slider, float value)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tslider.Set(Appearance, value);\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ResetShapes()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearanceCatalog.ResetShapes(Appearance);\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid Randomize()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tvar voice = Appearance.VoiceId;\r\n\t\tvar variant = Appearance.BaseShape?.ClothingVariant;\r\n\t\tvar photos = Appearance.BaseShape?.PhotoFaces.Select(x =\u003E x.DeepCopy()).ToList();\r\n\t\tAppearance = AppearanceCatalog.Randomize(blendHeads: Appearance.BaseShape is not null);\r\n\t\tAppearance.VoiceId = voice;\r\n\t\tif (Appearance.BaseShape is not null)\r\n\t\t{\r\n\t\t\tAppearance.BaseShape.ClothingVariant = variant;\r\n\t\t\tif (photos is not null)\r\n\t\t\t{\r\n\t\t\t\tforeach (var face in photos)\r\n\t\t\t\t\tface.Influence = 0;\r\n\t\t\t\tAppearance.BaseShape.PhotoFaces = photos;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid Reset()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tvar voice = Appearance.VoiceId;\r\n\t\tbool unified = Appearance.BaseShape is not null;\r\n\t\tAppearance = AppearanceCatalog.CreateDefault();\r\n\t\tif (unified)\r\n\t\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tAppearance.VoiceId = voice;\r\n\t\tChosenPreset = \u0022traveller\u0022;\r\n\t\tYaw = 0;\r\n\t\tRecenterPreview();\r\n\t\tSetZoom(CharacterCreatorPreview.FaceZoom);\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid Rotate(float delta)\r\n\t{\r\n\t\tYaw = (Yaw \u002B delta) % 360;\r\n\t}\r\n\r\n\tvoid SetYaw(float value)\r\n\t{\r\n\t\tYaw = value;\r\n\t}\r\n\r\n\tvoid SetView(float value)\r\n\t{\r\n\t\tRecenterPreview();\r\n\t\tSetZoom(value);\r\n\t}\r\n\r\n\tvoid SetZoom(float value)\r\n\t{\r\n\t\tif (float.IsFinite(value))\r\n\t\t{\r\n\t\t\tZoom = Math.Clamp(value, 0, CharacterCreatorPreview.MaxZoom);\r\n\t\t\tPortrait = Zoom \u003E= CharacterCreatorPreview.FaceZoom - 0.001f;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid SetPitch(float value)\r\n\t{\r\n\t\tPreviewPitch = value;\r\n\t}\r\n\r\n\tvoid SetPan(Vector3 value)\r\n\t{\r\n\t\tPreviewPan = value;\r\n\t}\r\n\r\n\tvoid RecenterPreview()\r\n\t{\r\n\t\tYaw = 0;\r\n\t\tPreviewPan = Vector3.Zero;\r\n\t\tPreviewPitch = 0;\r\n\t\tPreview?.Recenter();\r\n\t}\r\n\r\n\tvoid ToggleClothing()\r\n\t{\r\n\t\tShowClothing = !ShowClothing;\r\n\t}\r\n\r\n\tvoid Back()\r\n\t{\r\n\t\tif (!LoadingCatalog)\r\n\t\t{\r\n\t\t\tStopVoicePreview();\r\n\t\t\tClosed?.Invoke();\r\n\t\t}\r\n\t}\r\n\r\n\tvoid Start()\r\n\t{\r\n\t\tif (Disabled || LoadingCatalog || Preview?.Loading == true)\r\n\t\t\treturn;\r\n\t\tif (!CatalogReady)\r\n\t\t{\r\n\t\t\tFeedback = LoadingCatalog ? \u0022Loading character options\u2026\u0022 : \u0022Character options could not be loaded.\u0022;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (CharacterAppearance.ValidateForApply(Appearance)is string error)\r\n\t\t{\r\n\t\t\tFeedback = error;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tStopVoicePreview();\r\n\t\tFeedback = Saved?.Invoke(CharacterName, Appearance.DeepCopy());\r\n\t}\r\n\r\n\tpublic override void Delete(bool immediate = false)\r\n\t{\r\n\t\tStopVoicePreview();\r\n\t\tbase.Delete(immediate);\r\n\t}\r\n\r\n\tprotected override async Task OnParametersSetAsync()\r\n\t{\r\n\t\tif (InitialPreset is not null \u0026\u0026 !ReferenceEquals(loadedPreset, InitialPreset))\r\n\t\t{\r\n\t\t\tLoadDraft(InitialPreset.Name, InitialPreset.Appearance);\r\n\t\t\tloadedPreset = InitialPreset;\r\n\t\t}\r\n\r\n\t\tif (CatalogReady || LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tLoadingCatalog = true;\r\n\t\ttry\r\n\t\t{\r\n\t\t\tawait AppearanceCatalog.EnsureLoadedAsync();\r\n\t\t\tCatalogReady = true;\r\n\t\t}\r\n\t\tcatch (Exception)\r\n\t\t{\r\n\t\t\tFeedback = \u0022Character options could not be loaded. Please try again.\u0022;\r\n\t\t}\r\n\t\tfinally\r\n\t\t{\r\n\t\t\tLoadingCatalog = false;\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override int BuildHash() =\u003E HashCode.Combine(CharacterName, PlaythroughName, HashCode.Combine(Tab, ShapeCategory, SkinPalette, MakeupLayer, CustomMakeup, SelectedMarking, MarkingSection, BlendRegion), ChosenPreset, JsonSerializer.Serialize(Appearance) \u002B Appearance.Makeup.PresentationKey(), HashCode.Combine(Portrait, Yaw, Feedback, LoadingCatalog, SaveLabel, Preview?.Error, CatalogReady, HashCode.Combine(LoadingCatalog, Disabled, ProductLabel, LocationLabel, Title)), Zoom, HashCode.Combine(ShowClothing, PreviewPan, PreviewPitch, WardrobeSearch, WardrobeCategory, ClothingResources.Revision, CloudWardrobeOpen));\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"CharacterKit/PhotoProfileAnchors.cs","FileName":"PhotoProfileAnchors.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"namespace CharacterEditor;\n// Generated by ce_calibrate_photo_profile from native topology and packed engine deformers.\npublic static class PhotoProfileAnchors\n{\n public static readonly int[] Ids={10,168,4,1,13,14,152};\n public static readonly string[] Controls={\u0022head\u0022,\u0022jaw\u0022,\u0022eyes\u0022,\u0022nosewidth\u0022,\u0022nostrils\u0022,\u0022noseheight\u0022,\u0022mouthwidth\u0022,\u0022nose\u0022,\u0022chin\u0022,\u0022mouthprojection\u0022,\u0022chinheight\u0022};\n public static readonly Vector3[][] Heads={\nnew Vector3[]{new(3.6992545f,-0.066790596f,68.14279f),new(5.2946215f,0.11305821f,64.60188f),new(5.959813f,-0.041913833f,63.655262f),new(6.1343274f,-0.058205567f,63.346245f),new(5.8273835f,0.07009718f,61.76828f),new(5.7244573f,0.09594756f,61.383133f),new(4.6132507f,-0.040823318f,59.63866f)},\nnew Vector3[]{new(3.6878302f,-0.014352961f,68.02986f),new(5.085779f,0.0712296f,64.49504f),new(5.841306f,-0.03587849f,63.486412f),new(6.0319343f,-0.045126636f,63.211014f),new(5.53363f,0.09146978f,61.719315f),new(5.485255f,0.1368592f,61.244686f),new(4.676547f,-0.012164734f,59.841347f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.2011194f,0.07334158f,64.609566f),new(5.979442f,-0.033484418f,63.572296f),new(6.1241097f,-0.03964384f,63.281433f),new(5.778057f,0.053285245f,61.74f),new(5.719415f,0.069648676f,61.50413f),new(4.7025223f,-0.06262799f,59.70981f)},\nnew Vector3[]{new(3.5149546f,-0.009116913f,68.08719f),new(5.0824428f,0.056152936f,64.4079f),new(5.857973f,-0.08385171f,63.312653f),new(6.0649567f,-0.10289183f,62.99997f),new(5.4969316f,0.05211438f,61.666157f),new(5.502199f,0.10385137f,61.29811f),new(4.67235f,-0.1021266f,59.774113f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.2132f,0.087325275f,64.6164f),new(5.957733f,0.064072326f,63.39234f),new(6.137288f,0.064377435f,63.1475f),new(5.6911297f,0.052945673f,61.71075f),new(5.713699f,0.06934876f,61.489025f),new(4.702329f,-0.055987295f,59.563744f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.2012277f,0.1540686f,64.59866f),new(6.0016494f,-0.08192249f,63.62896f),new(6.165094f,-0.049669266f,63.36159f),new(5.7699647f,0.05389818f,61.78059f),new(5.719415f,0.06964957f,61.493782f),new(4.700662f,-0.0723621f,59.84928f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.173374f,0.09990916f,64.60435f),new(5.9552946f,0.042667035f,63.536186f),new(6.1126857f,0.03899601f,63.296906f),new(5.6911297f,0.0529435f,61.727646f),new(5.713699f,0.07963325f,61.420948f),new(4.702329f,-0.05598729f,59.74437f)}\n };\n // [head * Controls.Length * 2 \u002B control * 2 \u002B positiveEndpoint]\n public static readonly Vector3[][] Deltas={\nnew Vector3[]{new(-0.13209844f,0.0031800792f,-0.07652283f),new(-0.24389982f,-0.0069441274f,-0.03074646f),new(-0.23990822f,0.0022496209f,-0.010990143f),new(-0.23487473f,0.0029714853f,-0.0055236816f),new(-0.23757076f,-0.003810294f,0.020923615f),new(-0.23392248f,-0.0052277073f,0.02753067f),new(-0.2026701f,0.0023912787f,0.061531067f)},\nnew Vector3[]{new(0.13209844f,-0.0031800792f,0.07652283f),new(0.24389982f,0.0069441274f,0.03074646f),new(0.23990822f,-0.0022496209f,0.010990143f),new(0.23487473f,-0.0029714853f,0.0055236816f),new(0.23757076f,0.003810294f,-0.020923615f),new(0.23392248f,0.0052277073f,-0.02753067f),new(0.2026701f,-0.0023912787f,-0.061531067f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.011735871f,0f),new(0f,0.016297556f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.011735871f,0f),new(0f,-0.016297556f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.3449974f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.0107391365f,0f),new(0f,-0.0056006014f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.010739133f,0f),new(0f,0.0056006014f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.041153908f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.0532794f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.13788319f,0f,0f),new(-0.052534103f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.19303656f,0f,0f),new(0.07354784f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.47891617f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.31927872f)},\nnew Vector3[]{new(-0.1366136f,0.0007089311f,-0.07763672f),new(-0.24782324f,-0.004627906f,-0.03035736f),new(-0.2453003f,0.002008915f,-0.00851059f),new(-0.23964739f,0.0023905002f,-0.0034942627f),new(-0.24498606f,-0.0053994358f,0.02362442f),new(-0.23736048f,-0.007896334f,0.031650543f),new(-0.20918512f,0.0007255161f,0.05886841f)},\nnew Vector3[]{new(0.1366136f,-0.0007089311f,0.07763672f),new(0.24782324f,0.004627906f,0.03035736f),new(0.2453003f,-0.002008915f,0.00851059f),new(0.23964739f,-0.0023905002f,0.0034942627f),new(0.24498606f,0.0053994358f,-0.02362442f),new(0.23736048f,0.007896334f,-0.031650543f),new(0.20918512f,-0.0007255161f,-0.05886841f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.010045975f,0f),new(0f,0.012437068f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.010045975f,0f),new(0f,-0.012437068f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.33958435f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.33958435f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.018210128f,0f),new(0f,-0.0033276677f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.018210128f,0f),new(0f,0.0033276677f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4882121f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4882121f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.2155137f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.27901363f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.17917538f,0f,0f),new(-0.02188301f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.25084543f,0f,0f),new(0.03063631f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5400009f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.3600006f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.2449646f,-0.004605703f,-0.031585693f),new(-0.2398367f,0.001790762f,-0.009563446f),new(-0.23560143f,0.0020335242f,-0.004512787f),new(-0.2387433f,-0.002935592f,0.021694183f),new(-0.23645878f,-0.0038393363f,0.02576828f),new(-0.2047453f,0.0036357194f,0.059688568f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.2449646f,0.004605703f,0.031585693f),new(0.2398367f,-0.001790762f,0.009563446f),new(0.23560143f,-0.0020335242f,0.004512787f),new(0.2387433f,0.002935592f,-0.021694183f),new(0.23645878f,0.0038393363f,-0.02576828f),new(0.2047453f,-0.0036357194f,-0.059688568f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009375636f,0f),new(0f,0.011100274f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009375636f,0f),new(0f,-0.011100274f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.008732874f,0f),new(0f,-0.008089323f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.008732874f,0f),new(0f,0.008089326f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.1130929f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.14641476f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.14750004f,0f,0f),new(-0.104530334f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.20650053f,0f,0f),new(0.14634228f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5328598f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.35523987f)},\nnew Vector3[]{new(-0.13274455f,0.0004590759f,-0.08004761f),new(-0.24919367f,-0.0036709309f,-0.028762817f),new(-0.24560404f,0.004687473f,-0.0054626465f),new(-0.238554f,0.0053960904f,0f),new(-0.24498224f,-0.0030967854f,0.02476883f),new(-0.23813248f,-0.0059928596f,0.03068924f),new(-0.20696688f,0.006031759f,0.059539795f)},\nnew Vector3[]{new(0.13274455f,-0.0004590759f,0.08004761f),new(0.24919367f,0.0036709309f,0.028762817f),new(0.24560404f,-0.004687473f,0.0054626465f),new(0.238554f,-0.0053960904f,0f),new(0.24498224f,0.0030967854f,-0.02476883f),new(0.23813248f,0.0059928596f,-0.03068924f),new(0.20696688f,-0.006031759f,-0.059539795f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,-5.4202974E-05f,0f),new(0f,0.02339752f,0f),new(0f,0.020724386f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,5.4202974E-05f,0f),new(0f,-0.02339752f,0f),new(0f,-0.020724379f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,-0.0011901855f),new(0f,0f,-0.34381104f),new(0f,0f,-0.24817657f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0.0011901855f),new(0f,0f,0.34381104f),new(0f,0f,0.24817657f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.01034468f,0f),new(0f,-0.0050033107f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.01034468f,0f),new(0f,0.0050033107f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(-0.001709938f,0f,0f),new(-0.49428988f,0f,0f),new(-0.3567996f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0.001709938f,0f,0f),new(0.49428988f,0f,0f),new(0.3567996f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.15790367f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.20442915f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.17864943f,0f,0f),new(-0.043359756f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.2501092f,0f,0f),new(0.060703754f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.53889465f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.35926437f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.24469805f,-0.0054651946f,-0.03161621f),new(-0.24176359f,-0.0034667253f,-0.0066337585f),new(-0.23539543f,-0.0032922663f,-0.002357483f),new(-0.24088907f,-0.0029880553f,0.02273941f),new(-0.23634768f,-0.0038248226f,0.026042938f),new(-0.19970894f,0.0031703971f,0.060806274f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.24469805f,0.0054651946f,0.03161621f),new(0.24176359f,0.0034667253f,0.0066337585f),new(0.23539543f,0.0032922626f,0.002357483f),new(0.24088907f,0.0029880553f,-0.02273941f),new(0.23634768f,0.0038248226f,-0.026042938f),new(0.19970894f,-0.0031703971f,-0.060806274f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.017940246f,0f),new(0f,-0.017056447f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.017940246f,0f),new(0f,0.017056443f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.32645035f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.32645035f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009510666f,0f),new(0f,-0.0076843873f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009510666f,0f),new(0f,0.0076843947f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4693303f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4693303f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.015622139f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.020225048f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.16166735f,0f,0f),new(-0.099727154f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.22633457f,0f,0f),new(0.13961792f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.4568138f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.30454254f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.2450552f,-0.009678587f,-0.03138733f),new(-0.23850155f,0.004340723f,-0.010414124f),new(-0.23345089f,0.0025077388f,-0.005706787f),new(-0.2396326f,-0.002984602f,0.021102905f),new(-0.2362647f,-0.003836237f,0.025924683f),new(-0.2093401f,0.004296787f,0.05846405f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.2450552f,0.009678587f,0.03138733f),new(0.23850155f,-0.004340723f,0.010414124f),new(0.23345089f,-0.0025077388f,0.005706787f),new(0.2396326f,0.002984602f,-0.021102905f),new(0.2362647f,0.003836237f,-0.025924683f),new(0.2093401f,-0.004296787f,-0.05846405f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.022938296f,0f),new(0f,0.013907395f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.022938296f,0f),new(0f,-0.013907395f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009057671f,0f),new(0f,-0.0077751838f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009057675f,0f),new(0f,0.00777518f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.22866583f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.29604006f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.15124607f,0f,0f),new(-0.10046959f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.21174479f,0f,0f),new(0.14065742f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5400009f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.3600006f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.24530983f,-0.006316647f,-0.031700134f),new(-0.24099922f,-0.002302207f,-0.0090408325f),new(-0.23603249f,-0.0020077005f,-0.004776001f),new(-0.24115801f,-0.0029912703f,0.022464752f),new(-0.23502398f,-0.004367456f,0.027061462f),new(-0.2059083f,0.0032688156f,0.05939865f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.24530983f,0.006316647f,0.031700134f),new(0.24099922f,0.002302207f,0.0090408325f),new(0.23603249f,0.0020077005f,0.004776001f),new(0.24115801f,0.0029912703f,-0.022464752f),new(0.23502398f,0.004367456f,-0.027061462f),new(0.2059083f,-0.0032688156f,-0.05939865f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.011946769f,0f),new(0f,-0.010918882f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.011946768f,0f),new(0f,0.0109188855f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009599518f,0f),new(0f,-0.0062645525f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009599518f,0f),new(0f,0.0062645525f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.14241362f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.18437433f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.16318464f,0f,0f),new(-0.07080078f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.2284584f,0f,0f),new(0.099121094f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5386009f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.359066f)}\n };\n}\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/AppearanceCatalog.cs","FileName":"AppearanceCatalog.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace CharacterEditor;\r\npublic sealed record AppearanceOption(string Id, string Label, string ResourcePath, string BodyType = \u0022\u0022, string Icon = \u0022\u0022);\r\npublic sealed record AppearancePreset(string Id, string Label, CharacterAppearanceData Appearance);\r\npublic sealed record AppearanceColorOption(string Id, string Label, Color Color, bool NormalizeSkinTone = false);\r\npublic sealed class AppearanceSlider\r\n{\r\n\tpublic string Id { get; }\r\n\tpublic string Label { get; }\r\n\tpublic string Group { get; }\r\n\tpublic float Min =\u003E 0;\r\n\tpublic float Max =\u003E 1;\r\n\tpublic float Neutral =\u003E Id is \u0022eartaper\u0022 or \u0022belly\u0022 ? 0 : 0.5f;\r\n\r\n\tpublic string LabelFor(CharacterAppearanceData data) =\u003E Id == \u0022bodyfeature\u0022 ? (AppearanceCatalog.IsFemale(data) ? \u0022Breast size\u0022 : \u0022Bulge size\u0022) : Label;\r\n\tpublic AppearanceSlider(string id, string label, string group)\r\n\t{\r\n\t\tId = id;\r\n\t\tLabel = label;\r\n\t\tGroup = group;\r\n\t}\r\n\r\n\tpublic float Get(CharacterAppearanceData data) =\u003E Id switch\r\n\t{\r\n\t\t\u0022neck\u0022 =\u003E data.NeckSize,\r\n\t\t\u0022trapezius\u0022 =\u003E data.TrapeziusSize,\r\n\t\t\u0022collarbone\u0022 =\u003E data.CollarboneHeight,\r\n\t\t\u0022belly\u0022 =\u003E data.BellySize,\r\n\t\t\u0022waist\u0022 =\u003E data.WaistSize,\r\n\t\t\u0022chest\u0022 =\u003E data.ChestSize,\r\n\t\t\u0022fitness\u0022 =\u003E data.Fitness,\r\n\t\t\u0022head\u0022 =\u003E data.HeadShape,\r\n\t\t\u0022nose\u0022 =\u003E data.NoseSize,\r\n\t\t\u0022nosewidth\u0022 =\u003E data.NoseWidth,\r\n\t\t\u0022chin\u0022 =\u003E data.ChinSize,\r\n\t\t\u0022chinwidth\u0022 =\u003E data.ChinWidth,\r\n\t\t\u0022jaw\u0022 =\u003E data.JawWidth,\r\n\t\t\u0022nostrils\u0022 =\u003E data.NostrilWidth,\r\n\t\t\u0022forehead\u0022 =\u003E data.ForeheadWidth,\r\n\t\t\u0022cheekbones\u0022 =\u003E data.CheekboneWidth,\r\n\t\t\u0022ears\u0022 =\u003E data.EarSize,\r\n\t\t\u0022eyes\u0022 =\u003E data.EyeSize,\r\n\t\t\u0022cheeks\u0022 =\u003E data.CheekFullness,\r\n\t\t\u0022nostrilfullness\u0022 =\u003E data.NostrilFullness,\r\n\t\t\u0022noseheight\u0022 =\u003E data.NoseHeight,\r\n\t\t\u0022chinheight\u0022 =\u003E data.ChinHeight,\r\n\t\t\u0022submental\u0022 =\u003E data.SubmentalFullness,\r\n\t\t\u0022brow\u0022 =\u003E data.BrowProminence,\r\n\t\t\u0022bodyfeature\u0022 =\u003E data.BodyFeatureSize,\r\n\t\t\u0022butt\u0022 =\u003E data.ButtSize,\r\n\t\t\u0022feet\u0022 =\u003E data.FootSize,\r\n\t\t\u0022eartaper\u0022 =\u003E data.EarTaper,\r\n\t\t\u0022philtrum\u0022 =\u003E data.PhiltrumWidth,\r\n\t\t\u0022columella\u0022 =\u003E data.ColumellaWidth,\r\n\t\t\u0022mouthwidth\u0022 =\u003E data.MouthWidth,\r\n\t\t\u0022mouthprojection\u0022 =\u003E data.MouthProjection,\r\n\t\t\u0022upperlip\u0022 =\u003E data.UpperLipFullness,\r\n\t\t\u0022lowerlip\u0022 =\u003E data.LowerLipFullness,\r\n\t\t_ =\u003E Neutral\r\n\t};\r\n\tpublic void Set(CharacterAppearanceData data, float value)\r\n\t{\r\n\t\tif (data is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tvalue = Math.Clamp(value, Min, Max);\r\n\t\t// Store data rather than delegates in the catalog so native hotload can preserve these selectors.\r\n\t\tswitch (Id)\r\n\t\t{\r\n\t\t\tcase \u0022neck\u0022:\r\n\t\t\t\tdata.NeckSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022trapezius\u0022:\r\n\t\t\t\tdata.TrapeziusSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022collarbone\u0022:\r\n\t\t\t\tdata.CollarboneHeight = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022belly\u0022:\r\n\t\t\t\tdata.BellySize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022waist\u0022:\r\n\t\t\t\tdata.WaistSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022chest\u0022:\r\n\t\t\t\tdata.ChestSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022fitness\u0022:\r\n\t\t\t\tdata.Fitness = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022head\u0022:\r\n\t\t\t\tdata.HeadShape = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022nose\u0022:\r\n\t\t\t\tdata.NoseSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022nosewidth\u0022:\r\n\t\t\t\tdata.NoseWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022chin\u0022:\r\n\t\t\t\tdata.ChinSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022chinwidth\u0022:\r\n\t\t\t\tdata.ChinWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022jaw\u0022:\r\n\t\t\t\tdata.JawWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022nostrils\u0022:\r\n\t\t\t\tdata.NostrilWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022forehead\u0022:\r\n\t\t\t\tdata.ForeheadWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022cheekbones\u0022:\r\n\t\t\t\tdata.CheekboneWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022ears\u0022:\r\n\t\t\t\tdata.EarSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022eyes\u0022:\r\n\t\t\t\tdata.EyeSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022cheeks\u0022:\r\n\t\t\t\tdata.CheekFullness = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022nostrilfullness\u0022:\r\n\t\t\t\tdata.NostrilFullness = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022noseheight\u0022:\r\n\t\t\t\tdata.NoseHeight = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022chinheight\u0022:\r\n\t\t\t\tdata.ChinHeight = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022submental\u0022:\r\n\t\t\t\tdata.SubmentalFullness = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022brow\u0022:\r\n\t\t\t\tdata.BrowProminence = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022bodyfeature\u0022:\r\n\t\t\t\tdata.BodyFeatureSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022butt\u0022:\r\n\t\t\t\tdata.ButtSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022feet\u0022:\r\n\t\t\t\tdata.FootSize = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022eartaper\u0022:\r\n\t\t\t\tdata.EarTaper = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022philtrum\u0022:\r\n\t\t\t\tdata.PhiltrumWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022columella\u0022:\r\n\t\t\t\tdata.ColumellaWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022mouthwidth\u0022:\r\n\t\t\t\tdata.MouthWidth = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022mouthprojection\u0022:\r\n\t\t\t\tdata.MouthProjection = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022upperlip\u0022:\r\n\t\t\t\tdata.UpperLipFullness = value;\r\n\t\t\t\tbreak;\r\n\t\t\tcase \u0022lowerlip\u0022:\r\n\t\t\t\tdata.LowerLipFullness = value;\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/// \u003Csummary\u003ECurated installed, non-inventory native assets. IDs and paths are stable; no cloud discovery or external download is needed.\u003C/summary\u003E\r\npublic static class AppearanceCatalog\r\n{\r\n\tstatic readonly Dictionary\u003Cstring, Task\u003E resourceLoads = new(StringComparer.OrdinalIgnoreCase);\r\n\tpublic static bool Loading =\u003E resourceLoads.Values.Any(x =\u003E x is not null \u0026\u0026 !x.IsCompleted);\r\n\tpublic static string Error { get; private set; }\r\n\tpublic static int Revision { get; private set; }\r\n\r\n\tconst string Root = \u0022models/citizen_clothes/\u0022;\r\n\tstatic AppearanceOption Skin(string number, string label, string body) =\u003E new(\u0022human\u0022 \u002B number, label, Root \u002B \u0022body/human\u0022 \u002B number \u002B \u0022.clothing\u0022, body, Root \u002B \u0022body/human\u0022 \u002B number \u002B \u0022.clothing.png\u0022);\r\n\tstatic AppearanceOption Style(string id, string label, string relative, string body = \u0022\u0022) =\u003E new(id, label, Root \u002B \u0022hair/\u0022 \u002B relative \u002B \u0022.clothing\u0022, body, Root \u002B \u0022hair/\u0022 \u002B relative \u002B \u0022.clothing.png\u0022);\r\n\tpublic static IReadOnlyList\u003CAppearanceOption\u003E Skins { get; } = Array.AsReadOnly(new[] { Skin(\u002201\u0022, \u0022Face 1 \u00B7 dark \u00B7 male\u0022, \u0022male\u0022), Skin(\u002202\u0022, \u0022Face 2 \u00B7 dark \u00B7 female\u0022, \u0022female\u0022), Skin(\u002203\u0022, \u0022Face 3 \u00B7 light \u00B7 male\u0022, \u0022male\u0022), Skin(\u002204\u0022, \u0022Face 4 \u00B7 light \u00B7 female\u0022, \u0022female\u0022), Skin(\u002205\u0022, \u0022Face 5 \u00B7 light \u00B7 male\u0022, \u0022male\u0022), Skin(\u002206\u0022, \u0022Face 6 \u00B7 dark \u00B7 male\u0022, \u0022male\u0022), Skin(\u002207\u0022, \u0022Face 7 \u00B7 light \u00B7 male\u0022, \u0022male\u0022) });\r\n\tpublic static IReadOnlyList\u003CAppearanceOption\u003E Hair { get; } = Array.AsReadOnly(new[] { new AppearanceOption(\u0022none\u0022, \u0022No hair\u0022, \u0022\u0022), Style(\u0022scruffy\u0022, \u0022Short scruffy\u0022, \u0022hair_shortscruffy/hair_shortscruffy_brown\u0022), Style(\u0022short_braids\u0022, \u0022Short braids\u0022, \u0022hair_short_braids/hair_short_braids\u0022), Style(\u0022short_dreads\u0022, \u0022Short dreadlocks\u0022, \u0022hair_short_dreadlocks/short_dreadlocks\u0022), Style(\u0022bob\u0022, \u0022Bob cut\u0022, \u0022hair_bobcut/hair_bobcut\u0022), Style(\u0022long_bob\u0022, \u0022Long bob\u0022, \u0022hair_longbob/hair_longbob\u0022), Style(\u0022messy_bun\u0022, \u0022Messy bun\u0022, \u0022hair_messy_bun/hair_messy_bun\u0022), Style(\u0022ponytail\u0022, \u0022Tight ponytail\u0022, \u0022hair_tight_ponytail/hair_tight_ponytail_brown\u0022), Style(\u0022long_curly\u0022, \u0022Long curly\u0022, \u0022hair_longcurly/hair_longcurly_brown\u0022), Style(\u0022long_bangs\u0022, \u0022Long bangs\u0022, \u0022hair_long_bangs/hair_long_bangs\u0022), Style(\u0022long_dreads\u0022, \u0022Long dreadlocks\u0022, \u0022hair_long_dreadlocks/hair_long_dreadlocks\u0022), Style(\u0022tied_braids\u0022, \u0022Tied braids\u0022, \u0022hair_tied_braids/hair_tied_braids\u0022), Style(\u0022tied_dreads\u0022, \u0022Tied dreadlocks\u0022, \u0022hair_tied_dreads/hair_tied_dreads\u0022), Style(\u0022slicked\u0022, \u0022Slicked back\u0022, \u0022hair_slickedback/hair_slickedback\u0022), Style(\u0022balding\u0022, \u0022Balding\u0022, \u0022hair_balding/hair_baldingbrown\u0022), Style(\u0022thinning\u0022, \u0022Thinning short\u0022, \u0022hair_thinning_short/thinning_short\u0022), Style(\u0022pigtails\u0022, \u0022Pigtails\u0022, \u0022hair_pigtails/hair_pigtails\u0022), Style(\u0022long_brown\u0022, \u0022Long hair\u0022, \u0022hair_longbrown/Models/hair_longbrown\u0022), Style(\u0022loose\u0022, \u0022Loose hair\u0022, \u0022hair_looseblonde/hair.loose.grey\u0022), Style(\u0022slicked_balding\u0022, \u0022Slicked back balding\u0022, \u0022hair_slicked_back_balding/hair_slicked_back_balding\u0022) });\r\n\tpublic static IReadOnlyList\u003CAppearanceOption\u003E FacialHair { get; } = Array.AsReadOnly(new[] { new AppearanceOption(\u0022none\u0022, \u0022No facial hair\u0022, \u0022\u0022), Style(\u0022patchy\u0022, \u0022Patchy beard\u0022, \u0022patchy_beard/patchy_beard\u0022, \u0022male\u0022), Style(\u0022medium\u0022, \u0022Medium beard\u0022, \u0022medium_beard/medium_beard\u0022, \u0022male\u0022), Style(\u0022goatee\u0022, \u0022Goatee\u0022, \u0022goatee/goatee\u0022, \u0022male\u0022), Style(\u0022moustache\u0022, \u0022Moustache\u0022, \u0022moustache/moustache\u0022, \u0022male\u0022), Style(\u0022handlebar\u0022, \u0022Handlebar moustache\u0022, \u0022handlebar_moustache/handlebar_moustache\u0022, \u0022male\u0022), Style(\u0022chops\u0022, \u0022Sideburns\u0022, \u0022mutton_chops/mutton_chops\u0022, \u0022male\u0022), Style(\u0022braided\u0022, \u0022Braided beard\u0022, \u0022braided_long_beard/braided_long_beard\u0022, \u0022male\u0022), Style(\u0022decorated\u0022, \u0022Decorated beard\u0022, \u0022decorated_beard/decorated_beard\u0022, \u0022male\u0022) });\r\n\tpublic static IReadOnlyList\u003CAppearanceColorOption\u003E Colors { get; } = Array.AsReadOnly(new[] { new AppearanceColorOption(\u0022black\u0022, \u0022Black\u0022, new Color(0.045f, 0.035f, 0.028f)), new AppearanceColorOption(\u0022brown\u0022, \u0022Brown\u0022, new Color(0.22f, 0.13f, 0.08f)), new AppearanceColorOption(\u0022auburn\u0022, \u0022Auburn\u0022, new Color(0.48f, 0.17f, 0.07f)), new AppearanceColorOption(\u0022blonde\u0022, \u0022Blonde\u0022, new Color(0.74f, 0.57f, 0.31f)), new AppearanceColorOption(\u0022silver\u0022, \u0022Silver\u0022, new Color(0.66f, 0.67f, 0.7f)), new AppearanceColorOption(\u0022white\u0022, \u0022White\u0022, new Color(0.92f, 0.89f, 0.82f)) });\r\n\r\n\tpublic const int ShapeControlCount = 34;\r\n\tpublic static bool IsFemale(CharacterAppearanceData data) =\u003E data?.BaseShape is not null ? data.BaseShape.MaleBody \u003C .5f : Skins.FirstOrDefault(x =\u003E string.Equals(x.ResourcePath, data?.SkinPath, StringComparison.OrdinalIgnoreCase))?.BodyType == \u0022female\u0022;\r\n\tstatic IReadOnlyList\u003CAppearanceColorOption\u003E humanSkinColors;\r\n\tpublic static IReadOnlyList\u003CAppearanceColorOption\u003E HumanSkinColors\r\n\t{\r\n\t\tget\r\n\t\t{\r\n\t\t\tif (humanSkinColors?.Count != 7 || !humanSkinColors[1].NormalizeSkinTone)\r\n\t\t\t\thumanSkinColors = CreateHumanSkinColors();\r\n\t\t\treturn humanSkinColors;\r\n\t\t}\r\n\t}\r\n\r\n\tstatic IReadOnlyList\u003CAppearanceColorOption\u003E CreateHumanSkinColors() =\u003E Array.AsReadOnly(new[] { new AppearanceColorOption(\u0022native\u0022, \u0022Native\u0022, Color.White), new AppearanceColorOption(\u0022fair\u0022, \u0022Fair\u0022, Color.White, true), new AppearanceColorOption(\u0022rose\u0022, \u0022Rose\u0022, new Color(1, 0.96f, 0.96f), true), new AppearanceColorOption(\u0022golden\u0022, \u0022Golden\u0022, new Color(1, 0.96f, 0.88f), true), new AppearanceColorOption(\u0022olive\u0022, \u0022Olive\u0022, new Color(0.91f, 0.88f, 0.77f), true), new AppearanceColorOption(\u0022tan\u0022, \u0022Tan\u0022, new Color(0.72f, 0.6f, 0.5f), true), new AppearanceColorOption(\u0022umber\u0022, \u0022Umber\u0022, new Color(0.42f, 0.3f, 0.23f), true) });\r\n\tstatic IReadOnlyList\u003CAppearanceColorOption\u003E fantasySkinColors;\r\n\tpublic static IReadOnlyList\u003CAppearanceColorOption\u003E FantasySkinColors\r\n\t{\r\n\t\tget\r\n\t\t{\r\n\t\t\tif (fantasySkinColors?.Count != 6 || fantasySkinColors.Any(x =\u003E !x.NormalizeSkinTone))\r\n\t\t\t\tfantasySkinColors = CreateFantasySkinColors();\r\n\t\t\treturn fantasySkinColors;\r\n\t\t}\r\n\t}\r\n\r\n\tstatic IReadOnlyList\u003CAppearanceColorOption\u003E CreateFantasySkinColors() =\u003E Array.AsReadOnly(new[] { new AppearanceColorOption(\u0022blue\u0022, \u0022Blue\u0022, new Color(0.18f, 0.4f, 1), true), new AppearanceColorOption(\u0022purple\u0022, \u0022Purple\u0022, new Color(0.6f, 0.25f, 1), true), new AppearanceColorOption(\u0022green\u0022, \u0022Green\u0022, new Color(0.3f, 0.85f, 0.4f), true), new AppearanceColorOption(\u0022red\u0022, \u0022Crimson\u0022, new Color(1, 0.25f, 0.22f), true), new AppearanceColorOption(\u0022teal\u0022, \u0022Teal\u0022, new Color(0.18f, 0.85f, 0.9f), true), new AppearanceColorOption(\u0022grey\u0022, \u0022Slate\u0022, new Color(0.5f, 0.6f, 0.74f), true) });\r\n\t// Native hotload can retain the previous static list when the catalog grows.\r\n\t// Build the supported immutable catalog lazily so new controls are available\r\n\t// in an already running editor as well as a cold-start game.\r\n\tpublic static IReadOnlyList\u003CAppearanceSlider\u003E Sliders\r\n\t{\r\n\t\tget\r\n\t\t{\r\n\t\t\tif (contourControls?.Count != ShapeControlCount)\r\n\t\t\t\tcontourControls = CreateContourControls();\r\n\t\t\treturn contourControls;\r\n\t\t}\r\n\t}\r\n\r\n\tstatic IReadOnlyList\u003CAppearanceSlider\u003E contourControls;\r\n\tstatic IReadOnlyList\u003CAppearanceSlider\u003E CreateContourControls() =\u003E Array.AsReadOnly(new[] { new AppearanceSlider(\u0022neck\u0022, \u0022Neck size\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022trapezius\u0022, \u0022Trapezius\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022collarbone\u0022, \u0022Collarbone height\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022belly\u0022, \u0022Belly size\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022waist\u0022, \u0022Waist size\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022chest\u0022, \u0022Chest size\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022fitness\u0022, \u0022Fitness\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022bodyfeature\u0022, \u0022Body contour\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022butt\u0022, \u0022Butt size\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022feet\u0022, \u0022Foot size\u0022, \u0022Body\u0022), new AppearanceSlider(\u0022head\u0022, \u0022Head shape\u0022, \u0022Face\u0022), new AppearanceSlider(\u0022forehead\u0022, \u0022Forehead width\u0022, \u0022Face\u0022), new AppearanceSlider(\u0022cheekbones\u0022, \u0022Cheekbone width\u0022, \u0022Face\u0022), new AppearanceSlider(\u0022cheeks\u0022, \u0022Cheek fullness\u0022, \u0022Face\u0022), new AppearanceSlider(\u0022jaw\u0022, \u0022Jaw width\u0022, \u0022Jaw \u0026 chin\u0022), new AppearanceSlider(\u0022chin\u0022, \u0022Chin projection\u0022, \u0022Jaw \u0026 chin\u0022), new AppearanceSlider(\u0022chinwidth\u0022, \u0022Chin width\u0022, \u0022Jaw \u0026 chin\u0022), new AppearanceSlider(\u0022chinheight\u0022, \u0022Chin height\u0022, \u0022Jaw \u0026 chin\u0022), new AppearanceSlider(\u0022submental\u0022, \u0022Under-chin fullness\u0022, \u0022Jaw \u0026 chin\u0022), new AppearanceSlider(\u0022nose\u0022, \u0022Nose projection\u0022, \u0022Nose\u0022), new AppearanceSlider(\u0022nosewidth\u0022, \u0022Nose width\u0022, \u0022Nose\u0022), new AppearanceSlider(\u0022nostrils\u0022, \u0022Nostril width\u0022, \u0022Nose\u0022), new AppearanceSlider(\u0022nostrilfullness\u0022, \u0022Nostril-wing fullness\u0022, \u0022Nose\u0022), new AppearanceSlider(\u0022noseheight\u0022, \u0022Nose tip height\u0022, \u0022Nose\u0022), new AppearanceSlider(\u0022columella\u0022, \u0022Columella width\u0022, \u0022Nose\u0022), new AppearanceSlider(\u0022philtrum\u0022, \u0022Philtrum width\u0022, \u0022Mouth\u0022), new AppearanceSlider(\u0022mouthwidth\u0022, \u0022Mouth width\u0022, \u0022Mouth\u0022), new AppearanceSlider(\u0022mouthprojection\u0022, \u0022Mouth projection\u0022, \u0022Mouth\u0022), new AppearanceSlider(\u0022upperlip\u0022, \u0022Upper lip fullness\u0022, \u0022Mouth\u0022), new AppearanceSlider(\u0022lowerlip\u0022, \u0022Lower lip fullness\u0022, \u0022Mouth\u0022), new AppearanceSlider(\u0022eyes\u0022, \u0022Eye size\u0022, \u0022Eyes \u0026 ears\u0022), new AppearanceSlider(\u0022ears\u0022, \u0022Ear size\u0022, \u0022Eyes \u0026 ears\u0022), new AppearanceSlider(\u0022brow\u0022, \u0022Brow prominence\u0022, \u0022Eyes \u0026 ears\u0022), new AppearanceSlider(\u0022eartaper\u0022, \u0022Ear taper\u0022, \u0022Eyes \u0026 ears\u0022) });\r\n\tstatic CharacterAppearanceData Preset(string skin, string hair, string facial, string color) =\u003E new()\r\n\t{\r\n\t\tSkinPath = Skins.Single(x =\u003E x.Id == skin).ResourcePath,\r\n\t\tHairPath = Hair.Single(x =\u003E x.Id == hair).ResourcePath,\r\n\t\tFacialHairPath = FacialHair.Single(x =\u003E x.Id == facial).ResourcePath,\r\n\t\tHairColor = Colors.Single(x =\u003E x.Id == color).Color\r\n\t};\r\n\tstatic IReadOnlyList\u003CAppearancePreset\u003E presets;\r\n\tpublic static IReadOnlyList\u003CAppearancePreset\u003E Presets\r\n\t{\r\n\t\tget\r\n\t\t{\r\n\t\t\tif (presets?.Count != 7)\r\n\t\t\t\tpresets = Array.AsReadOnly(new[]\r\n\t\t\t\t{\r\n\t\t\t\t\tnew AppearancePreset(\u0022traveller\u0022, \u0022Traveller\u0022, Preset(\u0022human03\u0022, \u0022scruffy\u0022, \u0022none\u0022, \u0022brown\u0022)),\r\n\t\t\t\t\tnew AppearancePreset(\u0022scout\u0022, \u0022Scout\u0022, Preset(\u0022human02\u0022, \u0022short_braids\u0022, \u0022none\u0022, \u0022black\u0022)),\r\n\t\t\t\t\tnew AppearancePreset(\u0022scholar\u0022, \u0022Scholar\u0022, Preset(\u0022human04\u0022, \u0022messy_bun\u0022, \u0022none\u0022, \u0022auburn\u0022)),\r\n\t\t\t\t\tnew AppearancePreset(\u0022veteran\u0022, \u0022Veteran\u0022, Preset(\u0022human07\u0022, \u0022balding\u0022, \u0022medium\u0022, \u0022silver\u0022)),\r\n\t\t\t\t\tnew AppearancePreset(\u0022wanderer\u0022, \u0022Wanderer\u0022, Preset(\u0022human06\u0022, \u0022short_dreads\u0022, \u0022none\u0022, \u0022black\u0022)),\r\n\t\t\t\t\tnew AppearancePreset(\u0022artisan\u0022, \u0022Artisan\u0022, Preset(\u0022human05\u0022, \u0022slicked\u0022, \u0022goatee\u0022, \u0022brown\u0022)),\r\n\t\t\t\t\tnew AppearancePreset(\u0022explorer\u0022, \u0022Explorer\u0022, Preset(\u0022human01\u0022, \u0022tied_braids\u0022, \u0022none\u0022, \u0022black\u0022))\r\n\t\t\t\t});\r\n\t\t\treturn presets;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static CharacterAppearanceData CreateDefault() =\u003E ApplyPreset(\u0022traveller\u0022);\r\n\tpublic static CharacterAppearanceData ApplyPreset(string id) =\u003E Presets.FirstOrDefault(x =\u003E x.Id == id)?.Appearance.DeepCopy() ?? new CharacterAppearanceData();\r\n\tpublic static void ResetShapes(CharacterAppearanceData data)\r\n\t{\r\n\t\tif (data is null)\r\n\t\t\treturn;\r\n\t\tforeach (var slider in Sliders)\r\n\t\t\tslider.Set(data, slider.Neutral);\r\n\t\tif (data.BaseShape is not null)\r\n\t\t{\r\n\t\t\tdata.BaseShape.BreastSize = .5f;\r\n\t\t\tdata.BaseShape.BulgeSize = .5f;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static CharacterAppearanceData Randomize(Random rng = null, bool blendHeads = false)\r\n\t{\r\n\t\trng ??= new Random();\r\n\t\tvar body = rng.Next(2) == 0 ? \u0022male\u0022 : \u0022female\u0022;\r\n\t\t// Cross-body skins need weighted heads; native (v1) characters keep their native pool.\r\n\t\tvar skins = Skins.Where(x =\u003E blendHeads ? (body == \u0022male\u0022 || x.Id is not (\u0022human05\u0022 or \u0022human07\u0022)) : x.BodyType == body).ToArray();\r\n\t\tvar skin = skins[rng.Next(skins.Length)];\r\n\t\tvar hair = RandomHairFor(body);\r\n\t\tvar facial = FacialHair.Where(x =\u003E x.BodyType == \u0022\u0022 || x.BodyType == body).ToArray();\r\n\t\tvar tones = HumanSkinColors.Where(x =\u003E body == \u0022female\u0022 \u0026\u0026 skin.Id == \u0022human06\u0022 ? x.Id is \u0022native\u0022 or \u0022tan\u0022 or \u0022umber\u0022 : x.NormalizeSkinTone).ToArray();\r\n\t\tvar tone = tones[rng.Next(tones.Length)];\r\n\t\tvar data = new CharacterAppearanceData\r\n\t\t{\r\n\t\t\tSkinPath = skin.ResourcePath,\r\n\t\t\tHairPath = hair[rng.Next(hair.Count)].ResourcePath,\r\n\t\t\tFacialHairPath = facial[rng.Next(facial.Length)].ResourcePath,\r\n\t\t\tHairColor = Colors[rng.Next(Colors.Count)].Color,\r\n\t\t\tSkinColor = tone.Color,\r\n\t\t\tNormalizeSkinTone = tone.NormalizeSkinTone\r\n\t\t};\r\n\t\tforeach (var slider in Sliders.Where(x =\u003E x.Id != \u0022belly\u0022))\r\n\t\t\tslider.Set(data, 0.3f \u002B (float)rng.NextDouble() * 0.4f);\r\n\t\t// An added belly is an occasional, modest feature rather than a default.\r\n\t\tdata.BellySize = rng.NextDouble() \u003C 0.10 ? 0.1f \u002B (float)rng.NextDouble() * 0.3f : 0;\r\n\t\tif (body == \u0022female\u0022 \u0026\u0026 rng.NextDouble() \u003C 0.60)\r\n\t\t{\r\n\t\t\tAddMakeup(\u0022Lipstick\u0022, 0.70, 0.35f, 0.85f, 4);\r\n\t\t\tAddMakeup(\u0022Eyeshadow\u0022, 0.45, 0.20f, 0.65f, 3);\r\n\t\t\tAddMakeup(\u0022Blush\u0022, 0.55, 0.15f, 0.55f, 4);\r\n\t\t}\r\n\r\n\t\tif (blendHeads)\r\n\t\t{\r\n\t\t\tUnifiedHuman.Enable(data);\r\n\t\t\t// Body identity must follow the roll, not the selected skin\u0027s native body.\r\n\t\t\tbool female = body == \u0022female\u0022;\r\n\t\t\tdata.BaseShape.MaleBody = female ? 0 : 1;\r\n\t\t\tdata.BaseShape.BreastSize = female ? data.BodyFeatureSize : .5f;\r\n\t\t\tdata.BaseShape.BulgeSize = female ? .5f : data.BodyFeatureSize;\r\n\t\t\tdata.BaseShape.HeadWeights = RandomHeadWeights(rng, female, skin.Id, false);\r\n\t\t\tforeach (var region in FaceRegions.Ids)\r\n\t\t\t{\r\n\t\t\t\t// Each part gets its own dominant source rather than averaging every head\r\n\t\t\t\t// equally. Keep jaw/neck identity especially close to the body\u0027s family.\r\n\t\t\t\tdata.BaseShape.Regions[region] = new()\r\n\t\t\t\t{\r\n\t\t\t\t\tOverallInfluence = .15f \u002B (float)rng.NextDouble() * .20f,\r\n\t\t\t\t\tHeadWeights = RandomHeadWeights(rng, female, skin.Id, region is \u0022jaw\u0022 or \u0022throat\u0022)\r\n\t\t\t\t};\r\n\t\t\t}\r\n\r\n\t\t\t// A shared jaw/throat source keeps the lower-face silhouette coherent.\r\n\t\t\tdata.BaseShape.Regions[\u0022throat\u0022] = data.BaseShape.Regions[\u0022jaw\u0022].DeepCopy();\r\n\t\t}\r\n\r\n\t\tdata.ClothingPaths = WardrobeCatalog.Randomize(rng, body == \u0022female\u0022);\r\n\t\tdata.Version = 2;\r\n\t\treturn data;\r\n\t\tvoid AddMakeup(string layer, double chance, float low, float high, int paletteCount)\r\n\t\t{\r\n\t\t\tif (rng.NextDouble() \u003E= chance)\r\n\t\t\t\treturn;\r\n\t\t\tvar options = MakeupCatalog.Colors(layer);\r\n\t\t\tvar target = MakeupCatalog.Layer(data.Makeup, layer);\r\n\t\t\ttarget.Color = options[rng.Next(Math.Min(paletteCount, options.Count))].Color;\r\n\t\t\ttarget.Amount = low \u002B (float)rng.NextDouble() * (high - low);\r\n\t\t}\r\n\t}\r\n\r\n\tstatic float[] RandomHeadWeights(Random rng, bool female, string skin, bool structural)\r\n\t{\r\n\t\t// Allocate by family before choosing individual heads: five masculine faces\r\n\t\t// must not overwhelm the two feminine ones. Skin 04 on men excludes them.\r\n\t\tfloat ownShare = structural ? .95f \u002B (float)rng.NextDouble() * .05f : .85f \u002B (float)rng.NextDouble() * .15f;\r\n\t\tfloat femaleShare = female ? ownShare : skin == \u0022human04\u0022 ? 0 : 1 - ownShare;\r\n\t\tvar weights = new float[7];\r\n\t\tFill(new[] { 1, 3 }, femaleShare);\r\n\t\tFill(new[] { 0, 2, 4, 5, 6 }, 1 - femaleShare);\r\n\t\tfloat max = weights.Max();\r\n\t\tfor (int i = 0; i \u003C weights.Length; i\u002B\u002B)\r\n\t\t\tweights[i] /= max;\r\n\t\treturn weights;\r\n\t\tvoid Fill(int[] indices, float share)\r\n\t\t{\r\n\t\t\tint primary = indices[rng.Next(indices.Length)];\r\n\t\t\tfloat dominance = .50f \u002B (float)rng.NextDouble() * .40f;\r\n\t\t\tweights[primary] = share * dominance;\r\n\t\t\tfloat total = 0;\r\n\t\t\tforeach (int i in indices)\r\n\t\t\t\tif (i != primary)\r\n\t\t\t\t{\r\n\t\t\t\t\tweights[i] = .05f \u002B (float)rng.NextDouble();\r\n\t\t\t\t\ttotal \u002B= weights[i];\r\n\t\t\t\t}\r\n\r\n\t\t\tforeach (int i in indices)\r\n\t\t\t\tif (i != primary)\r\n\t\t\t\t\tweights[i] *= share * (1 - dominance) / total;\r\n\t\t}\r\n\t}\r\n\r\n\t// Preferences apply only to Randomize. Every hairstyle remains manually\r\n\t// selectable and valid on either human body, including male pigtails.\r\n\tpublic static IReadOnlyList\u003CAppearanceOption\u003E RandomHairFor(string bodyType) =\u003E Hair.Where(x =\u003E bodyType == \u0022female\u0022 ? x.Id is not (\u0022balding\u0022 or \u0022thinning\u0022 or \u0022slicked_balding\u0022) : x.Id is \u0022none\u0022 or \u0022scruffy\u0022 or \u0022short_braids\u0022 or \u0022short_dreads\u0022 or \u0022long_dreads\u0022 or \u0022tied_braids\u0022 or \u0022tied_dreads\u0022 or \u0022slicked\u0022 or \u0022balding\u0022 or \u0022thinning\u0022 or \u0022ponytail\u0022 or \u0022long_curly\u0022 or \u0022long_brown\u0022 or \u0022slicked_balding\u0022).ToArray();\r\n\t/// \u003Csummary\u003ELoads only curated native resources, either the whole creator catalog or one selected/saved appearance.\u003C/summary\u003E\r\n\tpublic static async Task EnsureLoadedAsync(CharacterAppearanceData data = null)\r\n\t{\r\n\t\tif (data is not null \u0026\u0026 CharacterAppearanceData.Validate(data)is string error)\r\n\t\t\tthrow new InvalidOperationException(error);\r\n\t\tvar paths = data is null ? Skins.Concat(Hair).Concat(FacialHair).Select(x =\u003E x.ResourcePath) : new[]\r\n\t\t{\r\n\t\t\tdata.SkinPath,\r\n\t\t\tUnifiedHuman.PresentationSkin(data),\r\n\t\t\tdata.HairPath,\r\n\t\t\tdata.FacialHairPath\r\n\t\t};\r\n\t\ttry\r\n\t\t{\r\n\t\t\tforeach (var path in paths.Where(x =\u003E !string.IsNullOrEmpty(x)).Distinct(StringComparer.OrdinalIgnoreCase))\r\n\t\t\t\tawait Load(path);\r\n\t\t\tif (data is null || data.Makeup.Active)\r\n\t\t\t\tforeach (var skin in data is null ? Skins : Skins.Where(x =\u003E x.ResourcePath == data.SkinPath))\r\n\t\t\t\t\tawait HumanMakeupAssets.EnsureLoadedAsync(skin.Id);\r\n\t\t\tif (data is null || data.CustomEyeColor)\r\n\t\t\t\tawait EyeColorPresentation.EnsureLoadedAsync();\r\n\t\t\tif (data is not null)\r\n\t\t\t\tawait HumanMarkingModels.EnsureLoadedAsync(data);\r\n\t\t\tif (data?.BaseShape is not null)\r\n\t\t\t{\r\n\t\t\t\tvar model = await Model.LoadAsync(UnifiedHuman.ModelPath);\r\n\t\t\t\tif (model is null || model.IsError)\r\n\t\t\t\t\tthrow new InvalidOperationException(\u0022The unified human model is unavailable.\u0022);\r\n\t\t\t\tawait UnifiedNeckAssets.LoadHeadAsync(data.SkinPath);\r\n\t\t\t}\r\n\r\n\t\t\tError = null;\r\n\t\t}\r\n\t\tcatch (Exception e)\r\n\t\t{\r\n\t\t\tError = e.Message;\r\n\t\t\tthrow;\r\n\t\t}\r\n\r\n\t\tstatic Task Load(string path)\r\n\t\t{\r\n\t\t\tif (resourceLoads.TryGetValue(path, out var pending) \u0026\u0026 pending is not null \u0026\u0026 !pending.IsFaulted \u0026\u0026 !pending.IsCanceled)\r\n\t\t\t\treturn pending;\r\n\t\t\treturn resourceLoads[path] = LoadResource(path);\r\n\t\t}\r\n\r\n\t\tstatic async Task LoadResource(string path)\r\n\t\t{\r\n\t\t\tClothing clothing = null;\r\n\t\t\tfor (var attempt = 0; attempt \u003C 3; attempt\u002B\u002B)\r\n\t\t\t{\r\n\t\t\t\tclothing = await ClothingResources.Load(path);\r\n\t\t\t\tif (clothing is not null \u0026\u0026 !clothing.IsError)\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tif (attempt \u003C 2)\r\n\t\t\t\t\tawait Task.Delay(150);\r\n\t\t\t}\r\n\r\n\t\t\tif (clothing is null || clothing.IsError)\r\n\t\t\t\tthrow new InvalidOperationException(\u0022A native appearance resource is unavailable: \u0022 \u002B path);\r\n\t\t\tforeach (var modelPath in new[]\r\n\t\t\t{\r\n\t\t\t\tclothing.HumanSkinModel,\r\n\t\t\t\tclothing.HumanAltModel,\r\n\t\t\t\tclothing.HumanAltFemaleModel\r\n\t\t\t}.Where(x =\u003E !string.IsNullOrEmpty(x)).Distinct(StringComparer.OrdinalIgnoreCase))\r\n\t\t\t{\r\n\t\t\t\tvar model = await Model.LoadAsync(modelPath);\r\n\t\t\t\tif (model is null || model.IsError)\r\n\t\t\t\t\tthrow new InvalidOperationException(\u0022A native appearance model is unavailable: \u0022 \u002B modelPath);\r\n\t\t\t}\r\n\r\n\t\t\tRevision\u002B\u002B;\r\n\t\t}\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/CharacterBaseShape.cs","FileName":"CharacterBaseShape.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003EVersion 2 identity shape, independent of skin. Weights are in human01..human07 order.\u003C/summary\u003E\r\npublic sealed class CharacterBaseShape\r\n{\r\n\tpublic float[] HeadWeights { get; set; } = new[]\r\n\t{\r\n\t\t0f,\r\n\t\t0f,\r\n\t\t1f,\r\n\t\t0f,\r\n\t\t0f,\r\n\t\t0f,\r\n\t\t0f\r\n\t};\r\n\tpublic List\u003CPhotoFace\u003E PhotoFaces { get; set; } = new();\r\n\tpublic Dictionary\u003Cstring, FaceRegionBlend\u003E Regions { get; set; } = new();\r\n\r\n\t[System.Text.Json.Serialization.JsonIgnore]\r\n\tpublic float TotalInfluence =\u003E HeadWeights.Sum() \u002B (PhotoFaces?.Sum(x =\u003E x.Influence) ?? 0);\r\n\r\n\tpublic float NativeShare(int i) =\u003E TotalInfluence \u003E 0 ? HeadWeights[i] / TotalInfluence : (i == 2 ? 1 : 0);\r\n\tpublic float PhotoShare(PhotoFace face) =\u003E TotalInfluence \u003E 0 ? face.Influence / TotalInfluence : 0;\r\n\tpublic float MaleBody { get; set; } = 1;\r\n\tpublic string ClothingVariant { get; set; }\r\n\tpublic float BreastSize { get; set; } = .5f;\r\n\tpublic float BulgeSize { get; set; } = .5f;\r\n\r\n\t[System.Text.Json.Serialization.JsonIgnore]\r\n\tpublic bool Valid =\u003E HeadWeights?.Length == 7 \u0026\u0026 HeadWeights.All(Unit) \u0026\u0026 PhotoFaces is not null \u0026\u0026 PhotoFaces.Count \u003C= 4 \u0026\u0026 PhotoFaces.All(x =\u003E x?.Valid == true) \u0026\u0026 PhotoFaces.Select(x =\u003E x.Id).Distinct().Count() == PhotoFaces.Count \u0026\u0026 Regions is not null \u0026\u0026 Regions.Count \u003C= FaceRegions.Ids.Length \u0026\u0026 Regions.All(x =\u003E FaceRegions.Ids.Contains(x.Key) \u0026\u0026 x.Value?.Valid(PhotoFaces) == true) \u0026\u0026 Unit(MaleBody) \u0026\u0026 Unit(BreastSize) \u0026\u0026 Unit(BulgeSize) \u0026\u0026 ClothingVariant is null or \u0022male\u0022 or \u0022female\u0022;\r\n\r\n\tstatic bool Unit(float x) =\u003E float.IsFinite(x) \u0026\u0026 x \u003E= 0 \u0026\u0026 x \u003C= 1;\r\n\tpublic float[] Normalized()\r\n\t{\r\n\t\tif (!Valid)\r\n\t\t\treturn new[]\r\n\t\t\t{\r\n\t\t\t\t0f,\r\n\t\t\t\t0f,\r\n\t\t\t\t1f,\r\n\t\t\t\t0f,\r\n\t\t\t\t0f,\r\n\t\t\t\t0f,\r\n\t\t\t\t0f\r\n\t\t\t};\r\n\t\tfloat sum = TotalInfluence;\r\n\t\treturn sum \u003E 0 ? HeadWeights.Select((x, i) =\u003E (x \u002B PhotoFaces.Sum(f =\u003E f.Influence * f.HeadWeights[i])) / sum).ToArray() : new[]\r\n\t\t{\r\n\t\t\t0f,\r\n\t\t\t0f,\r\n\t\t\t1f,\r\n\t\t\t0f,\r\n\t\t\t0f,\r\n\t\t\t0f,\r\n\t\t\t0f\r\n\t\t};\r\n\t}\r\n\r\n\tpublic CharacterBaseShape DeepCopy() =\u003E new()\r\n\t{\r\n\t\tHeadWeights = HeadWeights?.ToArray(),\r\n\t\tPhotoFaces = PhotoFaces?.Select(x =\u003E x?.DeepCopy()).ToList(),\r\n\t\tRegions = Regions?.ToDictionary(x =\u003E x.Key, x =\u003E x.Value.DeepCopy()),\r\n\t\tMaleBody = MaleBody,\r\n\t\tBreastSize = BreastSize,\r\n\t\tBulgeSize = BulgeSize,\r\n\t\tClothingVariant = ClothingVariant\r\n\t};\r\n}\r\n\r\npublic static class UnifiedHuman\r\n{\r\n\tpublic const string ModelPath = \u0022models/characterkit/unified_human.vmdl\u0022;\r\n\tpublic static void Enable(CharacterAppearanceData data)\r\n\t{\r\n\t\tif (data.BaseShape is not null)\r\n\t\t\treturn;\r\n\t\tbool female = AppearanceCatalog.IsFemale(data);\r\n\t\tvar index = AppearanceCatalog.Skins.ToList().FindIndex(s =\u003E s.ResourcePath == data.SkinPath);\r\n\t\tdata.BaseShape = new()\r\n\t\t{\r\n\t\t\tMaleBody = female ? 0 : 1,\r\n\t\t\tHeadWeights = new float[7],\r\n\t\t\tBreastSize = female ? data.BodyFeatureSize : .5f,\r\n\t\t\tBulgeSize = female ? .5f : data.BodyFeatureSize\r\n\t\t};\r\n\t\tdata.BaseShape.HeadWeights[Math.Max(0, index)] = 1;\r\n\t\tdata.Version = 2;\r\n\t}\r\n\r\n\tpublic static float MaleAmount(CharacterAppearanceData a) =\u003E a.BaseShape?.MaleBody ?? (AppearanceCatalog.IsFemale(a) ? 0 : 1);\r\n\tpublic static string PresentationSkin(CharacterAppearanceData a) =\u003E a.BaseShape is null ? a.SkinPath : (MaleAmount(a) \u003E= .5f ? \u0022models/citizen_clothes/body/human03.clothing\u0022 : \u0022models/citizen_clothes/body/human04.clothing\u0022);\r\n\tpublic static void Apply(SkinnedModelRenderer body, CharacterAppearanceData a)\r\n\t{\r\n\t\tif (a.BaseShape is null)\r\n\t\t\treturn;\r\n\t\tvar weights = FaceRegions.ReferenceWeights(a.BaseShape);\r\n\t\tfor (int i = 0; i \u003C 7; i\u002B\u002B)\r\n\t\t\tif (i != 2)\r\n\t\t\t\tbody.Morphs.Set($\u0022ce_human{i \u002B 1:00}\u0022, weights[i]);\r\n\t\tFaceRegions.Apply(body, a.BaseShape, weights);\r\n\t\tbody.Morphs.Set(\u0022ce_body_female\u0022, 1 - a.BaseShape.MaleBody);\r\n\t}\r\n\r\n\tpublic static string HeadMaterial(string skin)\r\n\t{\r\n\t\tvar id = System.IO.Path.GetFileNameWithoutExtension(skin);\r\n\t\tvar suffix = id switch\r\n\t\t{\r\n\t\t\t\u0022human01\u0022 =\u003E \u0022malehead01\u0022,\r\n\t\t\t\u0022human02\u0022 =\u003E \u0022femalehead01\u0022,\r\n\t\t\t\u0022human03\u0022 =\u003E \u0022malehead02\u0022,\r\n\t\t\t\u0022human04\u0022 =\u003E \u0022femalehead02\u0022,\r\n\t\t\t\u0022human05\u0022 =\u003E \u0022malehead03\u0022,\r\n\t\t\t\u0022human06\u0022 =\u003E \u0022malehead04\u0022,\r\n\t\t\t_ =\u003E \u0022malehead05\u0022\r\n\t\t};\r\n\t\treturn $\u0022models/citizen_human/heads/citizen_human_{suffix}/citizen_human_{suffix}.vmat\u0022;\r\n\t}\r\n\r\n\tpublic static Material SkinMaterial(Material slot, string skin, bool unifiedBody = true)\r\n\t{\r\n\t\tvar original = HumanMarkingModels.NativeMaterial(slot);\r\n\t\tif (original.Name.Contains(\u0022/heads/\u0022) \u0026\u0026 original.ShaderName == \u0022shaders/skin.shader\u0022 \u0026\u0026 !original.Name.EndsWith(\u0022/mouth.vmat\u0022))\r\n\t\t\treturn Material.Load(HeadMaterial(skin));\r\n\t\tif (original.Name.Contains(\u0022/bodies/\u0022) \u0026\u0026 original.ShaderName == \u0022shaders/skin.shader\u0022)\r\n\t\t{\r\n\t\t\t// The unified surface uses one body material family. Female chest normal maps\r\n\t\t\t// encode breast contours that become distorted on the flattened male endpoint.\r\n\t\t\tvar id = System.IO.Path.GetFileNameWithoutExtension(skin);\r\n\t\t\tbool female = !unifiedBody \u0026\u0026 (id is \u0022human02\u0022 or \u0022human04\u0022), dark = id is \u0022human01\u0022 or \u0022human02\u0022 or \u0022human06\u0022;\r\n\t\t\treturn Material.Load($\u0022models/citizen_human/bodies/{(female ? \u0022female\u0022 : \u0022male\u0022)}/{(female ? (dark ? \u0022female_body_mode_dark\u0022 : \u0022female_body_mode_light\u0022) : (dark ? \u0022body_mode_dark\u0022 : \u0022body_male_light\u0022))}.vmat\u0022);\r\n\t\t}\r\n\r\n\t\treturn original;\r\n\t}\r\n\r\n\tpublic static Color BodyToneTint(Material material, string skin)\r\n\t{\r\n\t\tif (!material.Name.Contains(\u0022/bodies/\u0022))\r\n\t\t\treturn Color.White;\r\n\t\treturn BodyToneTint(skin);\r\n\t}\r\n\r\n\tpublic static Color BodyToneTint(string skin)\r\n\t{\r\n\t\t// Albedo ratios of the native female/male materials sampled just inside\r\n\t\t// the shared neck boundary. Keep the female faces\u0027 authored complexion.\r\n\t\treturn System.IO.Path.GetFileNameWithoutExtension(skin) switch\r\n\t\t{\r\n\t\t\t\u0022human02\u0022 =\u003E new Color(1.0192f, .9194f, .8924f),\r\n\t\t\t\u0022human04\u0022 =\u003E new Color(1.0856f, 1.0137f, .8969f),\r\n\t\t\t_ =\u003E Color.White\r\n\t\t};\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/CharacterMarkingData.cs","FileName":"CharacterMarkingData.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\nusing System.Text.Json.Serialization;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003EOne independently editable ink layer. Placement is normalized within a curated surface region.\u003C/summary\u003E\r\npublic sealed class CharacterMarkingData\r\n{\r\n\tpublic string Style { get; set; } = \u0022crescent\u0022;\r\n\tpublic string Region { get; set; } = \u0022face\u0022;\r\n\tpublic float Horizontal { get; set; } = .5f;\r\n\tpublic float Vertical { get; set; } = .5f;\r\n\tpublic float Scale { get; set; } = .5f;\r\n\tpublic float Rotation { get; set; } = .5f;\r\n\tpublic float Spacing { get; set; } = .5f;\r\n\tpublic MakeupLayerData Ink { get; set; } = new()\r\n\t{\r\n\t\tRed = .1f,\r\n\t\tGreen = .12f,\r\n\t\tBlue = .16f,\r\n\t\tAmount = .8f\r\n\t};\r\n\r\n\t[JsonIgnore]\r\n\tpublic bool Valid =\u003E MarkingCatalog.ValidPlacement(Style, Region) \u0026\u0026 Ink?.Valid == true \u0026\u0026 new[]\r\n\t{\r\n\t\tHorizontal,\r\n\t\tVertical,\r\n\t\tScale,\r\n\t\tRotation,\r\n\t\tSpacing\r\n\t}.All(x =\u003E float.IsFinite(x) \u0026\u0026 x \u003E= 0 \u0026\u0026 x \u003C= 1) \u0026\u0026 (Style != \u0022barbed-band\u0022 || (Horizontal == .5f \u0026\u0026 Rotation == .5f));\r\n\r\n\tpublic CharacterMarkingData DeepCopy() =\u003E new()\r\n\t{\r\n\t\tStyle = Style,\r\n\t\tRegion = Region,\r\n\t\tHorizontal = Horizontal,\r\n\t\tVertical = Vertical,\r\n\t\tScale = Scale,\r\n\t\tRotation = Rotation,\r\n\t\tSpacing = Spacing,\r\n\t\tInk = Ink?.DeepCopy()\r\n\t};\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/PhotoProfileAnchors.cs","FileName":"PhotoProfileAnchors.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"namespace CharacterEditor;\n// Generated by ce_calibrate_photo_profile from native topology and packed engine deformers.\npublic static class PhotoProfileAnchors\n{\n public static readonly int[] Ids={10,168,4,1,13,14,152};\n public static readonly string[] Controls={\u0022head\u0022,\u0022jaw\u0022,\u0022eyes\u0022,\u0022nosewidth\u0022,\u0022nostrils\u0022,\u0022noseheight\u0022,\u0022mouthwidth\u0022,\u0022nose\u0022,\u0022chin\u0022,\u0022mouthprojection\u0022,\u0022chinheight\u0022};\n public static readonly Vector3[][] Heads={\nnew Vector3[]{new(3.6992545f,-0.066790596f,68.14279f),new(5.2946215f,0.11305821f,64.60188f),new(5.959813f,-0.041913833f,63.655262f),new(6.1343274f,-0.058205567f,63.346245f),new(5.8273835f,0.07009718f,61.76828f),new(5.7244573f,0.09594756f,61.383133f),new(4.6132507f,-0.040823318f,59.63866f)},\nnew Vector3[]{new(3.6878302f,-0.014352961f,68.02986f),new(5.085779f,0.0712296f,64.49504f),new(5.841306f,-0.03587849f,63.486412f),new(6.0319343f,-0.045126636f,63.211014f),new(5.53363f,0.09146978f,61.719315f),new(5.485255f,0.1368592f,61.244686f),new(4.676547f,-0.012164734f,59.841347f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.2011194f,0.07334158f,64.609566f),new(5.979442f,-0.033484418f,63.572296f),new(6.1241097f,-0.03964384f,63.281433f),new(5.778057f,0.053285245f,61.74f),new(5.719415f,0.069648676f,61.50413f),new(4.7025223f,-0.06262799f,59.70981f)},\nnew Vector3[]{new(3.5149546f,-0.009116913f,68.08719f),new(5.0824428f,0.056152936f,64.4079f),new(5.857973f,-0.08385171f,63.312653f),new(6.0649567f,-0.10289183f,62.99997f),new(5.4969316f,0.05211438f,61.666157f),new(5.502199f,0.10385137f,61.29811f),new(4.67235f,-0.1021266f,59.774113f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.2132f,0.087325275f,64.6164f),new(5.957733f,0.064072326f,63.39234f),new(6.137288f,0.064377435f,63.1475f),new(5.6911297f,0.052945673f,61.71075f),new(5.713699f,0.06934876f,61.489025f),new(4.702329f,-0.055987295f,59.563744f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.2012277f,0.1540686f,64.59866f),new(6.0016494f,-0.08192249f,63.62896f),new(6.165094f,-0.049669266f,63.36159f),new(5.7699647f,0.05389818f,61.78059f),new(5.719415f,0.06964957f,61.493782f),new(4.700662f,-0.0723621f,59.84928f)},\nnew Vector3[]{new(3.699734f,-0.0662729f,68.142685f),new(5.173374f,0.09990916f,64.60435f),new(5.9552946f,0.042667035f,63.536186f),new(6.1126857f,0.03899601f,63.296906f),new(5.6911297f,0.0529435f,61.727646f),new(5.713699f,0.07963325f,61.420948f),new(4.702329f,-0.05598729f,59.74437f)}\n };\n // [head * Controls.Length * 2 \u002B control * 2 \u002B positiveEndpoint]\n public static readonly Vector3[][] Deltas={\nnew Vector3[]{new(-0.13209844f,0.0031800792f,-0.07652283f),new(-0.24389982f,-0.0069441274f,-0.03074646f),new(-0.23990822f,0.0022496209f,-0.010990143f),new(-0.23487473f,0.0029714853f,-0.0055236816f),new(-0.23757076f,-0.003810294f,0.020923615f),new(-0.23392248f,-0.0052277073f,0.02753067f),new(-0.2026701f,0.0023912787f,0.061531067f)},\nnew Vector3[]{new(0.13209844f,-0.0031800792f,0.07652283f),new(0.24389982f,0.0069441274f,0.03074646f),new(0.23990822f,-0.0022496209f,0.010990143f),new(0.23487473f,-0.0029714853f,0.0055236816f),new(0.23757076f,0.003810294f,-0.020923615f),new(0.23392248f,0.0052277073f,-0.02753067f),new(0.2026701f,-0.0023912787f,-0.061531067f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.011735871f,0f),new(0f,0.016297556f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.011735871f,0f),new(0f,-0.016297556f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.3449974f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.0107391365f,0f),new(0f,-0.0056006014f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.010739133f,0f),new(0f,0.0056006014f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.041153908f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.0532794f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.13788319f,0f,0f),new(-0.052534103f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.19303656f,0f,0f),new(0.07354784f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.47891617f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.31927872f)},\nnew Vector3[]{new(-0.1366136f,0.0007089311f,-0.07763672f),new(-0.24782324f,-0.004627906f,-0.03035736f),new(-0.2453003f,0.002008915f,-0.00851059f),new(-0.23964739f,0.0023905002f,-0.0034942627f),new(-0.24498606f,-0.0053994358f,0.02362442f),new(-0.23736048f,-0.007896334f,0.031650543f),new(-0.20918512f,0.0007255161f,0.05886841f)},\nnew Vector3[]{new(0.1366136f,-0.0007089311f,0.07763672f),new(0.24782324f,0.004627906f,0.03035736f),new(0.2453003f,-0.002008915f,0.00851059f),new(0.23964739f,-0.0023905002f,0.0034942627f),new(0.24498606f,0.0053994358f,-0.02362442f),new(0.23736048f,0.007896334f,-0.031650543f),new(0.20918512f,-0.0007255161f,-0.05886841f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.010045975f,0f),new(0f,0.012437068f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.010045975f,0f),new(0f,-0.012437068f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.33958435f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.33958435f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.018210128f,0f),new(0f,-0.0033276677f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.018210128f,0f),new(0f,0.0033276677f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4882121f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4882121f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.2155137f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.27901363f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.17917538f,0f,0f),new(-0.02188301f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.25084543f,0f,0f),new(0.03063631f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5400009f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.3600006f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.2449646f,-0.004605703f,-0.031585693f),new(-0.2398367f,0.001790762f,-0.009563446f),new(-0.23560143f,0.0020335242f,-0.004512787f),new(-0.2387433f,-0.002935592f,0.021694183f),new(-0.23645878f,-0.0038393363f,0.02576828f),new(-0.2047453f,0.0036357194f,0.059688568f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.2449646f,0.004605703f,0.031585693f),new(0.2398367f,-0.001790762f,0.009563446f),new(0.23560143f,-0.0020335242f,0.004512787f),new(0.2387433f,0.002935592f,-0.021694183f),new(0.23645878f,0.0038393363f,-0.02576828f),new(0.2047453f,-0.0036357194f,-0.059688568f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009375636f,0f),new(0f,0.011100274f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009375636f,0f),new(0f,-0.011100274f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.008732874f,0f),new(0f,-0.008089323f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.008732874f,0f),new(0f,0.008089326f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.1130929f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.14641476f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.14750004f,0f,0f),new(-0.104530334f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.20650053f,0f,0f),new(0.14634228f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5328598f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.35523987f)},\nnew Vector3[]{new(-0.13274455f,0.0004590759f,-0.08004761f),new(-0.24919367f,-0.0036709309f,-0.028762817f),new(-0.24560404f,0.004687473f,-0.0054626465f),new(-0.238554f,0.0053960904f,0f),new(-0.24498224f,-0.0030967854f,0.02476883f),new(-0.23813248f,-0.0059928596f,0.03068924f),new(-0.20696688f,0.006031759f,0.059539795f)},\nnew Vector3[]{new(0.13274455f,-0.0004590759f,0.08004761f),new(0.24919367f,0.0036709309f,0.028762817f),new(0.24560404f,-0.004687473f,0.0054626465f),new(0.238554f,-0.0053960904f,0f),new(0.24498224f,0.0030967854f,-0.02476883f),new(0.23813248f,0.0059928596f,-0.03068924f),new(0.20696688f,-0.006031759f,-0.059539795f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,-5.4202974E-05f,0f),new(0f,0.02339752f,0f),new(0f,0.020724386f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,5.4202974E-05f,0f),new(0f,-0.02339752f,0f),new(0f,-0.020724379f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,-0.0011901855f),new(0f,0f,-0.34381104f),new(0f,0f,-0.24817657f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0.0011901855f),new(0f,0f,0.34381104f),new(0f,0f,0.24817657f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.01034468f,0f),new(0f,-0.0050033107f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.01034468f,0f),new(0f,0.0050033107f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(-0.001709938f,0f,0f),new(-0.49428988f,0f,0f),new(-0.3567996f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0.001709938f,0f,0f),new(0.49428988f,0f,0f),new(0.3567996f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.15790367f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.20442915f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.17864943f,0f,0f),new(-0.043359756f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.2501092f,0f,0f),new(0.060703754f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.53889465f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.35926437f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.24469805f,-0.0054651946f,-0.03161621f),new(-0.24176359f,-0.0034667253f,-0.0066337585f),new(-0.23539543f,-0.0032922663f,-0.002357483f),new(-0.24088907f,-0.0029880553f,0.02273941f),new(-0.23634768f,-0.0038248226f,0.026042938f),new(-0.19970894f,0.0031703971f,0.060806274f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.24469805f,0.0054651946f,0.03161621f),new(0.24176359f,0.0034667253f,0.0066337585f),new(0.23539543f,0.0032922626f,0.002357483f),new(0.24088907f,0.0029880553f,-0.02273941f),new(0.23634768f,0.0038248226f,-0.026042938f),new(0.19970894f,-0.0031703971f,-0.060806274f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.017940246f,0f),new(0f,-0.017056447f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.017940246f,0f),new(0f,0.017056443f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.32645035f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.32645035f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009510666f,0f),new(0f,-0.0076843873f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009510666f,0f),new(0f,0.0076843947f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4693303f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4693303f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.015622139f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.020225048f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.16166735f,0f,0f),new(-0.099727154f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.22633457f,0f,0f),new(0.13961792f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.4568138f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.30454254f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.2450552f,-0.009678587f,-0.03138733f),new(-0.23850155f,0.004340723f,-0.010414124f),new(-0.23345089f,0.0025077388f,-0.005706787f),new(-0.2396326f,-0.002984602f,0.021102905f),new(-0.2362647f,-0.003836237f,0.025924683f),new(-0.2093401f,0.004296787f,0.05846405f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.2450552f,0.009678587f,0.03138733f),new(0.23850155f,-0.004340723f,0.010414124f),new(0.23345089f,-0.0025077388f,0.005706787f),new(0.2396326f,0.002984602f,-0.021102905f),new(0.2362647f,0.003836237f,-0.025924683f),new(0.2093401f,-0.004296787f,-0.05846405f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.022938296f,0f),new(0f,0.013907395f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.022938296f,0f),new(0f,-0.013907395f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009057671f,0f),new(0f,-0.0077751838f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009057675f,0f),new(0f,0.00777518f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.22866583f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.29604006f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.15124607f,0f,0f),new(-0.10046959f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.21174479f,0f,0f),new(0.14065742f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5400009f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.3600006f)},\nnew Vector3[]{new(-0.13210607f,0.0031552091f,-0.0765152f),new(-0.24530983f,-0.006316647f,-0.031700134f),new(-0.24099922f,-0.002302207f,-0.0090408325f),new(-0.23603249f,-0.0020077005f,-0.004776001f),new(-0.24115801f,-0.0029912703f,0.022464752f),new(-0.23502398f,-0.004367456f,0.027061462f),new(-0.2059083f,0.0032688156f,0.05939865f)},\nnew Vector3[]{new(0.13210607f,-0.0031552091f,0.0765152f),new(0.24530983f,0.006316647f,0.031700134f),new(0.24099922f,0.002302207f,0.0090408325f),new(0.23603249f,0.0020077005f,0.004776001f),new(0.24115801f,0.0029912703f,-0.022464752f),new(0.23502398f,0.004367456f,-0.027061462f),new(0.2059083f,-0.0032688156f,-0.05939865f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.011946769f,0f),new(0f,-0.010918882f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0.011946768f,0f),new(0f,0.0109188855f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.34500122f),new(0f,0f,-0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.34500122f),new(0f,0f,0.34500122f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,-0.009599518f,0f),new(0f,-0.0062645525f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0.009599518f,0f),new(0f,0.0062645525f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(-0.4959998f,0f,0f),new(-0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0.4959998f,0f,0f),new(0.4959998f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.14241362f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.18437433f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(-0.16318464f,0f,0f),new(-0.07080078f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0.2284584f,0f,0f),new(0.099121094f,0f,0f),new(0f,0f,0f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,-0.5386009f)},\nnew Vector3[]{new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0f),new(0f,0f,0.359066f)}\n };\n}\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/WardrobeCatalog.Generated.cs","FileName":"WardrobeCatalog.Generated.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"namespace CharacterEditor;\r\npublic static partial class WardrobeCatalog\r\n{\r\n public static IReadOnlyList\u003CWardrobeItem\u003E Items=\u003Eentries??=CreateEntries();\r\n static WardrobeItem[] entries;\r\n static WardrobeItem[] CreateEntries()=\u003Enew WardrobeItem[]{\r\n  new(\u0022Denim Skirt Black\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/dress/denim_skirt/denim_skirt_black.clothing\u0022,\u0022LeftThigh, RightThigh, LeftKnee, RightKnee\u0022,\u0022\u0022,\u0022\u0022,\u0022Skirt\u0022),\r\n  new(\u0022Denim Skirt Blue\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/dress/denim_skirt/denim_skirt_blue.clothing\u0022,\u0022LeftThigh, RightThigh, LeftKnee, RightKnee\u0022,\u0022\u0022,\u0022\u0022,\u0022Skirt\u0022),\r\n  new(\u0022Denim Skirt Green\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/dress/denim_skirt/denim_skirt_green.clothing\u0022,\u0022LeftThigh, RightThigh, LeftKnee, RightKnee\u0022,\u0022\u0022,\u0022\u0022,\u0022Skirt\u0022),\r\n  new(\u0022Denim Skirt Leapard\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/dress/denim_skirt/denim_skirt_leapard.clothing\u0022,\u0022LeftThigh, RightThigh, LeftKnee, RightKnee\u0022,\u0022\u0022,\u0022\u0022,\u0022Skirt\u0022),\r\n  new(\u0022Dotted Dress\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/dress/dotted_dress/dotted_dress.clothing\u0022,\u0022Chest, Groin, LeftThigh, RightThigh\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Dress\u0022),\r\n  new(\u0022Grand Ball Gown\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/dress/posh_dress/models/posh_dress.clothing\u0022,\u0022Chest, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Chest, Legs\u0022,\u0022Dress\u0022),\r\n  new(\u0022Simple Dress\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/dress/simple_dress/simple_dress.clothing\u0022,\u0022Chest, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Dress\u0022),\r\n  new(\u0022Simple Dress Black\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/dress/simple_dress/simple_dress_black.clothing\u0022,\u0022Chest, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Dress\u0022),\r\n  new(\u0022Simple Dress Blue\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/dress/simple_dress/simple_dress_blue.clothing\u0022,\u0022Chest, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Dress\u0022),\r\n  new(\u0022Standard Skirt\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/dress/standard_skirt/standard_skirt.clothing\u0022,\u0022LeftThigh, RightThigh, LeftKnee, RightKnee\u0022,\u0022\u0022,\u0022\u0022,\u0022Skirt\u0022),\r\n  new(\u0022Pirate Outfit\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/full_outfit/pirate_outfit/pirate_outfit.clothing\u0022,\u0022Chest, LeftArm, RightArm, LeftWrist, RightWrist, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot\u0022,\u0022Chest, LeftArm, RightArm, LeftWrist, RightWrist, LeftHand, RightHand, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot, Waist\u0022,\u0022Chest, Legs, Feet\u0022,\u0022Costume\u0022),\r\n  new(\u0022Nerdy Glasses\u0022,\u0022Glasses\u0022,\u0022Glasses\u0022,\u0022models/citizen_clothes/glasses/nerdy_glasses/nerdy_glasses.clothing\u0022,\u0022Glasses\u0022,\u0022\u0022,\u0022\u0022,\u0022GlassesEye\u0022),\r\n  new(\u0022Party Glasses\u0022,\u0022Glasses\u0022,\u0022Glasses\u0022,\u0022models/citizen_clothes/glasses/party_glasses/party_glasses.clothing\u0022,\u0022Glasses\u0022,\u0022\u0022,\u0022\u0022,\u0022GlassesSun\u0022),\r\n  new(\u0022Boxing Gloves\u0022,\u0022Gloves\u0022,\u0022Gloves\u0022,\u0022models/citizen_clothes/gloves/boxing_gloves/boxing_gloves.clothing\u0022,\u0022LeftHand, RightHand\u0022,\u0022\u0022,\u0022Hands\u0022,\u0022Gloves\u0022),\r\n  new(\u0022Boxing Gloves Blue\u0022,\u0022Gloves\u0022,\u0022Gloves\u0022,\u0022models/citizen_clothes/gloves/boxing_gloves/boxing_gloves_blue.clothing\u0022,\u0022LeftHand, RightHand\u0022,\u0022\u0022,\u0022Hands\u0022,\u0022Gloves\u0022),\r\n  new(\u0022Boxing Gloves Red\u0022,\u0022Gloves\u0022,\u0022Gloves\u0022,\u0022models/citizen_clothes/gloves/boxing_gloves/boxing_gloves_red.clothing\u0022,\u0022LeftHand, RightHand\u0022,\u0022\u0022,\u0022Hands\u0022,\u0022Gloves\u0022),\r\n  new(\u0022Fingerless Gloves\u0022,\u0022Gloves\u0022,\u0022Gloves\u0022,\u0022models/citizen_clothes/gloves/fingerless_gloves/fingerless_gloves.clothing\u0022,\u0022LeftHand, RightHand\u0022,\u0022\u0022,\u0022Hands\u0022,\u0022Gloves\u0022),\r\n  new(\u0022Goalie Gloves\u0022,\u0022Gloves\u0022,\u0022Gloves\u0022,\u0022models/citizen_clothes/gloves/goalie_gloves/goalie_gloves.clothing\u0022,\u0022LeftHand, RightHand\u0022,\u0022\u0022,\u0022Hands\u0022,\u0022Gloves\u0022),\r\n  new(\u0022Blue Rubber Gloves\u0022,\u0022Gloves\u0022,\u0022Gloves\u0022,\u0022models/citizen_clothes/gloves/rubber_gloves/rubber_gloves.clothing\u0022,\u0022LeftHand, RightHand\u0022,\u0022\u0022,\u0022Hands\u0022,\u0022Gloves\u0022),\r\n  new(\u0022Black Rubber Gloves\u0022,\u0022Gloves\u0022,\u0022Gloves\u0022,\u0022models/citizen_clothes/gloves/rubber_gloves/rubber_gloves_black.clothing\u0022,\u0022LeftHand, RightHand\u0022,\u0022\u0022,\u0022Hands\u0022,\u0022Gloves\u0022),\r\n  new(\u0022Mask\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/balaclava/balaclava.clothing\u0022,\u0022HeadTop, HeadBottom\u0022,\u0022\u0022,\u0022\u0022,\u0022HatBeanie\u0022),\r\n  new(\u0022Bandana Mask\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/bandana_mask/bandana_mask.clothing\u0022,\u0022\u0022,\u0022\u0022,\u0022\u0022,\u0022HeadSpecial\u0022),\r\n  new(\u0022Baseball Cap\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/baseball_cap/baseball_cap.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022Hat\u0022),\r\n  new(\u0022Baseball Cap Black\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/baseball_cap/baseball_cap_black.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022Hat\u0022),\r\n  new(\u0022Baseball Cap Blue White\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/baseball_cap/baseball_cap_blue_white.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022Hat\u0022),\r\n  new(\u0022Baseball Cap Brown\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/baseball_cap/baseball_cap_brown.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022Hat\u0022),\r\n  new(\u0022Beanie\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/beanie/beanie.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatBeanie\u0022),\r\n  new(\u0022Beanie Green\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/beanie/beanie_green.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatBeanie\u0022),\r\n  new(\u0022Beanie Red\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/beanie/beanie_red.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatBeanie\u0022),\r\n  new(\u0022Boxing Headgear\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/boxing_headgear/boxing_headgear.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Boxing Headgear Blue\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/boxing_headgear/boxing_headgear_blue.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Boxing Headgear Red\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/boxing_headgear/boxing_headgear_red.clothing\u0022,\u0022HeadTop, HeadBottom\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Bucket Hat\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/bucket_hat/bucket_hat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022Hat\u0022),\r\n  new(\u0022Bucket Hat Black\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/bucket_hat/bucket_hat_black.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022Hat\u0022),\r\n  new(\u0022Chef\u0027s Hat\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/chefs_hat/chefs_hat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Cowboy Hat\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/cowboy_hat/cowboy_hat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Fedora\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/fedora/fedora.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatFormal\u0022),\r\n  new(\u0022Fedora Brown\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/fedora/fedora_brown.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatFormal\u0022),\r\n  new(\u0022Flatcap\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/flatcap/flatcap.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022Hat\u0022),\r\n  new(\u0022Gasmask\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/gasmask/gasmask.clothing\u0022,\u0022HeadBottom\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Hair Net\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/hair_net/hair_net.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Hair Net White\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/hair_net/hair_net_white.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Hard Hat\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/hard_hat/hard_hat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Headphones\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/headphones/models/headphones.clothing\u0022,\u0022\u0022,\u0022\u0022,\u0022\u0022,\u0022HeadTech\u0022),\r\n  new(\u0022Police Hat\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/police_hat/police_hat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Racer Helmet\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/racer_helmet/racer_helmet.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Scifi Trooper Helmet\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/scifi_trooper_helmet/scifi_trooper_helmet.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Skater Helmet\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/skater_helmet/skater_helmet.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Skater Helmet Camo\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/skater_helmet/skater_helmet_camo.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Skater Helmet White\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/skater_helmet/skater_helmet_white.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Tactical Helmet\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/tactical_helmet/tactical_helmet.clothing\u0022,\u0022HeadTop, Glasses\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Army Helmet\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/tactical_helmet/tactical_helmet_army.clothing\u0022,\u0022HeadTop, Glasses\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Trapper Hat\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/trapper_hat/trapper_hat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatBeanie\u0022),\r\n  new(\u0022Trapper Hat Cloth\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/trapper_hat/trapper_hat_cloth.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatBeanie\u0022),\r\n  new(\u0022War Helmet\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/hat/war_helmet/war_helmet.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatUniform\u0022),\r\n  new(\u0022Biker Jacket\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/biker_jacket/biker_jacket.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Jacket\u0022),\r\n  new(\u0022Bomber Jacket\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/bomber_jacket/bomber_jacket.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Jacket\u0022),\r\n  new(\u0022Black Leather Jacket\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/brown_leather_jacket/black_leather_jacket.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Jacket\u0022),\r\n  new(\u0022Brown Leather Jacket\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/brown_leather_jacket/brown_leather_jacket.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Jacket\u0022),\r\n  new(\u0022Hoodie\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jacket/hoodie/hoodie.clothing\u0022,\u0022Chest\u0022,\u0022LeftArm, RightArm\u0022,\u0022Chest\u0022,\u0022Hoodie\u0022),\r\n  new(\u0022Black Hoodie\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jacket/hoodie/hoodie_black.clothing\u0022,\u0022LeftArm, RightArm, LeftWrist, RightWrist\u0022,\u0022Chest\u0022,\u0022Chest\u0022,\u0022Hoodie\u0022),\r\n  new(\u0022Lab Coat\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/labcoat/labcoat.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Coat\u0022),\r\n  new(\u0022Puffer Jacket\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/puffer_jacket/puffer_jacket.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Jacket\u0022),\r\n  new(\u0022Puffer Jacket Blue\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/puffer_jacket/puffer_jacket_blue.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Jacket\u0022),\r\n  new(\u0022Puffer Jacket Orange\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/puffer_jacket/puffer_jacket_orange.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Jacket\u0022),\r\n  new(\u0022Rain Coat\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/raincoat/raincoat.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Coat\u0022),\r\n  new(\u0022Sleeveless Jack\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/sleeveless_jacket/sleeveless_jacket.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Gilet\u0022),\r\n  new(\u0022Wind Breaker\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/wind_breaker/wind_breaker.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Coat\u0022),\r\n  new(\u0022Winter Coat\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jacket/winter_coat/winter_coat.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Coat\u0022),\r\n  new(\u0022Cardigan\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jumper/cardigan/cardigan.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Cardigan\u0022),\r\n  new(\u0022Cardigan Pink\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jumper/cardigan/cardigan_colored.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Cardigan\u0022),\r\n  new(\u0022Cardigan Dark\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jumper/cardigan/cardigan_dark.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Cardigan\u0022),\r\n  new(\u0022Cardigan Dirty\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/jumper/cardigan/cardigan_dirty.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Cardigan\u0022),\r\n  new(\u0022Loose Jumper\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jumper/loose_jumper/loose_jumper.clothing\u0022,\u0022Chest\u0022,\u0022LeftArm, RightArm\u0022,\u0022Chest\u0022,\u0022Knitwear\u0022),\r\n  new(\u0022Goalie Jumper\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jumper/loose_jumper/loose_jumper_goalie.clothing\u0022,\u0022Chest\u0022,\u0022LeftArm, RightArm\u0022,\u0022Chest\u0022,\u0022Knitwear\u0022),\r\n  new(\u0022Loose Jumper Wooly\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jumper/loose_jumper/loose_jumper_wooly.clothing\u0022,\u0022Chest\u0022,\u0022LeftArm, RightArm\u0022,\u0022Chest\u0022,\u0022Knitwear\u0022),\r\n  new(\u0022Sleeveless Jumper\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jumper/sleeveless_jumper/sleeveless_jumper.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Knitwear\u0022),\r\n  new(\u0022Sleeveless Jumper Grey\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jumper/sleeveless_jumper/sleeveless_jumper_grey.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Knitwear\u0022),\r\n  new(\u0022Sleeveless Jumper Red\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/jumper/sleeveless_jumper/sleeveless_jumper_red.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Knitwear\u0022),\r\n  new(\u0022Pearl Necklace\u0022,\u0022Accessories\u0022,\u0022necklace\u0022,\u0022models/citizen_clothes/necklace/pearl_necklace/pearl_necklace.clothing\u0022,\u0022Chin\u0022,\u0022\u0022,\u0022\u0022,\u0022NecklacePendant\u0022),\r\n  new(\u0022Vintage Necklace\u0022,\u0022Accessories\u0022,\u0022necklace\u0022,\u0022models/citizen_clothes/necklace/vintage_necklace/vintage_necklace.clothing\u0022,\u0022Chin\u0022,\u0022\u0022,\u0022\u0022,\u0022NecklacePendant\u0022),\r\n  new(\u0022Santa Hat 2024\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/seasonal/2024/santa/santahat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Santa Jacket 2024\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/seasonal/2024/santa/santajacket.clothing\u0022,\u0022LeftArm, RightArm\u0022,\u0022LeftArm, RightArm\u0022,\u0022Chest\u0022,\u0022Coat\u0022),\r\n  new(\u0022Santa Trousers 2024\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/seasonal/2024/santa/santatrousers.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot\u0022,\u0022Legs, Feet\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Easter Bonnet\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/seasonal/2025/easter_farmer/easter_bonnet/easter_bonnet.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Easter Dungarees\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/seasonal/2025/easter_farmer/easter_dungarees/models/easter_dungarees.clothing\u0022,\u0022Chest, LeftArm, RightArm, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022Chest, LeftArm, RightArm, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022Chest, Legs\u0022,\u0022Suit\u0022),\r\n  new(\u0022Easter Slippers\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/seasonal/2025/easter_farmer/easter_slippers/models/easter_slippers.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Footwear\u0022),\r\n  new(\u0022Valentines Bear Head 2025\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/seasonal/2025/valentines_bear/bear_head.clothing\u0022,\u0022HeadTop, HeadBottom\u0022,\u0022HeadTop, HeadBottom\u0022,\u0022Head\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Valentines Bear Top 2025\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/seasonal/2025/valentines_bear/bear_top.clothing\u0022,\u0022Chest, LeftArm, RightArm, LeftWrist, RightWrist, LeftHand, RightHand\u0022,\u0022LeftArm, RightArm, LeftWrist, RightWrist, LeftHand, RightHand\u0022,\u0022Chest, Hands\u0022,\u0022Costume\u0022),\r\n  new(\u0022Valentines Bear Trousers 2025\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/seasonal/2025/valentines_bear/bear_trousers.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot\u0022,\u0022Legs, Feet\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Wizard Beard\u0022,\u0022Accessories\u0022,\u0022costume_beard\u0022,\u0022models/citizen_clothes/seasonal/2025/wizard_outfit/wizard_beard/wizard_beard.clothing\u0022,\u0022Face\u0022,\u0022\u0022,\u0022\u0022,\u0022FacialHairBeard\u0022),\r\n  new(\u0022Wizard Gown\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/seasonal/2025/wizard_outfit/wizard_gown/wizard_gown.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022Chest, LeftArm, RightArm, LeftWrist\u0022,\u0022Chest\u0022,\u0022Coat\u0022),\r\n  new(\u0022Wizard Hat\u0022,\u0022Hats\u0022,\u0022Hats\u0022,\u0022models/citizen_clothes/seasonal/2025/wizard_outfit/wizard_hat/wizard_hat.clothing\u0022,\u0022HeadTop\u0022,\u0022\u0022,\u0022\u0022,\u0022HatCostume\u0022),\r\n  new(\u0022Wizard Trousers\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/seasonal/2025/wizard_outfit/wizard_trousers/wizard_trousers.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Legs, Feet\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Army Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/army_shirt/army_shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Chef Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/chef_shirt/chef_shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Fisherman Jumpsuit\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/shirt/fisherman_jumpsuit/fisherman_jumpsuit.clothing\u0022,\u0022Chest, LeftArm, RightArm, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin, LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Chest, Legs, Feet\u0022,\u0022Suit\u0022),\r\n  new(\u0022Flannel Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/flannel_shirt/flannel_shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Blue Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/flannel_shirt/variations/blue_shirt/blue_shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Hawaiian Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/hawaiian_shirt/hawaiian shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Blue Jumpsuit\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/shirt/jumpsuit/blue_jumpsuit.clothing\u0022,\u0022Chest, LeftArm, RightArm, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Chest, Legs\u0022,\u0022Suit\u0022),\r\n  new(\u0022Prison Jumpsuit\u0022,\u0022Outfits\u0022,\u0022Outfits\u0022,\u0022models/citizen_clothes/shirt/jumpsuit/prison_jumpsuit.clothing\u0022,\u0022Chest, LeftArm, RightArm, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Chest, Legs\u0022,\u0022Suit\u0022),\r\n  new(\u0022Leotard\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/leotard/leotard.clothing\u0022,\u0022Chest, LeftArm, RightArm, LeftWrist, RightWrist, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022\u0022,\u0022TShirt\u0022),\r\n  new(\u0022Leotard Blue\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/leotard/leotard_blue.clothing\u0022,\u0022Chest, LeftArm, RightArm, LeftWrist, RightWrist, Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Loose Crop Top\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/loose_crop_top/loose_crop_top.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Loose Crop Top Pattern\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/loose_crop_top/loose_crop_top_pattern.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Loose Crop Top Purple\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/loose_crop_top/loose_crop_top_purple.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Loose Crop Top White\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/loose_crop_top/loose_crop_top_white.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Loose Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/loose_shirt/loose_shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm, LeftWrist, RightWrist\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022TShirt\u0022),\r\n  new(\u0022Polo Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/polo_shirt/polo_shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022TShirt\u0022),\r\n  new(\u0022Polo Shirt White\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/polo_shirt/polo_shirt_white.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022TShirt\u0022),\r\n  new(\u0022Tank Top \u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/tanktop/tanktop.clothing\u0022,\u0022Chest\u0022,\u0022\u0022,\u0022\u0022,\u0022TShirt\u0022),\r\n  new(\u0022Football Tshirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/tshirt/football_tshirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022TShirt\u0022),\r\n  new(\u0022Tshirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/tshirt/tshirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Tops\u0022),\r\n  new(\u0022Waistcoat and Shirt\u0022,\u0022Tops\u0022,\u0022Tops\u0022,\u0022models/citizen_clothes/shirt/waistcoat_and_shirt/waistcoat_and_shirt.clothing\u0022,\u0022Chest, LeftArm, RightArm\u0022,\u0022\u0022,\u0022Chest\u0022,\u0022Shirt\u0022),\r\n  new(\u0022Boot Heels Black\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boot_heels/boot_heels_black.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Heels\u0022),\r\n  new(\u0022Boot Heels Brown\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boot_heels/boot_heels_brown.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Heels\u0022),\r\n  new(\u0022Boot Heels Leopard\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boot_heels/boot_heels_leopard.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Heels\u0022),\r\n  new(\u0022Boot Heels Red\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boot_heels/boot_heels_red.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Heels\u0022),\r\n  new(\u0022Army Boots\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boots/army_boots.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Boots\u0022),\r\n  new(\u0022Black Boots\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boots/black_boots.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Boots\u0022),\r\n  new(\u0022Boxing Boots\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boxing_boots/boxing_boots.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Boots\u0022),\r\n  new(\u0022Boxing Boots Blue\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/boxing_boots/boxing_boots_blue.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Boots\u0022),\r\n  new(\u0022Clogs and Socks\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/crocs_and_socks/clogs_and_socks.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Slippers\u0022),\r\n  new(\u0022Flat Shoes\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/flatshoes/textures/flat_shoes.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022\u0022,\u0022Shoes\u0022),\r\n  new(\u0022Football Shoes\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/football_shoes/football_shoes.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Trainers\u0022),\r\n  new(\u0022Slippers\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/slippers/slippers.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022\u0022,\u0022Slippers\u0022),\r\n  new(\u0022Blue Sneakers\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/sneakers/blue_sneakers.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Shoes\u0022),\r\n  new(\u0022Sneakers\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/sneakers/sneakers.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Shoes\u0022),\r\n  new(\u0022Trainers\u0022,\u0022Shoes\u0022,\u0022Shoes\u0022,\u0022models/citizen_clothes/shoes/trainers/trainers.clothing\u0022,\u0022LeftFoot, RightFoot\u0022,\u0022\u0022,\u0022Feet\u0022,\u0022Trainers\u0022),\r\n  new(\u0022Boxing Shorts\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/shorts/boxing_shorts/boxing_shorts.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Shorts\u0022),\r\n  new(\u0022Boxing Shorts Blue\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/shorts/boxing_shorts/boxing_shorts_blue.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Shorts\u0022),\r\n  new(\u0022Boxing Shorts Red\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/shorts/boxing_shorts/boxing_shorts_red.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Shorts\u0022),\r\n  new(\u0022Football Shorts\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/shorts/football_shorts/football_shorts.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Shorts\u0022),\r\n  new(\u0022Shorts\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/shorts/shorts/shorts.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Shorts\u0022),\r\n  new(\u0022Summer Shorts\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/shorts/summer_shorts/summer shorts.clothing\u0022,\u0022LeftThigh, RightThigh\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Shorts\u0022),\r\n  new(\u0022Bin Man Trousers\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/cargopants/binman_trousers.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Brown Cargo Pants\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/cargopants/brown_cargo_pants.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Army Cargo Pants\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/cargopants/cargo_pants_army.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Jeans\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/jeans/jeans.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Jeans\u0022),\r\n  new(\u0022Black Jeans\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/jeans/jeans_black.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Jeans\u0022),\r\n  new(\u0022Loose Jeans\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/loosejeans/loose_jeans.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Jeans\u0022),\r\n  new(\u0022Loose Trousers\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/loosetrousers/loose_trousers.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Smart Trousers\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/smarttrousers/trousers.smart.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Trackie Bottoms\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/trackiebottoms/trackie_bottoms.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Black Trackie Bottoms\u0022,\u0022Bottoms\u0022,\u0022Bottoms\u0022,\u0022models/citizen_clothes/trousers/trackiebottoms/trackie_bottoms_black.clothing\u0022,\u0022Groin, LeftThigh, RightThigh, LeftKnee, RightKnee, LeftShin, RightShin\u0022,\u0022\u0022,\u0022Legs\u0022,\u0022Trousers\u0022),\r\n  new(\u0022Chef\u0027s Apron\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/vest/chefs_apron/chefs_apron.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Vest\u0022),\r\n  new(\u0022Tactical Vest\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/vest/tactical_vest/models/tactical_vest.clothing\u0022,\u0022\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022Vest\u0022),\r\n  new(\u0022Army Vest\u0022,\u0022Outerwear\u0022,\u0022Outerwear\u0022,\u0022models/citizen_clothes/vest/tactical_vest/models/tactical_vest_army.clothing\u0022,\u0022LeftArm, RightArm\u0022,\u0022\u0022,\u0022\u0022,\u0022Vest\u0022),\r\n  new(\u0022Modern Watch\u0022,\u0022Accessories\u0022,\u0022watch\u0022,\u0022models/citizen_clothes/watch/modern_watch/modern_watch.clothing\u0022,\u0022LeftWrist\u0022,\u0022\u0022,\u0022\u0022,\u0022WristWatch\u0022),\r\n  new(\u0022Black Modern Watch\u0022,\u0022Accessories\u0022,\u0022watch\u0022,\u0022models/citizen_clothes/watch/modern_watch/modern_watch_black.clothing\u0022,\u0022LeftWrist\u0022,\u0022\u0022,\u0022\u0022,\u0022WristWatch\u0022)\r\n };\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"CharacterKit/EyeColorPresentation.cs","FileName":"EyeColorPresentation.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003EChanges only the iris albedo of the curated native human eyes.\u003C/summary\u003E\r\npublic sealed class EyeColorPresentation : IDisposable\r\n{\r\n\tstatic ComputeShader compositor;\r\n\tstatic Material carrier;\r\n\treadonly Dictionary\u003Cint, Material\u003E copies = new();\r\n\treadonly Dictionary\u003Cint, Texture\u003E textures = new();\r\n\tSkinnedModelRenderer target;\r\n\tModel model;\r\n\tstring group;\r\n\tColor? applied;\r\n\tCharacterAppearanceData requested;\r\n\tDateTime started, nextRefresh;\r\n\tpublic static async System.Threading.Tasks.Task EnsureLoadedAsync()\r\n\t{\r\n\t\tcarrier ??= await Material.LoadAsync(\u0022materials/makeup/iris_compositor.vmat\u0022);\r\n\t\tif (carrier?.ShaderName != \u0022shaders/human_iris.shader\u0022 || !carrier.Shader.IsValid || !carrier.Shader.Schema.Variables.Any(x =\u003E x.Name == \u0022IrisColor\u0022))\r\n\t\t{\r\n\t\t\tcarrier = null;\r\n\t\t\tthrow new InvalidOperationException(\u0022The human iris shader is unavailable.\u0022);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void Apply(SkinnedModelRenderer body, CharacterAppearanceData data, bool refresh = false)\r\n\t{\r\n\t\tif (!body.IsValid() || body.Model is null)\r\n\t\t\treturn;\r\n\t\tif (target != body || model != body.Model || group != body.MaterialGroup)\r\n\t\t{\r\n\t\t\tDispose();\r\n\t\t\ttarget = body;\r\n\t\t\tmodel = body.Model;\r\n\t\t\tgroup = body.MaterialGroup;\r\n\t\t\tapplied = null;\r\n\t\t}\r\n\r\n\t\tif (data?.CustomEyeColor != true)\r\n\t\t{\r\n\t\t\tforeach (var i in copies.Keys)\r\n\t\t\t\tbody.Materials.SetOverride(i, null);\r\n\t\t\tDispose();\r\n\t\t\tapplied = null;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tvar color = data.EyeColor;\r\n\t\trequested = data.DeepCopy();\r\n\t\tif (applied != color)\r\n\t\t{\r\n\t\t\tstarted = DateTime.UtcNow;\r\n\t\t\tnextRefresh = started.AddMilliseconds(250);\r\n\t\t}\r\n\r\n\t\tfor (var i = 0; i \u003C body.Materials.Count; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar original = body.Materials.GetOriginal(i);\r\n\t\t\tif (original?.ShaderName != \u0022shaders/eyeball.shader\u0022)\r\n\t\t\t\tcontinue;\r\n\t\t\tif (!copies.TryGetValue(i, out var copy))\r\n\t\t\t\tcopies[i] = copy = original.CreateCopy($\u0022fate-iris-{body.Id}-{i}.vmat\u0022);\r\n\t\t\tif (refresh || applied != color || !textures.ContainsKey(i))\r\n\t\t\t{\r\n\t\t\t\tvar source = original.GetTexture(\u0022g_tColor\u0022);\r\n\t\t\t\tsource.MarkUsed(Math.Max(source.Width, source.Height));\r\n\t\t\t\tif (!textures.TryGetValue(i, out var texture))\r\n\t\t\t\t{\r\n\t\t\t\t\ttexture = Texture.Create(source.Width, source.Height).WithFormat(ImageFormat.RGBA8888).WithUAVBinding().WithName(\u0022Local human iris\u0022).Finish();\r\n\t\t\t\t\ttextures[i] = texture;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tvar attributes = new RenderAttributes();\r\n\t\t\t\tattributes.Set(\u0022BaseColor\u0022, source);\r\n\t\t\t\tattributes.Set(\u0022IrisColor\u0022, new Vector4(color.r, color.g, color.b, 1));\r\n\t\t\t\tattributes.Set(\u0022Result\u0022, texture);\r\n\t\t\t\tcompositor ??= new(\u0022shaders/human_iris.shader\u0022);\r\n\t\t\t\tcompositor.DispatchWithAttributes(attributes, source.Width, source.Height, 1);\r\n\t\t\t\tcopy.Set(\u0022g_tColor\u0022, texture);\r\n\t\t\t}\r\n\r\n\t\t\tbody.Materials.SetOverride(i, copy);\r\n\t\t}\r\n\r\n\t\tapplied = color;\r\n\t}\r\n\r\n\tpublic void Refresh()\r\n\t{\r\n\t\tif (!target.IsValid() || requested?.CustomEyeColor != true)\r\n\t\t\treturn;\r\n\t\tfor (var i = 0; i \u003C target.Materials.Count; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar material = target.Materials.GetOriginal(i);\r\n\t\t\tif (material?.ShaderName == \u0022shaders/eyeball.shader\u0022 \u0026\u0026 material.GetTexture(\u0022g_tColor\u0022)is Texture source)\r\n\t\t\t\tsource.MarkUsed(Math.Max(source.Width, source.Height));\r\n\t\t}\r\n\r\n\t\tvar now = DateTime.UtcNow;\r\n\t\tif ((now - started).TotalSeconds \u003C 3 \u0026\u0026 now \u003E= nextRefresh)\r\n\t\t{\r\n\t\t\tApply(target, requested, true);\r\n\t\t\tnextRefresh = now.AddMilliseconds(250);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void Dispose()\r\n\t{\r\n\t\tforeach (var texture in textures.Values)\r\n\t\t\ttexture.Dispose();\r\n\t\ttextures.Clear();\r\n\t\tcopies.Clear();\r\n\t\tapplied = null;\r\n\t\trequested = null;\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/ClothingResources.cs","FileName":"ClothingResources.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace CharacterEditor;\r\n\r\n/// \u003Csummary\u003ENative paths and portable cloud identities. Assets remain owned by their original packages.\u003C/summary\u003E\r\npublic static class ClothingResources\r\n{\r\n\tstatic readonly Dictionary\u003Cstring, Clothing\u003E loaded = new(StringComparer.OrdinalIgnoreCase);\r\n\tstatic readonly Dictionary\u003Cstring, Task\u003CClothing\u003E\u003E pending = new(StringComparer.OrdinalIgnoreCase);\r\n\tstatic readonly Dictionary\u003Cstring, Task\u003E iconLoads = new(StringComparer.OrdinalIgnoreCase);\n\tstatic readonly System.Threading.SemaphoreSlim iconRequests = new(3);\n\tstatic readonly Dictionary\u003Cstring, string\u003E cloudIcons = new(StringComparer.OrdinalIgnoreCase);\r\n\r\n\tpublic static int Revision { get; private set; }\r\n\r\n\tpublic static bool IsCloud(string reference) =\u003E reference?.StartsWith(\u0022cloud:\u0022, StringComparison.OrdinalIgnoreCase) == true;\r\n\r\n\tpublic static bool ValidReference(string reference)\r\n\t{\r\n\t\tif (string.IsNullOrWhiteSpace(reference) || reference.Length \u003E 256 || reference.Contains(\u0022..\u0022)) return false;\r\n\t\tif (!IsCloud(reference)) return reference.EndsWith(\u0022.clothing\u0022, StringComparison.OrdinalIgnoreCase);\r\n\t\tvar ident = reference[6..];\r\n\t\treturn ident.Split(\u0027.\u0027).Length == 2 \u0026\u0026 ident.Split(\u0027.\u0027).All(x =\u003E x.Length \u003E 0) \u0026\u0026 ident.All(c =\u003E char.IsAsciiLetterOrDigit(c) || c is \u0027.\u0027 or \u0027_\u0027 or \u0027-\u0027);\r\n\t}\r\n\r\n\tpublic static Clothing Get(string reference) =\u003E reference is null ? null : loaded.TryGetValue(reference, out var item) ? item : ResourceLibrary.Get\u003CClothing\u003E(reference);\r\n\r\n\tpublic static string Reference(Clothing item)\r\n\t{\r\n\t\tvar cloud = loaded.FirstOrDefault(x =\u003E IsCloud(x.Key) \u0026\u0026 x.Value == item).Key;\r\n\t\tif (cloud is not null) return cloud;\r\n\t\tvar definition = item.SteamItemDefinitionId is { } id ? Sandbox.Services.Inventory.FindDefinition(id) : null;\r\n\t\treturn !string.IsNullOrEmpty(definition?.PackageIdent) ? \u0022cloud:\u0022 \u002B definition.PackageIdent : item.ResourcePath;\r\n\t}\r\n\r\n\tpublic static Task\u003CClothing\u003E Load(string reference)\r\n\t{\r\n\t\tif (!ValidReference(reference)) return Task.FromException\u003CClothing\u003E(new InvalidOperationException(\u0022Use a .clothing asset path or cloud:creator.item reference.\u0022));\r\n\t\tif (Get(reference) is { } existing)\r\n\t\t{\r\n\t\t\tif (!SupportsHuman(existing)) return Task.FromException\u003CClothing\u003E(new InvalidOperationException(\u0022This item does not have a human model.\u0022));\r\n\t\t\treturn Finish(reference, existing);\r\n\t\t}\r\n\t\tif (pending.TryGetValue(reference, out var task) \u0026\u0026 !task.IsFaulted \u0026\u0026 !task.IsCanceled) return task;\r\n\t\treturn pending[reference] = LoadMissing(reference);\r\n\t}\r\n\r\n\tstatic async Task\u003CClothing\u003E LoadMissing(string reference)\r\n\t{\r\n\t\tif (!ValidReference(reference)) throw new InvalidOperationException(\u0022Invalid clothing reference.\u0022);\r\n\t\tvar item = IsCloud(reference) ? await Cloud.Load\u003CClothing\u003E(reference[6..]) : await ResourceLibrary.LoadAsync\u003CClothing\u003E(reference);\r\n\t\tif (item is null || item.IsError) throw new InvalidOperationException(\u0022Clothing unavailable: \u0022 \u002B reference);\r\n\t\tif (!SupportsHuman(item))\r\n\t\t\tthrow new InvalidOperationException(\u0022This item does not have a human model.\u0022);\r\n\t\tloaded[reference] = item;\r\n\t\tRevision\u002B\u002B;\r\n\t\treturn await Finish(reference, item);\r\n\t}\r\n\r\n\tstatic async Task\u003CClothing\u003E Finish(string reference, Clothing item)\r\n\t{\r\n\t\tif (IsCloud(reference) \u0026\u0026 !LocalIconExists(item) \u0026\u0026 !cloudIcons.ContainsKey(reference))\r\n\t\t{\r\n\t\t\tvar definition = item.SteamItemDefinitionId is { } id ? Sandbox.Services.Inventory.FindDefinition(id) : null;\r\n\t\t\tvar icon = definition?.IconUrl;\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tif (string.IsNullOrEmpty(icon)) icon = (await Package.FetchAsync(reference[6..], partial: true))?.ThumbWide;\r\n\t\t\t}\r\n\t\t\tcatch { /* A preview failure must not prevent wearing a downloaded item. */ }\r\n\t\t\tcloudIcons[reference] = icon ?? \u0022\u0022;\r\n\t\t\tcloudIcons[item.ResourcePath] = icon ?? \u0022\u0022;\r\n\t\t\tRevision\u002B\u002B;\r\n\t\t}\r\n\t\treturn item;\r\n\t}\r\n\r\n\tstatic bool LocalIconExists(Clothing item) =\u003E !string.IsNullOrEmpty(item.Icon.Path) \u0026\u0026\r\n\t\t(item.Icon.Path.StartsWith(\u0022https://\u0022, StringComparison.OrdinalIgnoreCase) || FileSystem.Mounted.FileExists(item.Icon.Path));\r\n\r\n\tpublic static string Icon(string reference)\n\t{\n\t\tvar item = Get(reference);\n\t\tif (item is not null \u0026\u0026 LocalIconExists(item)) return item.Icon.Path;\n\t\tif (reference is not null \u0026\u0026 cloudIcons.TryGetValue(reference, out var icon)) return icon;\n\t\tif (item is null) return \u0022\u0022;\n\t\tif (cloudIcons.TryGetValue(item.ResourcePath, out icon)) return icon;\n\t\tif (!iconLoads.TryGetValue(item.ResourcePath, out var request) || request.IsCompleted)\n\t\t\ticonLoads[item.ResourcePath] = RecoverIcon(item);\n\t\treturn \u0022\u0022;\n\t}\n\n\tstatic async Task RecoverIcon(Clothing item)\n\t{\n\t\tawait iconRequests.WaitAsync();\n\t\ttry\n\t\t{\n\t\t\tvar reference = Reference(item);\n\t\t\tif (IsCloud(reference))\n\t\t\t{\n\t\t\t\tawait Finish(reference, item);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Previously mounted clothing can appear as a local path without a session\n\t\t\t// alias. Match the package\u0027s primary asset, never just its display name.\n\t\t\tvar definition = Sandbox.Services.Inventory.Definitions.FirstOrDefault(d =\u003E string.Equals(d.Asset, item.ResourcePath, StringComparison.OrdinalIgnoreCase));\n\t\t\tif (!string.IsNullOrEmpty(definition?.PackageIdent))\n\t\t\t{\n\t\t\t\tRegisterPreview(item, definition.PackageIdent, definition.IconUrl);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar words = string.Join(\u0022 \u0022, (item.Title ?? \u0022\u0022).Split(\u0027 \u0027, StringSplitOptions.RemoveEmptyEntries).Where(x =\u003E !x.Contains(\u0027:\u0027)));\n\t\t\tif (words.Length == 0) return;\n\t\t\tvar found = await Package.FindAsync(words \u002B \u0022 type:clothing\u0022, 16);\n\t\t\tforeach (var candidate in found.Packages.OrderByDescending(p =\u003E string.Equals(p.Title, item.Title, StringComparison.OrdinalIgnoreCase)))\n\t\t\t{\n\t\t\t\tvar package = await Package.FetchAsync(candidate.FullIdent, partial: false);\n\t\t\t\tif (!string.Equals(package?.PrimaryAsset?.TrimStart(\u0027/\u0027), item.ResourcePath.TrimStart(\u0027/\u0027), StringComparison.OrdinalIgnoreCase)) continue;\n\t\t\t\tRegisterPreview(item, package.FullIdent, package.ThumbWide);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tcatch { /* Keep the picker usable when hosted previews are unavailable. */ }\n\t\tfinally\n\t\t{\n\t\t\tcloudIcons.TryAdd(item.ResourcePath, \u0022\u0022);\n\t\t\tRevision\u002B\u002B;\n\t\t\ticonRequests.Release();\n\t\t}\n\t}\n\n\tstatic void RegisterPreview(Clothing item, string ident, string icon)\n\t{\n\t\tloaded[\u0022cloud:\u0022 \u002B ident] = item;\n\t\tcloudIcons[item.ResourcePath] = icon ?? \u0022\u0022;\n\t\tcloudIcons[\u0022cloud:\u0022 \u002B ident] = icon ?? \u0022\u0022;\n\t}\n\r\n\tpublic static bool IsHair(Clothing c) =\u003E c.Category.ToString().StartsWith(\u0022Hair\u0022);\r\n\r\n\tpublic static bool IsFacialHair(Clothing c) =\u003E c.Category.ToString().StartsWith(\u0022FacialHair\u0022);\r\n\r\n\tpublic static bool IsBaseSkin(Clothing c) =\u003E c.HasHumanSkin \u0026\u0026 string.IsNullOrEmpty(c.HumanAltModel) \u0026\u0026 string.IsNullOrEmpty(c.HumanAltFemaleModel);\r\n\r\n\tstatic bool SupportsHuman(Clothing c) =\u003E c.HasHumanSkin || !string.IsNullOrEmpty(c.HumanAltModel) || !string.IsNullOrEmpty(c.HumanAltFemaleModel);\r\n\r\n\tpublic static WardrobeItem Describe(string reference, Clothing item)\r\n\t{\r\n\t\tvar category = Category(item.Category.ToString());\r\n\t\treturn new(item.Title, category, category, reference, item.SlotsUnder.ToString(), item.SlotsOver.ToString(), item.HideBody.ToString(), item.Category.ToString());\r\n\t}\r\n\tstatic string Category(string c) =\u003E c switch\r\n\t{\r\n\t\t\u0022Tops\u0022 or \u0022TShirt\u0022 or \u0022Sweatshirt\u0022 or \u0022Hoodie\u0022 or \u0022Shirt\u0022 or \u0022Knitwear\u0022 =\u003E \u0022Tops\u0022,\r\n\t\t\u0022Bottoms\u0022 or \u0022Shorts\u0022 or \u0022Trousers\u0022 or \u0022Jeans\u0022 or \u0022Skirt\u0022 or \u0022Underpants\u0022 =\u003E \u0022Bottoms\u0022,\r\n\t\t\u0022Footwear\u0022 or \u0022Heels\u0022 or \u0022Sandals\u0022 or \u0022Shoes\u0022 or \u0022Trainers\u0022 or \u0022Boots\u0022 or \u0022Slippers\u0022 or \u0022Socks\u0022 =\u003E \u0022Shoes\u0022,\r\n\t\t\u0022Vest\u0022 or \u0022Jacket\u0022 or \u0022Cardigan\u0022 or \u0022Coat\u0022 or \u0022Gilet\u0022 =\u003E \u0022Outerwear\u0022,\r\n\t\t\u0022Fullbody\u0022 or \u0022Dress\u0022 or \u0022Suit\u0022 or \u0022Costume\u0022 or \u0022Uniform\u0022 =\u003E \u0022Outfits\u0022,\r\n\t\t\u0022Gloves\u0022 =\u003E \u0022Gloves\u0022,\r\n\t\t_ when c.StartsWith(\u0022Glasses\u0022) || c == \u0022Eyewear\u0022 =\u003E \u0022Glasses\u0022,\r\n\t\t_ when c.StartsWith(\u0022Hat\u0022) || c == \u0022Headwear\u0022 =\u003E \u0022Hats\u0022,\r\n\t\t_ when c.StartsWith(\u0022FacialHair\u0022) =\u003E \u0022Facial hair\u0022,\r\n\t\t_ when c.StartsWith(\u0022Hair\u0022) =\u003E \u0022Hair\u0022,\r\n\t\t_ =\u003E \u0022Accessories\u0022\r\n\t};\r\n\r\n\t/// \u003Csummary\u003EProject clothing is discovered automatically; cloud choices are kept for the session.\u003C/summary\u003E\r\n\r\n\tpublic static IEnumerable\u003CWardrobeItem\u003E Available =\u003E WardrobeCatalog.Items\r\n\t\t.Concat(loaded.Where(x =\u003E !IsBaseSkin(x.Value)).Select(x =\u003E Describe(x.Key, x.Value)))\r\n\t\t.Concat(ResourceLibrary.GetAll\u003CClothing\u003E().Where(c =\u003E !IsBaseSkin(c) \u0026\u0026 (!string.IsNullOrEmpty(c.HumanAltModel) || !string.IsNullOrEmpty(c.HumanAltFemaleModel)))\r\n\t\t\t.Where(c =\u003E !loaded.Values.Contains(c)).Select(c =\u003E Describe(Reference(c), c)))\n\t\t.DistinctBy(x =\u003E x.Path);\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/PhotoFitAnchors.cs","FileName":"PhotoFitAnchors.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"namespace CharacterEditor;\r\n// Generated by the companion authoring tools/face-transfer/calibrate_photo_fit.py; approximate rest-space anchors.\r\npublic static class PhotoFitAnchors\r\n{\r\n public static readonly string[] Keys={\u0022faceHeight\u0022,\u0022jawWidth\u0022,\u0022eyeSeparation\u0022,\u0022leftEyeWidth\u0022,\u0022rightEyeWidth\u0022,\u0022noseWidth\u0022,\u0022noseLength\u0022,\u0022mouthWidth\u0022};\r\n public static readonly float[][] Ratios={\r\n  new float[]{1.44567f,0.8893f,0.18852f,0.27444f,0.25628f,0.26924f,0.26822f,0.50607f},\r\n  new float[]{1.39547f,0.87962f,0.16641f,0.28168f,0.26739f,0.22625f,0.27752f,0.47095f},\r\n  new float[]{1.43572f,0.89854f,0.17047f,0.25843f,0.25695f,0.28462f,0.27722f,0.45054f},\r\n  new float[]{1.4587f,0.8663f,0.16107f,0.30609f,0.27849f,0.22656f,0.30262f,0.43632f},\r\n  new float[]{1.44561f,0.89132f,0.16515f,0.25603f,0.25612f,0.19936f,0.30243f,0.43829f},\r\n  new float[]{1.41067f,0.89818f,0.17149f,0.26097f,0.25952f,0.26348f,0.25932f,0.4242f},\r\n  new float[]{1.41915f,0.89606f,0.16683f,0.25769f,0.25657f,0.22921f,0.28683f,0.42675f}\r\n };\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/SkinColorPresentation.cs","FileName":"SkinColorPresentation.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003EPer-renderer complexion and cosmetics retain native shading; optional iris color applies separately.\u003C/summary\u003E\r\npublic sealed class SkinColorPresentation\r\n{\r\n\treadonly Dictionary\u003Cint, Material\u003E copies = new();\r\n\tSkinnedModelRenderer target;\r\n\tModel model;\r\n\tstring group;\r\n\tColor? applied;\r\n\tbool normalized;\r\n\tstring makeupSignature;\r\n\tTexture appliedNeck;\r\n\treadonly Dictionary\u003Cint, HumanMakeupPresentation\u003E makeupCopies = new();\r\n\t// These native materials contain a darker photographed base than the other curated faces.\r\n\t// Correct their base when a palette/custom color is chosen, leaving Native/legacy looks exact.\r\n\tstatic readonly HashSet\u003Cstring\u003E DarkBases = new(StringComparer.OrdinalIgnoreCase)\r\n\t{\r\n\t\t\u0022models/citizen_human/bodies/male/body_mode_dark.vmat\u0022,\r\n\t\t\u0022models/citizen_human/bodies/female/female_body_mode_dark.vmat\u0022,\r\n\t\t\u0022models/citizen_human/heads/citizen_human_malehead01/citizen_human_malehead01.vmat\u0022,\r\n\t\t\u0022models/citizen_human/heads/citizen_human_malehead04/citizen_human_malehead04.vmat\u0022,\r\n\t\t\u0022models/citizen_human/heads/citizen_human_femalehead01/citizen_human_femalehead01.vmat\u0022\r\n\t};\r\n\tpublic static Color MaterialTint(Material original, Color color, bool normalize) =\u003E original.GetColor(\u0022g_vColorTint\u0022) * color * (normalize \u0026\u0026 DarkBases.Contains(original.Name) ? new Color(1.7f, 1.91f, 2.155f) : Color.White);\r\n\treadonly EyeColorPresentation eyes = new();\r\n\tpublic void Apply(SkinnedModelRenderer body, Color color, bool normalize = false, CharacterMakeupData makeup = null, string skinPath = \u0022\u0022, CharacterAppearanceData appearance = null)\r\n\t{\r\n\t\tApplySkin(body, color, normalize, makeup, skinPath, false, appearance?.BaseShape is not null, appearance?.BaseShape is not null);\r\n\t\teyes.Apply(body, appearance);\r\n\t}\r\n\r\n\tpublic void ApplyEmbeddedSkin(SkinnedModelRenderer body, Color color, bool normalize = false) =\u003E ApplySkin(body, color, normalize, null, \u0022\u0022, true);\r\n\tpublic void ApplyEmbeddedSkin(SkinnedModelRenderer body, CharacterAppearanceData appearance) =\u003E ApplySkin(body, appearance.SkinColor, appearance.NormalizeSkinTone, null, appearance.SkinPath, true, true, appearance.BaseShape is not null);\r\n\tvoid ApplySkin(SkinnedModelRenderer body, Color color, bool normalize, CharacterMakeupData makeup, string skinPath, bool preserveNonSkin = false, bool unified = false, bool unifiedBody = false)\r\n\t{\r\n\t\tif (!body.IsValid() || body.Model is null)\r\n\t\t\treturn;\r\n\t\tif (target != body || model != body.Model || group != body.MaterialGroup)\r\n\t\t{\r\n\t\t\ttarget = body;\r\n\t\t\tmodel = body.Model;\r\n\t\t\tgroup = body.MaterialGroup;\r\n\t\t\tcopies.Clear();\r\n\t\t\tapplied = null;\r\n\t\t\tforeach (var item in makeupCopies.Values)\r\n\t\t\t\titem.Dispose();\r\n\t\t\tmakeupCopies.Clear();\r\n\t\t\tmakeupSignature = null;\r\n\t\t\tClear(body, preserveNonSkin);\r\n\t\t}\r\n\r\n\t\tvar cosmetics = \u0022matched-neck-albedo\u0022 \u002B makeup?.PresentationKey() \u002B skinPath \u002B unified \u002B unifiedBody \u002B (unifiedBody ? UnifiedHuman.BodyToneTint(skinPath) : Color.White).ToString();\n\t\tvar neck = unifiedBody ? UnifiedNeckAssets.ColorTexture(skinPath) : null;\r\n\t\tif (applied == color \u0026\u0026 normalized == normalize \u0026\u0026 makeupSignature == cosmetics \u0026\u0026 appliedNeck == neck)\r\n\t\t{\r\n\t\t\tRefreshMakeup();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tappliedNeck = neck;\r\n\t\tmakeupSignature = cosmetics;\r\n\t\tif (makeup?.Active != true)\r\n\t\t{\r\n\t\t\tforeach (var item in makeupCopies.Values)\r\n\t\t\t\titem.Dispose();\r\n\t\t\tmakeupCopies.Clear();\r\n\t\t}\r\n\r\n\t\tif (color == Color.White \u0026\u0026 !normalize \u0026\u0026 makeup?.Active != true \u0026\u0026 !unified)\r\n\t\t{\r\n\t\t\tClear(body, preserveNonSkin);\r\n\t\t\tapplied = color;\r\n\t\t\tnormalized = false;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t// MaterialAccessor uses original model slots, including alternatives. GetMaterials(group)\r\n\t\t// returns a compact group list and cannot be used as renderer override indices.\r\n\t\tfor (var i = 0; i \u003C body.Materials.Count; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar slot = body.Materials.GetOriginal(i);\r\n\t\t\tvar original = unified ? UnifiedHuman.SkinMaterial(slot, skinPath, unifiedBody) : HumanMarkingModels.NativeMaterial(slot);\r\n\t\t\tvar armSide = HumanMarkingModels.ArmSide(slot);\r\n\t\t\tif (original?.ShaderName != \u0022shaders/skin.shader\u0022 || original.Name.EndsWith(\u0022/mouth.vmat\u0022, StringComparison.OrdinalIgnoreCase))\r\n\t\t\t\tcontinue;\r\n\t\t\tif (!copies.TryGetValue(i, out var copy) || unified)\r\n\t\t\t\tcopies[i] = copy = original.CreateCopy($\u0022fate-skin-{body.Id}-{i}.vmat\u0022);\r\n\t\t\tvar tint = MaterialTint(original, color, normalize) * (unifiedBody ? UnifiedHuman.BodyToneTint(original, skinPath) : Color.White);\r\n\t\t\tvar selectedHead = IsSelectedHead(original, skinPath);\r\n\t\t\tvar sourceColor = unifiedBody \u0026\u0026 selectedHead ? UnifiedNeckAssets.ColorTexture(skinPath) : original.GetTexture(\u0022g_tColor\u0022);\r\n\t\t\tvar bodyInk = original.Name.Contains(\u0022/bodies/\u0022, StringComparison.OrdinalIgnoreCase) \u0026\u0026 makeup?.Markings.Any(x =\u003E x.Style != \u0022none\u0022 \u0026\u0026 x.Ink.Amount \u003E 0 \u0026\u0026 MarkingCatalog.OnMaterial(x, armSide)) == true;\r\n\t\t\tif (makeup?.Active == true \u0026\u0026 (selectedHead || bodyInk))\r\n\t\t\t{\r\n\t\t\t\tif (!makeupCopies.TryGetValue(i, out var presentation))\r\n\t\t\t\t\tmakeupCopies[i] = presentation = new();\r\n\t\t\t\t// Keep complexion correction in the native skin shader on every surface.\r\n\t\t\t\t// Baking it into an 8-bit makeup texture changes color-space behavior and\r\n\t\t\t\t// clips bright corrections, creating a visible head/body seam.\r\n\t\t\t\tcopy.Set(\u0022g_tColor\u0022, presentation.Apply(original, makeup, skinPath, selectedHead, armSide, sourceColor));\r\n\t\t\t\tcopy.Set(\u0022g_vColorTint\u0022, tint);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tcopy.Set(\u0022g_tColor\u0022, sourceColor);\r\n\t\t\t\tcopy.Set(\u0022g_vColorTint\u0022, tint);\r\n\t\t\t\tif (makeupCopies.Remove(i, out var unused))\r\n\t\t\t\t\tunused.Dispose();\r\n\t\t\t}\r\n\r\n\t\t\tbody.Materials.SetOverride(i, copy);\r\n\t\t}\r\n\r\n\t\tapplied = color;\r\n\t\tnormalized = normalize;\r\n\t}\r\n\r\n\tpublic static bool IsSelectedHead(Material original, string skinPath)\r\n\t{\r\n\t\tvar id = System.IO.Path.GetFileNameWithoutExtension(skinPath);\r\n\t\tvar head = id switch\r\n\t\t{\r\n\t\t\t\u0022human01\u0022 =\u003E \u0022malehead01\u0022,\r\n\t\t\t\u0022human02\u0022 =\u003E \u0022femalehead01\u0022,\r\n\t\t\t\u0022human03\u0022 =\u003E \u0022malehead02\u0022,\r\n\t\t\t\u0022human04\u0022 =\u003E \u0022femalehead02\u0022,\r\n\t\t\t\u0022human05\u0022 =\u003E \u0022malehead03\u0022,\r\n\t\t\t\u0022human06\u0022 =\u003E \u0022malehead04\u0022,\r\n\t\t\t\u0022human07\u0022 =\u003E \u0022malehead05\u0022,\r\n\t\t\t_ =\u003E \u0022\u0022\r\n\t\t};\r\n\t\treturn head != \u0022\u0022 \u0026\u0026 original.Name.EndsWith($\u0022citizen_human_{head}.vmat\u0022, StringComparison.OrdinalIgnoreCase);\r\n\t}\r\n\r\n\tpublic void Dispose()\r\n\t{\r\n\t\teyes.Dispose();\r\n\t\tforeach (var item in makeupCopies.Values)\r\n\t\t\titem.Dispose();\r\n\t\tmakeupCopies.Clear();\r\n\t}\r\n\r\n\tvoid RefreshMakeup()\r\n\t{\r\n\t\tforeach (var pair in makeupCopies)\r\n\t\t{\r\n\t\t\tvar texture = pair.Value.Refresh();\r\n\t\t\tif (copies.TryGetValue(pair.Key, out var copy) \u0026\u0026 copy.GetTexture(\u0022g_tColor\u0022) != texture)\r\n\t\t\t\tcopy.Set(\u0022g_tColor\u0022, texture);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void Refresh()\r\n\t{\r\n\t\teyes.Refresh();\r\n\t\tRefreshMakeup();\r\n\t}\r\n\r\n\tpublic void Invalidate()\r\n\t{\r\n\t\tapplied = null;\r\n\t}\r\n\r\n\tstatic void Clear(SkinnedModelRenderer body, bool preserveNonSkin = false)\r\n\t{\r\n\t\tfor (var i = 0; i \u003C body.Materials.Count; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar slot = body.Materials.GetOriginal(i);\r\n\t\t\tif (preserveNonSkin \u0026\u0026 (HumanMarkingModels.NativeMaterial(slot)?.ShaderName != \u0022shaders/skin.shader\u0022 || HumanMarkingModels.NativeMaterial(slot).Name.EndsWith(\u0022/mouth.vmat\u0022, StringComparison.OrdinalIgnoreCase)))\r\n\t\t\t\tcontinue;\r\n\t\t\tif (HumanMarkingModels.ArmSide(slot) != 0)\r\n\t\t\t\tbody.Materials.SetOverride(i, HumanMarkingModels.NativeMaterial(slot));\r\n\t\t\telse if (body.Materials.HasOverride(i))\r\n\t\t\t\tbody.Materials.SetOverride(i, null);\r\n\t\t}\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Studio/StudioPanel.razor","FileName":"StudioPanel.razor","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"@using System\r\n@using System.Text.Json\r\n@using Sandbox\r\n@using Sandbox.UI\r\n@namespace CharacterEditor\r\n@inherits PanelComponent\r\n\r\n\u003Croot\u003E\r\n@if(Session is not null \u0026\u0026 Session.View!=\u0022hidden\u0022)\r\n{\r\n @if(Session.View==\u0022editor\u0022)\r\n {\r\n  \u003CCharacterCreatorPanel InitialPreset=@Session.Draft @ref=Creator Saved=@Save Closed=@RequestClose SaveLabel=@(Session.IsNew?\u0022Save character\u0022:\u0022Save changes\u0022) /\u003E\r\n  @if(!Session.IsNew){\u003Cbutton class=\u0022savecopy\u0022 onclick=@SaveCopy\u003ESave as a copy\u003C/button\u003E}\r\n  @if(ConfirmClose){\u003Cdiv class=\u0022shade\u0022\u003E\u003Cdiv class=\u0022dialog\u0022\u003E\u003Ch2\u003EDiscard your edits?\u003C/h2\u003E\u003Cp\u003EYour saved character will stay as it was.\u003C/p\u003E\u003Cdiv class=\u0022actions\u0022\u003E\u003Cbutton onclick=@(()=\u003EConfirmClose=false)\u003EKeep editing\u003C/button\u003E\u003Cbutton class=\u0022primary\u0022 onclick=@Discard\u003EDiscard edits\u003C/button\u003E\u003C/div\u003E\u003C/div\u003E\u003C/div\u003E}\r\n }\r\n else if(Session.View==\u0022plaza\u0022)\r\n {\r\n  \u003Cdiv class=\u0022plazahead\u0022\u003E\u003Cdiv\u003E\u003Cdiv class=\u0022eyebrow\u0022\u003ECHARACTER EDITOR\u003C/div\u003E\u003Ch1\u003EThe gathering place\u003C/h1\u003E\u003Cp\u003EYour characters, a little more alive.\u003C/p\u003E\u003C/div\u003E\u003Cbutton onclick=@(()=\u003ESession.View=\u0022library\u0022)\u003EBack to collection\u003C/button\u003E\u003C/div\u003E\r\n  \u003Cdiv class=\u0022residents\u0022\u003E\r\n   \u003Cdiv class=\u0022eyebrow\u0022\u003EIN THE PLAZA \u00B7 @Session.Plaza?.Residents.Count\u003C/div\u003E\r\n   @foreach(var resident in Session.Plaza?.Residents??new List\u003CCharacterAvatar\u003E())\r\n   {\u003Cdiv class=\u0022resident\u0022\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.Focus(resident.Preset.Id))\u003E@resident.GameObject.Name\u003C/button\u003E\u003Cbutton onclick=@(()=\u003ESession.Edit(resident.Preset,resident))\u003EEdit\u003C/button\u003E\u003C/div\u003E}\r\n   @if(Session.Library.Items.Count==0){\u003Cp\u003ESave your first character to bring this place to life.\u003C/p\u003E\u003Cbutton class=\u0022primary\u0022 onclick=@(()=\u003ESession.Edit())\u003ECreate a character\u003C/button\u003E}\r\n   @if(Session.Plaza?.PageCount\u003E1){\u003Cdiv class=\u0022actions\u0022\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.ChangePage(-1))\u003EPrevious\u003C/button\u003E\u003Cspan\u003E@(Session.Plaza.Page\u002B1) / @Session.Plaza.PageCount\u003C/span\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.ChangePage(1))\u003ENext\u003C/button\u003E\u003C/div\u003E}\r\n  \u003C/div\u003E\r\n  \u003Cdiv class=\u0022plazatools\u0022\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.Yaw-=25)\u003ETurn left\u003C/button\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.Yaw\u002B=25)\u003ETurn right\u003C/button\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.Distance-=80)\u003ECloser\u003C/button\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.Distance\u002B=80)\u003EFurther\u003C/button\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.Overview())\u003EOverview\u003C/button\u003E\u003Cbutton onclick=@(()=\u003ESession.Plaza.Paused=!Session.Plaza.Paused)\u003E@(Session.Plaza?.Paused==true?\u0022Let them wander\u0022:\u0022Pause wandering\u0022)\u003C/button\u003E\u003C/div\u003E\r\n }\r\n else\r\n {\r\n  \u003Cdiv class=\u0022collection\u0022\u003E\r\n   \u003Cdiv class=\u0022topbar\u0022\u003E\u003Cdiv class=\u0022brand\u0022\u003E\u003Cspan class=\u0022brandmark\u0022\u003EC\u003C/span\u003E\u003Cspan\u003ECHARACTER EDITOR\u003C/span\u003E\u003C/div\u003E\u003Cdiv class=\u0022actions\u0022\u003E\u003Cbutton onclick=@(()=\u003EImportOpen=true)\u003EImport preset\u003C/button\u003E\u003Cbutton class=\u0022primary\u0022 onclick=@(()=\u003ESession.Edit())\u003E\u002B Create character\u003C/button\u003E\u003C/div\u003E\u003C/div\u003E\r\n   \u003Cdiv class=\u0022intro\u0022\u003E\u003Cdiv\u003E\u003Cdiv class=\u0022eyebrow\u0022\u003EA PLACE FOR EVERY VERSION OF YOU\u003C/div\u003E\u003Ch1\u003EMeet your characters.\u003C/h1\u003E\u003Cp\u003EShape a face. Find a personality. Build a little world of your own.\u003C/p\u003E\u003C/div\u003E@if(Session.Plaza.IsValid()){\u003Cbutton class=\u0022plazalink\u0022 onclick=@Session.ShowPlaza\u003E\u003Cspan\u003EVisit the plaza\u003C/span\u003E\u003Cspan class=\u0022arrow\u0022\u003E\u2197\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n   \u003Cdiv class=\u0022librarybar\u0022\u003E\u003Cdiv class=\u0022actions\u0022\u003E\u003Cbutton class=@(!Favorites?\u0022chosen\u0022:\u0022\u0022) onclick=@(()=\u003EFavorites=false)\u003EAll characters \u00B7 @Session.Library.Items.Count\u003C/button\u003E\u003Cbutton class=@(Favorites?\u0022chosen\u0022:\u0022\u0022) onclick=@(()=\u003EFavorites=true)\u003EFavorites\u003C/button\u003E\u003C/div\u003E\u003CTextEntry class=\u0022search\u0022 Value=@Search OnTextEdited=@((string value)=\u003E{Search=value;}) Placeholder=\u0022Find a character\u0022 /\u003E\u003C/div\u003E\r\n   \u003Cdiv class=\u0022cards\u0022\u003E\r\n    @foreach(var preset in Visible)\r\n    {\r\n     \u003Cdiv class=\u0022card\u0022\u003E\u003Cbutton class=\u0022portrait\u0022 onclick=@(()=\u003ESession.Edit(preset))\u003E\u003CCharacterCreatorPreview Appearance=@preset.Appearance Portrait=@true /\u003E\u003C/button\u003E\u003Cdiv class=\u0022cardtitle\u0022\u003E\u003Ch2\u003E@preset.Name\u003C/h2\u003E\u003Cbutton class=@($\u0022favorite {(preset.Favorite?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESession.Favorite(preset))\u003E@(preset.Favorite?\u0022\u2605\u0022:\u0022\u2606\u0022)\u003C/button\u003E\u003C/div\u003E\u003Cdiv class=\u0022cardactions\u0022\u003E\u003Cbutton onclick=@(()=\u003ESession.Edit(preset))\u003EEdit\u003C/button\u003E\u003Cbutton onclick=@(()=\u003EExport(preset))\u003EExport\u003C/button\u003E\u003Cbutton class=\u0022delete\u0022 onclick=@(()=\u003EPendingDelete=preset)\u003EDelete\u003C/button\u003E\u003C/div\u003E\u003C/div\u003E\r\n    }\r\n    @if(!Visible.Any())\r\n    {\u003Cdiv class=\u0022empty\u0022\u003E\u003Cdiv class=\u0022emptyicon\u0022\u003E\u2726\u003C/div\u003E\u003Ch2\u003E@(Session.Library.Items.Count==0?\u0022Your cast starts here.\u0022:\u0022No characters found.\u0022)\u003C/h2\u003E\u003Cp\u003E@(Session.Library.Items.Count==0?\u0022Start with a familiar face, or make someone entirely new. Every saved character gets a place in your plaza.\u0022:\u0022Try another search or switch back to all characters.\u0022)\u003C/p\u003E\u003Cbutton class=\u0022primary\u0022 onclick=@(()=\u003ESession.Edit())\u003ECreate your first look\u003C/button\u003E\u003C/div\u003E}\r\n   \u003C/div\u003E\r\n   \u003Cdiv class=\u0022bottomline\u0022\u003E\u003Cspan\u003E7 blendable faces \u00B7 Body shaping \u00B7 Your own wardrobe\u003C/span\u003E\u003Cspan\u003EMade to mix, match \u0026amp; make your own\u003C/span\u003E\u003C/div\u003E\r\n  \u003C/div\u003E\r\n }\r\n @if(!string.IsNullOrWhiteSpace(Session.Message)){\u003Cdiv class=\u0022toast\u0022 onclick=@(()=\u003ESession.Message=null)\u003E@Session.Message\u003C/div\u003E}\r\n @if(Session.Library.Warnings.Count\u003E0 \u0026\u0026 !DismissWarnings){\u003Cdiv class=\u0022warning\u0022 onclick=@(()=\u003EDismissWarnings=true)\u003E@string.Join(\u0022 \u00B7 \u0022,Session.Library.Warnings) \u00B7 Click to dismiss\u003C/div\u003E}\r\n @if(PendingDelete is not null){\u003Cdiv class=\u0022shade\u0022\u003E\u003Cdiv class=\u0022dialog\u0022\u003E\u003Ch2\u003EDelete @PendingDelete.Name?\u003C/h2\u003E\u003Cp\u003EThis removes the saved character and its backup from your collection.\u003C/p\u003E\u003Cdiv class=\u0022actions\u0022\u003E\u003Cbutton onclick=@(()=\u003EPendingDelete=null)\u003EKeep character\u003C/button\u003E\u003Cbutton class=\u0022primary\u0022 onclick=@Delete\u003EDelete character\u003C/button\u003E\u003C/div\u003E\u003C/div\u003E\u003C/div\u003E}\r\n @if(ImportOpen || ExportJson is not null)\r\n {\u003Cdiv class=\u0022shade\u0022\u003E\u003Cdiv class=\u0022dialog transfer\u0022\u003E\u003Cdiv class=\u0022eyebrow\u0022\u003EPORTABLE PRESETS\u003C/div\u003E\u003Ch2\u003E@(ImportOpen?\u0022Bring a character along.\u0022:\u0022Take this character with you.\u0022)\u003C/h2\u003E\u003Cp\u003E@(ImportOpen?\u0022Paste a Character Editor preset or raw Fate Pending appearance JSON. Import always creates a new copy.\u0022:\u0022Copy this preset into another project using Character Kit. A copy is also saved in character_editor/exports in this game\u0027s data folder.\u0022)\u003C/p\u003E\u003CTextEntry class=\u0022json\u0022 Multiline=@true Value=@(ImportOpen?ImportJson:ExportJson) OnTextEdited=@((string value)=\u003E{ImportJson=value;}) /\u003E\u003Cdiv class=\u0022actions\u0022\u003E\u003Cbutton onclick=@CloseTransfer\u003EClose\u003C/button\u003E@if(ImportOpen){\u003Cbutton class=\u0022primary\u0022 onclick=@Import\u003EImport character\u003C/button\u003E}else{\u003Cbutton class=\u0022primary\u0022 onclick=@(()=\u003EClipboard.SetText(ExportJson))\u003ECopy preset\u003C/button\u003E}\u003C/div\u003E\u003C/div\u003E\u003C/div\u003E}\r\n @if(Session.EnableEscapeMenu \u0026\u0026 !Session.Embedded){\u003CStudioEscapeMenu @ref=EscapeMenu MainMenu=@ReturnToMainMenu OpenLab=@OpenFaceLab EnableExperiments=@(Session.EnableFaceLab \u0026\u0026 Session.View!=\u0022editor\u0022) HasUnsavedEdits=@HasUnsavedEdits /\u003E}\r\n}\r\n\u003C/root\u003E\r\n\r\n@code {\r\n\tpublic StudioSession Session =\u003E Components.Get\u003CStudioSession\u003E();\r\n\tpublic CharacterCreatorPanel Creator { get; set; }\r\n\tpublic StudioEscapeMenu EscapeMenu { get; set; }\r\n\r\n\tstring Search = \u0022\u0022, ImportJson = \u0022\u0022, ExportJson;\r\n\tbool Favorites, ImportOpen, ConfirmClose, DismissWarnings;\r\n\tCharacterPreset PendingDelete;\r\n\tIEnumerable\u003CCharacterPreset\u003E Visible =\u003E Session.Library.Items.Where(x =\u003E (!Favorites || x.Favorite) \u0026\u0026 x.Name.Contains(Search ?? \u0022\u0022, StringComparison.OrdinalIgnoreCase)).OrderByDescending(x =\u003E x.Favorite).ThenBy(x =\u003E x.Name);\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tSession?.EnsureLoaded();\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tbase.OnUpdate();\r\n\t\tif (Session?.EnableEscapeMenu == true \u0026\u0026 !Session.Embedded \u0026\u0026 Session.View != \u0022hidden\u0022 \u0026\u0026 EscapeMenu is not null \u0026\u0026 Input.EscapePressed)\r\n\t\t{\r\n\t\t\tInput.EscapePressed = false;\r\n\t\t\tEscapeMenu.Toggle();\r\n\t\t}\r\n\t}\r\n\r\n\tbool HasUnsavedEdits() =\u003E Session.View == \u0022editor\u0022 \u0026\u0026 Creator is not null \u0026\u0026 Session.Draft is not null \u0026\u0026 (Creator.DraftCharacterName != Session.Draft.Name || JsonSerializer.Serialize(Creator.DraftAppearance) != JsonSerializer.Serialize(Session.Draft.Appearance));\r\n\tvoid ReturnToMainMenu() =\u003E StudioSession.OpenScene(\u0022scenes/main_menu.scene\u0022);\r\n\tvoid OpenFaceLab() =\u003E StudioSession.OpenScene(\u0022scenes/face_lab.scene\u0022);\r\n\tstring Save(string name, CharacterAppearanceData data) =\u003E Session.Save(name, data);\r\n\tvoid SaveCopy()\r\n\t{\r\n\t\tif (Creator?.AppearanceReady == true)\r\n\t\t{\r\n\t\t\tvar error = Session.Save(Creator.DraftCharacterName, Creator.DraftAppearance, true);\r\n\t\t\tif (error is not null)\r\n\t\t\t\tSession.Message = error;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid RequestClose()\r\n\t{\r\n\t\tif (HasUnsavedEdits())\r\n\t\t\tConfirmClose = true;\r\n\t\telse\r\n\t\t\tDiscard();\r\n\t}\r\n\r\n\tvoid Discard()\r\n\t{\r\n\t\tConfirmClose = false;\r\n\t\tSession.CloseEditor();\r\n\t\tCreator = null;\r\n\t}\r\n\r\n\tvoid Delete()\r\n\t{\r\n\t\tSession.Delete(PendingDelete);\r\n\t\tPendingDelete = null;\r\n\t}\r\n\r\n\tvoid Import()\r\n\t{\r\n\t\tSession.Import(ImportJson);\r\n\t\tif (Session.Message.StartsWith(\u0022Imported \u0022))\r\n\t\t\tCloseTransfer();\r\n\t}\r\n\r\n\tvoid Export(CharacterPreset preset)\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\tExportJson = preset.ToJson();\r\n\t\t\tFileSystem.Data.CreateDirectory(\u0022character_editor/exports\u0022);\r\n\t\t\tFileSystem.Data.WriteAllText($\u0022character_editor/exports/{preset.Id}.json\u0022, ExportJson);\r\n\t\t}\r\n\t\tcatch (Exception e)\r\n\t\t{\r\n\t\t\tSession.Message = e.Message;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid CloseTransfer()\r\n\t{\r\n\t\tImportOpen = false;\r\n\t\tExportJson = null;\r\n\t\tImportJson = \u0022\u0022;\r\n\t}\r\n\r\n\tprotected override int BuildHash() =\u003E HashCode.Combine(Session?.View, Session?.Revision, Session?.Library.Revision, Search, Favorites, HashCode.Combine(ImportOpen, ExportJson, PendingDelete, ConfirmClose, DismissWarnings, Session?.Message), Session?.Plaza?.Paused, HashCode.Combine(Session?.Plaza?.Page, Session?.Plaza?.Residents.Count, Session?.EnableEscapeMenu, Session?.EnableFaceLab, Session?.Embedded));\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/Studio/StudioPanel.razor.scss","FileName":"StudioPanel.razor.scss","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"StudioPanel {\n position: absolute; width: 100%; height: 100%; font-family: Inter; color: #f0eee6; pointer-events: none;\n button { pointer-events: all; cursor: pointer; padding: 11px 17px; border-radius: 6px; background-color: #ffffff0a; border: 1px solid #ffffff20; color: #e9ebe6; font-size: 14px; flex-shrink: 0; align-items: center; justify-content: center; gap: 10px; }\n button:hover { background-color: #ffffff20; border-color: #c5e4a7; }\n button.primary { background-color: #d6ebaf; border-color: #d6ebaf; color: #263425; font-weight: 600; }\n button.primary:hover { background-color: #eaf9cc; }\n button.chosen { color: #d6ebaf; border-color: #d6ebaf88; }\n h1, h2, p { margin: 0; flex-shrink: 0; }\n h1 { font-size: 48px; font-weight: 500; letter-spacing: -1.5px; }\n h2 { font-size: 22px; font-weight: 500; }\n p { font-size: 15px; line-height: 25px; color: #aeb8ab; white-space: normal; }\n .eyebrow { font-size: 11px; letter-spacing: 2px; color: #c6d9b2; margin-bottom: 13px; }\n .collection { position: absolute; width: 100%; height: 100%; background-color: #17221ff9; padding: 30px 54px 20px; flex-direction: column; pointer-events: all; }\n .topbar { justify-content: space-between; align-items: center; flex-shrink: 0; }\n .brand { gap: 13px; align-items: center; letter-spacing: 2px; font-size: 12px; font-weight: 600; }\n .brandmark { width: 37px; height: 37px; border-radius: 50%; border: 2px solid #d6ebaf; color: #d6ebaf; font-size: 25px; align-items: center; justify-content: center; }\n .actions { gap: 10px; align-items: center; flex-shrink: 0; }\n .intro { margin: 42px 0 30px; justify-content: space-between; align-items: center; flex-shrink: 0; }\n .intro \u003E div { flex-direction: column; gap: 9px; }\n .intro .eyebrow { margin-bottom: 0; }\n .plazalink { width: 235px; height: 100px; background-color: #354b3e; justify-content: space-between; padding: 25px; font-size: 19px; border-color: #a5c08466; }\n .arrow { font-size: 36px; color: #d6ebaf; }\n .librarybar { justify-content: space-between; align-items: center; border-bottom: 1px solid #ffffff20; padding-bottom: 17px; flex-shrink: 0; }\n TextEntry.search { width: 240px; padding: 12px 15px; background-color: #0e1916; border: 1px solid #ffffff20; border-radius: 6px; color: #edf2e5; font-size: 14px; }\n .cards { flex-grow: 1; min-height: 0; overflow-y: auto; flex-wrap: wrap; align-content: flex-start; gap: 20px; padding: 22px 0; }\n .card { width: 220px; flex-shrink: 0; flex-direction: column; background-color: #25312c; border: 1px solid #ffffff18; border-radius: 9px; overflow: hidden; }\n button.portrait { width: 100%; height: 190px; padding: 0; border: 0; border-radius: 0; background-color: #435146; position: relative; }\n .portrait CharacterCreatorPreview { position: absolute; width: 100%; height: 100%; pointer-events: none; }\n .cardtitle { align-items: center; justify-content: space-between; padding: 10px 14px 4px; }\n .cardtitle h2 { font-size: 17px; max-width: 153px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; }\n button.favorite { padding: 0; border: 0; background-color: transparent; font-size: 26px; }\n .cardactions { gap: 5px; padding: 8px 12px 14px; }\n .cardactions button { font-size: 11px; padding: 7px 10px; }\n button.delete { color: #d5aaa1; }\n .empty { width: 100%; padding: 45px 0; flex-direction: column; align-items: center; gap: 17px; }\n .empty p { max-width: 450px; text-align: center; }\n .emptyicon { font-size: 54px; color: #d6ebaf; }\n .bottomline { justify-content: space-between; color: #809487; font-size: 11px; padding-top: 14px; border-top: 1px solid #ffffff18; flex-shrink: 0; }\n .plazahead { position: absolute; left: 38px; right: 38px; top: 30px; align-items: flex-start; justify-content: space-between; text-shadow: 0 2px 4px #000a; }\n .plazahead \u003E div { flex-direction: column; gap: 5px; }\n .plazahead h1 { font-size: 34px; }\n .plazahead button { background-color: #17221fee; }\n .plazahead p { color: #edf2e5; }\n .residents { position: absolute; left: 30px; top: 190px; width: 245px; max-height: 65%; overflow-y: auto; flex-direction: column; padding: 20px; background-color: #17221fe8; border: 1px solid #ffffff25; border-radius: 8px; pointer-events: all; gap: 8px; }\n .resident { gap: 6px; flex-shrink: 0; }\n .resident button { font-size: 12px; padding: 8px; }\n .resident button:first-child { flex-grow: 1; justify-content: flex-start; }\n .plazatools { position: absolute; bottom: 25px; right: 30px; gap: 6px; padding: 10px; background-color: #17221fe8; border-radius: 8px; }\n .plazatools button { font-size: 12px; padding: 10px; }\n .shade { position: absolute; width: 100%; height: 100%; background-color: #07140eea; align-items: center; justify-content: center; pointer-events: all; z-index: 50; }\n .dialog { width: 490px; padding: 35px; background-color: #25352b; border: 1px solid #acc79666; border-radius: 12px; flex-direction: column; gap: 22px; }\n .dialog .actions { justify-content: flex-end; }\n .transfer { width: 700px; }\n TextEntry.json { height: 260px; padding: 15px; background-color: #121f18; color: #d6ebaf; font-size: 12px; overflow-y: auto; white-space: pre-wrap; }\n .toast { position: absolute; bottom: 65px; left: 35%; max-width: 60%; padding: 14px 22px; background-color: #d6ebaf; color: #263425; border-radius: 8px; pointer-events: all; z-index: 100; font-size: 14px; }\n .warning { position: absolute; bottom: 110px; left: 30px; max-width: 70%; background-color: #633f24; padding: 15px; pointer-events: all; z-index: 101; }\n button.savecopy { position: absolute; right: 250px; bottom: 27px; z-index: 6; background-color: #25352b; }\n}\n"},{"Ident":"vendetta.characterkit","Path":"CharacterKit/SerializedMeshBones.cs","FileName":"SerializedMeshBones.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\nusing System.IO;\r\nusing System.Text;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003EReads the mesh-local bone order in the engine\u0027s SaveToVmdl metadata.\u003C/summary\u003E\ninternal static class SerializedMeshBones\r\n{\r\n\t// The engine\u0027s own serializer currently emits KV3 v2 with LZ4. This reader\r\n\t// consumes that output, never arbitrary on-disk compressed clothing files.\r\n\tpublic static string[] Read(byte[] block)\r\n\t{\r\n\t\tusing var r = new BinaryReader(new MemoryStream(block));\r\n\t\tif (r.ReadUInt32() != 0x4b563302)\r\n\t\t\tthrow new InvalidDataException(\u0022Unsupported serialized mesh metadata version.\u0022);\r\n\t\tr.ReadBytes(16);\r\n\t\tint compression = r.ReadInt32();\r\n\t\tr.ReadInt32();\r\n\t\tint bytes = r.ReadInt32(), ints = r.ReadInt32(), doubles = r.ReadInt32();\r\n\t\tr.ReadInt32();\r\n\t\tr.ReadInt32();\r\n\t\tint size = r.ReadInt32(), packed = r.ReadInt32();\r\n\t\tr.ReadInt32();\r\n\t\tr.ReadInt32();\r\n\t\tif (size \u003C 0 || size \u003E 32000000)\r\n\t\t\tthrow new InvalidDataException(\u0022Invalid mesh metadata size.\u0022);\r\n\t\tvar payload = r.ReadBytes(packed);\r\n\t\tvar data = compression == 0 ? payload : compression == 1 ? Expand(payload, size) : throw new InvalidDataException(\u0022Unsupported mesh metadata compression.\u0022);\r\n\t\tvar p4 = (bytes \u002B 3) \u0026 ~3;\r\n\t\tvar p8 = (p4 \u002B ints * 4 \u002B 7) \u0026 ~7;\r\n\t\tvar stringsAt = p8 \u002B doubles * 8;\r\n\t\tint strings = BitConverter.ToInt32(data, p4);\r\n\t\tvar text = new string[strings];\r\n\t\tint cursor = stringsAt;\r\n\t\tfor (int i = 0; i \u003C strings; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar end = Array.IndexOf(data, (byte)0, cursor);\r\n\t\t\tif (end \u003C 0)\r\n\t\t\t\tthrow new InvalidDataException(\u0022Missing metadata string terminator.\u0022);\r\n\t\t\ttext[i] = Encoding.UTF8.GetString(data, cursor, end - cursor);\r\n\t\t\tcursor = end \u002B 1;\r\n\t\t}\r\n\r\n\t\tvar reader = new Values(data, text, 0, p4 \u002B 4, p8, cursor);\r\n\t\tvar root = reader.Value(reader.Type(), 0) as Dictionary\u003Cstring, object\u003E;\r\n\t\tif (root?.TryGetValue(\u0022m_skeleton\u0022, out var skeleton) == true)\r\n\t\t\troot = skeleton as Dictionary\u003Cstring, object\u003E;\r\n\t\tif (root is null || !root.TryGetValue(\u0022m_bones\u0022, out var boneValue) || boneValue is not List\u003Cobject\u003E bones)\r\n\t\t\tthrow new InvalidDataException(\u0022Serialized garment has no mesh bone table.\u0022);\r\n\t\treturn bones.Cast\u003CDictionary\u003Cstring, object\u003E\u003E().Select(b =\u003E (string)b[\u0022m_boneName\u0022]).ToArray();\r\n\t}\r\n\r\n\tstatic byte[] Expand(byte[] input, int length)\r\n\t{\r\n\t\tvar output = new byte[length];\r\n\t\tint i = 0, o = 0;\r\n\t\tint Extended(int n)\r\n\t\t{\r\n\t\t\tif (n != 15)\r\n\t\t\t\treturn n;\r\n\t\t\tint b;\r\n\t\t\tdo\r\n\t\t\t{\r\n\t\t\t\tb = input[i\u002B\u002B];\r\n\t\t\t\tn \u002B= b;\r\n\t\t\t}\r\n\t\t\twhile (b == 255);\r\n\t\t\treturn n;\r\n\t\t}\r\n\r\n\t\twhile (i \u003C input.Length)\r\n\t\t{\r\n\t\t\tint token = input[i\u002B\u002B];\r\n\t\t\tint literal = Extended(token \u003E\u003E 4);\r\n\t\t\tif (i \u002B literal \u003E input.Length || o \u002B literal \u003E length)\r\n\t\t\t\tthrow new InvalidDataException(\u0022Invalid metadata literal.\u0022);\r\n\t\t\tArray.Copy(input, i, output, o, literal);\r\n\t\t\ti \u002B= literal;\r\n\t\t\to \u002B= literal;\r\n\t\t\tif (i == input.Length)\r\n\t\t\t\tbreak;\r\n\t\t\tint offset = input[i\u002B\u002B] | input[i\u002B\u002B] \u003C\u003C 8;\r\n\t\t\tint match = Extended(token \u0026 15) \u002B 4;\r\n\t\t\tif (offset \u003C= 0 || offset \u003E o || o \u002B match \u003E length)\r\n\t\t\t\tthrow new InvalidDataException(\u0022Invalid metadata match.\u0022);\r\n\t\t\tfor (int j = 0; j \u003C match; j\u002B\u002B)\r\n\t\t\t\toutput[o \u002B j] = output[o \u002B j - offset];\r\n\t\t\to \u002B= match;\r\n\t\t}\r\n\r\n\t\tif (o != length)\r\n\t\t\tthrow new InvalidDataException(\u0022Truncated mesh metadata.\u0022);\r\n\t\treturn output;\r\n\t}\r\n\r\n\tsealed class Values\r\n\t{\r\n\t\treadonly byte[] data;\r\n\t\treadonly string[] strings;\r\n\t\tint p1, p4, p8, types;\r\n\t\tpublic Values(byte[] data, string[] strings, int p1, int p4, int p8, int types)\r\n\t\t{\r\n\t\t\tthis.data = data;\r\n\t\t\tthis.strings = strings;\r\n\t\t\tthis.p1 = p1;\r\n\t\t\tthis.p4 = p4;\r\n\t\t\tthis.p8 = p8;\r\n\t\t\tthis.types = types;\r\n\t\t}\r\n\r\n\t\tint I()\r\n\t\t{\r\n\t\t\tvar value = BitConverter.ToInt32(data, p4);\r\n\t\t\tp4 \u002B= 4;\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t\tlong L()\r\n\t\t{\r\n\t\t\tvar value = BitConverter.ToInt64(data, p8);\r\n\t\t\tp8 \u002B= 8;\r\n\t\t\treturn value;\r\n\t\t}\r\n\r\n\t\tstring S(int i) =\u003E i \u003C 0 ? \u0022\u0022 : strings[i];\r\n\t\tpublic int Type()\r\n\t\t{\r\n\t\t\tint t = data[types\u002B\u002B];\r\n\t\t\tif ((t \u0026 128) != 0)\r\n\t\t\t{\r\n\t\t\t\tt \u0026= 127;\r\n\t\t\t\ttypes\u002B\u002B;\r\n\t\t\t}\r\n\r\n\t\t\treturn t;\r\n\t\t}\r\n\r\n\t\tpublic object Value(int type, int depth)\r\n\t\t{\r\n\t\t\tif (depth \u003E 64)\r\n\t\t\t\tthrow new InvalidDataException(\u0022Excessive mesh metadata nesting.\u0022);\r\n\t\t\tswitch (type)\r\n\t\t\t{\r\n\t\t\t\tcase 1:\r\n\t\t\t\t\treturn null;\r\n\t\t\t\tcase 2:\r\n\t\t\t\t\treturn data[p1\u002B\u002B] != 0;\r\n\t\t\t\tcase 3:\r\n\t\t\t\tcase 4:\r\n\t\t\t\t\treturn L();\r\n\t\t\t\tcase 5:\r\n\t\t\t\t\tvar number = BitConverter.Int64BitsToDouble(L());\r\n\t\t\t\t\treturn number;\r\n\t\t\t\tcase 6:\r\n\t\t\t\t\treturn S(I());\r\n\t\t\t\tcase 8:\r\n\t\t\t\tcase 10:\r\n\t\t\t\tcase 24:\r\n\t\t\t\t\tint count = type == 24 ? data[p1\u002B\u002B] : I();\r\n\t\t\t\t\tif (count \u003C 0 || count \u003E 2000000)\r\n\t\t\t\t\t\tthrow new InvalidDataException(\u0022Invalid metadata array.\u0022);\r\n\t\t\t\t\tint element = type == 8 ? 0 : Type();\r\n\t\t\t\t\tvar array = new List\u003Cobject\u003E(count);\r\n\t\t\t\t\tfor (int i = 0; i \u003C count; i\u002B\u002B)\r\n\t\t\t\t\t\tarray.Add(Value(element == 0 ? Type() : element, depth \u002B 1));\r\n\t\t\t\t\treturn array;\r\n\t\t\t\tcase 9:\r\n\t\t\t\t\tint members = I();\r\n\t\t\t\t\tif (members \u003C 0 || members \u003E 100000)\r\n\t\t\t\t\t\tthrow new InvalidDataException(\u0022Invalid metadata object.\u0022);\r\n\t\t\t\t\tvar obj = new Dictionary\u003Cstring, object\u003E();\r\n\t\t\t\t\tfor (int i = 0; i \u003C members; i\u002B\u002B)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tint t = Type();\r\n\t\t\t\t\t\tstring name = S(I());\r\n\t\t\t\t\t\tobj[name] = Value(t, depth \u002B 1);\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\treturn obj;\r\n\t\t\t\tcase 11:\r\n\t\t\t\tcase 12:\r\n\t\t\t\t\treturn I();\r\n\t\t\t\tcase 13:\r\n\t\t\t\t\treturn true;\r\n\t\t\t\tcase 14:\r\n\t\t\t\t\treturn false;\r\n\t\t\t\tcase 15:\r\n\t\t\t\t\treturn 0L;\r\n\t\t\t\tcase 16:\r\n\t\t\t\t\treturn 1L;\r\n\t\t\t\tcase 17:\r\n\t\t\t\t\treturn 0d;\r\n\t\t\t\tcase 18:\r\n\t\t\t\t\treturn 1d;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tthrow new InvalidDataException(\u0022Unsupported mesh metadata node \u0022 \u002B type);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/CharacterAvatar.cs","FileName":"CharacterAvatar.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003EDrop-in human prefab. The same portable JSON drives editor and world rendering.\u003C/summary\u003E\r\npublic sealed class CharacterAvatar : Component, Component.ExecuteInEditor\r\n{\r\n\t[Property, TextArea]\r\n\tpublic string PresetJson { get; set; } = \u0022\u0022;\r\n\r\n\t[Property]\r\n\tpublic bool ShowClothing { get; set; } = true;\r\n\r\n\t[Property]\r\n\tpublic bool AllowEditing { get; set; } = true;\r\n\r\n\t[Property]\r\n\tpublic bool ShowEditButton { get; set; }\r\n\r\n\t[Property]\r\n\tpublic SkinnedModelRenderer BodyTarget { get; set; }\r\n\r\n\t[Property]\r\n\tpublic Dresser Wardrobe { get; set; }\r\n\tpublic CharacterPreset Preset { get; private set; } = new();\r\n\r\n\t[Property, ReadOnly]\r\n\tpublic string Error { get; private set; }\r\n\tpublic SkinnedModelRenderer Body =\u003E BodyTarget.IsValid() ? BodyTarget : visual?.Body;\r\n\r\n\tCharacterVisual visual;\r\n\tstring received;\r\n\tGameObject editPrompt;\r\n\tTask wardrobeLoad;\r\n\tint wardrobeRevision;\r\n\tpublic void Apply(CharacterPreset preset)\r\n\t{\r\n\t\tif (preset.Validate()is string error)\r\n\t\t\tthrow new InvalidOperationException(error);\r\n\t\tPreset = preset.DeepCopy();\r\n\t\tif (BodyTarget.IsValid() \u0026\u0026 Preset.Appearance.BaseShape is null)\r\n\t\t\tUnifiedHuman.Enable(Preset.Appearance);\r\n\t\tPresetJson = Preset.ToJson();\r\n\t\treceived = PresetJson;\r\n\t\tSyncWardrobe();\r\n\t}\r\n\r\n\t[Button]\r\n\tpublic void Randomize()\r\n\t{\r\n\t\tvar previous = Preset.Appearance;\r\n\t\tvar a = AppearanceCatalog.Randomize(blendHeads: true);\r\n\t\ta.VoiceId = previous.VoiceId;\r\n\t\tif (previous.BaseShape is { } shape)\r\n\t\t{\r\n\t\t\ta.BaseShape.ClothingVariant = shape.ClothingVariant;\r\n\t\t\ta.BaseShape.PhotoFaces = shape.PhotoFaces.Select(f =\u003E f.DeepCopy()).ToList();\r\n\t\t\tforeach (var face in a.BaseShape.PhotoFaces)\r\n\t\t\t\tface.Influence = 0;\r\n\t\t}\r\n\r\n\t\tvar name = GameObject.Name;\r\n\t\tApply(new CharacterPreset { Name = string.IsNullOrWhiteSpace(name) ? \u0022New character\u0022 : name[..Math.Min(name.Length, 40)], Appearance = a });\r\n\t}\r\n\r\n\t[Button]\r\n\tpublic void LoadJson()\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\tApply(CharacterPreset.FromJson(PresetJson));\r\n\t\t\tError = null;\r\n\t\t}\r\n\t\tcatch (Exception e)\r\n\t\t{\r\n\t\t\tError = e.Message;\r\n\t\t}\r\n\t}\r\n\r\n\t[Button]\r\n\tpublic void RefreshAppearance()\r\n\t{\r\n\t\tvisual?.Invalidate();\r\n\t\tOnUpdate();\r\n\t}\r\n\r\n\t[Button]\r\n\tpublic void UpdateJson()\r\n\t{\r\n\t\tif (Wardrobe.IsValid())\r\n\t\t{\r\n\t\t\tvar items = Wardrobe.Clothing.Where(e =\u003E e.Clothing is not null).Select(e =\u003E e.Clothing).ToArray();\r\n\t\t\tvar a = Preset.Appearance;\r\n\t\t\ta.HairPath = items.FirstOrDefault(ClothingResources.IsHair) is { } hair ? ClothingResources.Reference(hair) : \u0022\u0022;\r\n\t\t\ta.FacialHairPath = items.FirstOrDefault(ClothingResources.IsFacialHair) is { } facial ? ClothingResources.Reference(facial) : \u0022\u0022;\r\n\t\t\ta.ClothingPaths = items.Where(c =\u003E !ClothingResources.IsBaseSkin(c) \u0026\u0026 !ClothingResources.IsHair(c) \u0026\u0026 !ClothingResources.IsFacialHair(c)).Select(ClothingResources.Reference).ToArray();\r\n\t\t}\r\n\r\n\t\tif (Preset.Validate()is string error)\r\n\t\t{\r\n\t\t\tError = error;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tPresetJson = Preset.ToJson();\r\n\t\treceived = PresetJson;\r\n\t\tError = null;\r\n\t}\r\n\r\n\tvoid SyncWardrobe()\r\n\t{\r\n\t\tif (!Wardrobe.IsValid())\r\n\t\t\treturn;\r\n\t\twardrobeLoad = SyncWardrobeAsync(Preset.Appearance.DeepCopy(), \u002B\u002BwardrobeRevision);\r\n\t}\r\n\r\n\tasync Task SyncWardrobeAsync(CharacterAppearanceData a, int revision)\r\n\t{\r\n\t\tawait CharacterVisual.Load(a);\r\n\t\tif (!IsValid || revision != wardrobeRevision || !Wardrobe.IsValid())\r\n\t\t\treturn;\r\n\t\tvar container = new ClothingContainer\r\n\t\t{\r\n\t\t\tPrefersHuman = true\r\n\t\t};\r\n\t\tvar paths = new[]\r\n\t\t{\r\n\t\t\tUnifiedHuman.PresentationSkin(a),\r\n\t\t\ta.HairPath,\r\n\t\t\ta.FacialHairPath\r\n\t\t}.Concat(WardrobeCatalog.Paths(a)).Where(p =\u003E !string.IsNullOrEmpty(p));\r\n\t\tforeach (var path in paths)\r\n\t\t{\r\n\t\t\tvar item = ClothingResources.Get(path);\r\n\t\t\tif (item is not null)\r\n\t\t\t\tcontainer.Add(item);\r\n\t\t}\r\n\r\n\t\tWardrobe.Source = Dresser.ClothingSource.Manual;\r\n\t\tWardrobe.BodyTarget = Body;\r\n\t\tWardrobe.ApplyHeightScale = false;\r\n\t\tWardrobe.Clothing = container.Clothing;\r\n\t\tWardrobe.Apply(container);\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tif (received != PresetJson)\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tbool initial = received is null;\r\n\t\t\t\tPreset = string.IsNullOrWhiteSpace(PresetJson) ? new CharacterPreset() : CharacterPreset.FromJson(PresetJson);\r\n\t\t\t\tif (BodyTarget.IsValid() \u0026\u0026 Preset.Appearance.BaseShape is null)\r\n\t\t\t\t\tUnifiedHuman.Enable(Preset.Appearance);\r\n\t\t\t\treceived = PresetJson;\r\n\t\t\t\tif (!string.IsNullOrWhiteSpace(PresetJson) \u0026\u0026 (!initial || !Wardrobe.IsValid() || Wardrobe.Clothing.Count == 0))\r\n\t\t\t\t\tSyncWardrobe();\r\n\t\t\t\tError = null;\r\n\t\t\t}\r\n\t\t\tcatch (Exception e)\r\n\t\t\t{\r\n\t\t\t\tError = e.Message;\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (wardrobeLoad?.IsFaulted == true)\r\n\t\t{\r\n\t\t\tError = wardrobeLoad.Exception.GetBaseException().Message;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (wardrobeLoad?.IsCompleted == false)\r\n\t\t\treturn;\r\n\t\tvisual ??= new CharacterVisual(GameObject, BodyTarget, Wardrobe);\r\n\t\tvisual.Update(Preset.Appearance, ShowClothing);\r\n\t\tError = visual.Error;\r\n\t\tif (!Scene.IsEditor \u0026\u0026 ShowEditButton \u0026\u0026 AllowEditing \u0026\u0026 !editPrompt.IsValid())\r\n\t\t{\r\n\t\t\teditPrompt = new GameObject\r\n\t\t\t{\r\n\t\t\t\tParent = GameObject,\r\n\t\t\t\tName = \u0022Edit character button\u0022,\r\n\t\t\t\tNetworkMode = NetworkMode.Never\r\n\t\t\t};\r\n\t\t\teditPrompt.Components.Create\u003CScreenPanel\u003E();\r\n\t\t\teditPrompt.Components.Create\u003CAvatarEditButton\u003E().Avatar = this;\r\n\t\t}\r\n\r\n\t\tif (editPrompt.IsValid())\r\n\t\t\teditPrompt.Enabled = !Scene.IsEditor \u0026\u0026 ShowEditButton \u0026\u0026 AllowEditing;\r\n\t}\r\n\r\n\tpublic void OpenEditor()\r\n\t{\r\n\t\tif (!AllowEditing)\r\n\t\t\treturn;\r\n\t\tvar session = Scene.GetAllComponents\u003CStudioSession\u003E().FirstOrDefault();\r\n\t\tif (!session.IsValid())\r\n\t\t{\r\n\t\t\tusing var scope = Scene.Push();\r\n\t\t\tvar ui = new GameObject\r\n\t\t\t{\r\n\t\t\t\tName = \u0022Character editor\u0022,\r\n\t\t\t\tNetworkMode = NetworkMode.Never\r\n\t\t\t};\r\n\t\t\tsession = ui.Components.Create\u003CStudioSession\u003E();\r\n\t\t\tsession.Embedded = true;\r\n\t\t\tui.Components.Create\u003CScreenPanel\u003E();\r\n\t\t\tui.Components.Create\u003CStudioPanel\u003E();\r\n\t\t}\r\n\r\n\t\tsession.Edit(Preset, this);\r\n\t}\r\n\r\n\tprotected override void OnDisabled()\r\n\t{\r\n\t\tvisual?.Dispose();\r\n\t\tvisual = null;\r\n\t}\r\n\r\n\tprotected override void OnDestroy()\r\n\t{\r\n\t\tvisual?.Dispose();\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/CharacterCreatorPanel.razor","FileName":"CharacterCreatorPanel.razor","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"@using Sandbox\r\n\r\n@using Sandbox.UI\r\n\r\n@using System\r\n\r\n@using System.Text.Json\r\n\r\n@using System.Threading.Tasks\r\n\r\n@namespace CharacterEditor\r\n\r\n@inherits Panel\r\n\r\n\u003Croot class=\u0022interactive creatoroverlay\u0022\u003E\r\n\r\n @if(true)\r\n\r\n {\r\n\r\n  \u003Cdiv class=\u0022creatorwindow\u0022\u003E\r\n\r\n   \u003Cdiv class=\u0022creatorheader\u0022\u003E\u003Cdiv\u003E\u003Cdiv class=\u0022creatorbreadcrumb\u0022\u003E\u003Cspan\u003E@ProductLabel\u003C/span\u003E\u003Cspan class=\u0022material-icons\u0022\u003Echevron_right\u003C/span\u003E\u003Cspan\u003E@LocationLabel\u003C/span\u003E\u003C/div\u003E\u003Cdiv class=\u0022creatortitle\u0022\u003E@Title\u003C/div\u003E\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton quiet cancelcreation\u0022 onclick=@Back\u003E\u003Cspan class=\u0022material-icons\u0022\u003Earrow_back\u003C/span\u003EBack\u003C/button\u003E\u003C/div\u003E\r\n\r\n   \u003Cdiv class=\u0022creatorbody\u0022\u003E\r\n\r\n    \u003Cdiv class=\u0022creatorcontrols\u0022\u003E\r\n\r\n     \u003Cdiv class=\u0022creatortabs\u0022\u003E@foreach(var tab in Tabs){\u003Cbutton class=@($\u0022creatorbutton creatortab {(Tab==tab?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChangeTab(tab))\u003E@tab\u003C/button\u003E}\u003C/div\u003E\r\n\r\n     \u003Cdiv class=@($\u0022creatoroptions {(Tab==\u0022Voice\u0022?\u0022voiceoptions\u0022:\u0022\u0022)}\u0022)\u003E\r\n\r\n      \u003CReferenceFacePanel style=@(Tab==\u0022Photos\u0022?\u0022display: flex;\u0022:\u0022display: none;\u0022) Appearance=@Appearance Generated=@AddPhotoFace /\u003E\r\n      @if(Tab==\u0022Presets\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EChoose a starting look\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EStart with a preset, then make it your own.\u003C/div\u003E\u003Cdiv class=\u0022presetgrid\u0022\u003E\r\n\r\n        @foreach(var preset in AppearanceCatalog.Presets){\u003Cbutton class=@($\u0022creatorbutton presetchoice {(ChosenPreset==preset.Id?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EApplyPreset(preset.Id))\u003E\u003Cdiv class=\u0022presetportrait\u0022\u003E\u003CCharacterCreatorPreview Appearance=@preset.Appearance Portrait=@true /\u003E\u003C/div\u003E\u003Cdiv class=\u0022presetlabel\u0022\u003E@preset.Label\u003C/div\u003E\u003C/button\u003E}\r\n\r\n       \u003C/div\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Face \u0026 body\u0022)\r\n\r\n      {\r\n\r\n       @if(Appearance.BaseShape is not null)\r\n       {\r\n        \u003Cdiv class=\u0022optionheading\u0022\u003EBlend your base face\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EOverall sets the whole face. In regional tabs, blend that result with individual faces for that area. Refine the result in Shape; choose textures in Skin.\u003C/div\u003E\r\n        \u003Cdiv class=\u0022blendtabs\u0022\u003E@foreach(var region in new[]{\u0022overall\u0022}.Concat(FaceRegions.Ids)){\u003Cbutton class=@($\u0022creatorbutton blendtab blend-tab-{region} {(BlendRegion==region?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EBlendRegion=region)\u003E@FaceRegions.Label(region)\u003C/button\u003E}\u003C/div\u003E\r\n        @if(BlendRegion!=\u0022overall\u0022)\r\n        {\r\n         \u003Cdiv class=\u0022regionstatus\u0022\u003E\u003Cspan\u003EMix the live Overall blend with individual faces. Relative weights determine each share.\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet reset-region\u0022 onclick=@ResetRegion\u003EReset region\u003C/button\u003E\u003C/div\u003E\r\n         \u003Cdiv class=\u0022overallblendrow\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EOverall blend\u003C/span\u003E\u003Cspan\u003E@($\u0022{MathF.Round(OverallWeight*100)} influence \u00B7 {MathF.Round(FaceRegions.OverallShare(Appearance.BaseShape,BlendRegion)*100)}% of region\u0022)\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider region-overall-weight\u0022 Value=@OverallWeight OnValueChanged=@SetOverallWeight Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EFollows changes made in the Overall tab.\u003C/div\u003E\u003C/div\u003E\r\n        }\r\n        @for(var i=0;i\u003C7;i\u002B\u002B)\r\n        {\r\n         var index=i;\r\n         \u003Cdiv class=\u0022shaperow\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EFace @(index\u002B1)\u003C/span\u003E\u003Cspan\u003E@BlendWeightLabel(index)\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider head-weight-{index\u002B1}\u0022) Value=@HeadWeight(index) OnValueChanged=@((float value)=\u003ESetHeadWeight(index,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n        }\r\n        @foreach(var face in Appearance.BaseShape.PhotoFaces)\r\n        {\r\n         \u003Cdiv class=\u0022photoblendcard\u0022\u003E\r\n          \u003Cdiv class=\u0022photoblendheader\u0022\u003E\u003Cspan class=\u0022photoblendname\u0022\u003E@face.Name\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet delete-photo-face\u0022 tooltip=\u0022Delete this custom face from the character and all regions\u0022 onclick=@(()=\u003EFaceRegions.RemovePhoto(Appearance.BaseShape,face))\u003E\u003Cspan class=\u0022material-icons\u0022\u003Edelete_outline\u003C/span\u003E\u003C/button\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EBlend influence\u003C/span\u003E\u003Cspan\u003E@($\u0022{MathF.Round(PhotoWeight(face)*100)} influence \u00B7 {MathF.Round(FaceRegions.PhotoEntryShare(Appearance.BaseShape,face,BlendRegion)*100)}% of blend\u0022)\u003C/span\u003E\u003C/div\u003E\r\n          \u003CSliderControl class=\u0022shapeslider photo-face-weight\u0022 Value=@PhotoWeight(face) OnValueChanged=@((float value)=\u003ESetPhotoWeight(face,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\r\n          @if(BlendRegion==\u0022overall\u0022)\r\n          {\r\n           \u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EPhoto detail strength\u003C/span\u003E\u003Cspan\u003E@MathF.Round(face.Strength*100)%\u003C/span\u003E\u003C/div\u003E\r\n           \u003CSliderControl class=\u0022shapeslider photo-face-strength\u0022 Value=@face.Strength OnValueChanged=@((float value)=\u003E{face.Strength=Math.Clamp(value,0,2);}) Min=@(0f) Max=@(2f) Step=@(.01f) ShowTextEntry=@false /\u003E\r\n           \u003Cdiv class=\u0022optiondescription\u0022\u003E100% is the fitted shape. Higher values exaggerate its slider changes, within normal Shape limits. This also affects regions using this face.\u003C/div\u003E\r\n          }\r\n         \u003C/div\u003E\r\n        }\r\n        @if(Appearance.BaseShape.TotalInfluence==0){\u003Cdiv class=\u0022optiondescription\u0022\u003EAll influences are zero: using Face 3 as the neutral base.\u003C/div\u003E}\r\n        \u003Cdiv class=\u0022optionheading\u0022\u003EBase body\u003C/div\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@MathF.Round((1-Appearance.BaseShape.MaleBody)*100)% female\u003C/span\u003E\u003Cspan\u003E@MathF.Round(Appearance.BaseShape.MaleBody*100)% male\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider base-body\u0022 Value=@Appearance.BaseShape.MaleBody OnValueChanged=@SetBodyBlend Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\r\n        @foreach(var variant in new[]{\u0022male\u0022,\u0022female\u0022})\r\n        {\r\n         \u003Cbutton class=@($\u0022creatorbutton quiet clothing-prefer-{variant}\u0022) role=\u0022checkbox\u0022 aria-checked=@(Appearance.BaseShape.ClothingVariant==variant?\u0022true\u0022:\u0022false\u0022) onclick=@(()=\u003EToggleClothingVariant(variant))\u003E\u003Cspan class=\u0022material-icons\u0022\u003E@(Appearance.BaseShape.ClothingVariant==variant?\u0022check_box\u0022:\u0022check_box_outline_blank\u0022)\u003C/span\u003EPrefer @variant clothing version\u003C/button\u003E\r\n        }\r\n        \u003Cdiv class=\u0022optiondescription\u0022\u003ENeither selected: use the body blend. Available versions still fit your body shape.\u003C/div\u003E\r\n        \u003Cdiv class=\u0022optiondescription\u0022\u003EBody blend is independent of face and skin. Breast adjustments fade with the female amount; bulge adjustments fade with the male amount.\u003C/div\u003E\r\n       }\r\n       else\r\n       {\r\n        \u003Cdiv class=\u0022optionheading\u0022\u003ENative base\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EThis saved character uses its original native face and body.\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton\u0022 onclick=@EnableBlending\u003EEnable weighted base blending\u003C/button\u003E\r\n        \u003Cdiv class=\u0022bodychoices\u0022\u003E\u003Cbutton class=\u0022creatorbutton\u0022 onclick=@(()=\u003EChooseBody(\u0022male\u0022))\u003EMasculine\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton\u0022 onclick=@(()=\u003EChooseBody(\u0022female\u0022))\u003EFeminine\u003C/button\u003E\u003C/div\u003E\u003Cdiv class=\u0022skingrid\u0022\u003E@foreach(var skin in AppearanceCatalog.Skins.Where(x=\u003Ex.BodyType==BodyType)){\u003Cbutton class=\u0022creatorbutton skinchoice\u0022 onclick=@(()=\u003EChooseSkin(skin))\u003E@FaceLabel(skin)\u003C/button\u003E}\u003C/div\u003E\r\n       }\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EEye color\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EColor the iris while keeping the pupil and whites of the eyes.\u003C/div\u003E\r\n       \u003Cdiv class=\u0022colorchoices\u0022\u003E\u003Cbutton class=@($\u0022creatorbutton colorchoice eyecolorchoice {(!Appearance.CustomEyeColor?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseEyeColor(null))\u003ENative\u003C/button\u003E@foreach(var color in EyeColors){\u003Cbutton class=@($\u0022creatorbutton colorchoice eyecolorchoice {(Appearance.CustomEyeColor \u0026\u0026 Appearance.EyeColor==color.Color?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseEyeColor(color.Color))\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n       \u003Cbutton class=\u0022creatorbutton quiet eyecustomtoggle\u0022 onclick=@(()=\u003EChooseEyeColor(Appearance.EyeColor))\u003E\u003Cspan class=\u0022material-icons\u0022\u003Epalette\u003C/span\u003ECustom eye color\u003C/button\u003E\r\n       @if(Appearance.CustomEyeColor){@foreach(var channel in new[]{\u0022Red\u0022,\u0022Green\u0022,\u0022Blue\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@channel\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider eye-{channel.ToLowerInvariant()}\u0022) Value=@EyeChannel(channel) OnValueChanged=@((float value)=\u003ESetEyeChannel(channel,value)) Min=@(0f) Max=@(1f) Step=@(1f/255) ShowTextEntry=@false /\u003E\u003C/div\u003E}}\r\n      }\r\n\r\n      else if(Tab==\u0022Skin\u0022)\r\n\r\n      {\r\n\r\n       @if(Appearance.BaseShape is not null){\u003Cdiv class=\u0022optionheading\u0022\u003ESkin details\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EChoose the native texture set: pigment, pores, freckles and other details. This does not change your face weights or body shape.\u003C/div\u003E\u003Cdiv class=\u0022skingrid\u0022\u003E@foreach(var skin in AppearanceCatalog.Skins){\u003Cbutton class=@($\u0022creatorbutton skinchoice {(Appearance.SkinPath==skin.ResourcePath?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseSkin(skin))\u003ESkin @skin.Id.Replace(\u0022human\u0022,\u0022\u0022)\u003C/button\u003E}\u003C/div\u003E}\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003ESkin color\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EChoose a complexion independently of face and body. Native restores the selected skin\u0027s original color.\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E\u003Cbutton class=@($\u0022creatorbutton skinpalette {(SkinPalette==\u0022Human\u0022?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESkinPalette=\u0022Human\u0022)\u003EHuman\u003C/button\u003E\u003Cbutton class=@($\u0022creatorbutton skinpalette {(SkinPalette==\u0022Fantasy\u0022?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESkinPalette=\u0022Fantasy\u0022)\u003EFantasy \u0026amp; custom\u003C/button\u003E\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022colorchoices\u0022\u003E@foreach(var color in SkinPalette==\u0022Human\u0022?AppearanceCatalog.HumanSkinColors:AppearanceCatalog.FantasySkinColors){\u003Cbutton class=@($\u0022creatorbutton colorchoice skincolorchoice skincolor-{color.Id} {(Appearance.SkinColor==color.Color \u0026\u0026 Appearance.NormalizeSkinTone==color.NormalizeSkinTone?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseSkinColor(color.Color,color.NormalizeSkinTone)) tooltip=@color.Label\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n\r\n       @if(SkinPalette==\u0022Fantasy\u0022)\r\n\r\n       {\r\n\r\n        \u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003ECustom color\u003C/div\u003E\u003Cdiv class=\u0022skincolorpreview\u0022\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(Appearance.SkinColor)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@($\u0022RGB {MathF.Round(Appearance.SkinColorR*255)} \u00B7 {MathF.Round(Appearance.SkinColorG*255)} \u00B7 {MathF.Round(Appearance.SkinColorB*255)}\u0022)\u003C/span\u003E\u003C/div\u003E\r\n\r\n        @foreach(var channel in new[]{\u0022Red\u0022,\u0022Green\u0022,\u0022Blue\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@channel\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider skin-{channel.ToLowerInvariant()}\u0022) Value=@SkinChannel(channel) OnValueChanged=@((float value)=\u003ESetSkinChannel(channel,value)) Min=@(0f) Max=@(1f) Step=@(1f/255) ShowTextEntry=@false /\u003E\u003C/div\u003E}\r\n\r\n       }\r\n\r\n       \u003Cbutton class=\u0022creatorbutton quiet resetshape resetskincolor\u0022 onclick=@(()=\u003EChooseSkinColor(Color.White))\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003EReset skin color\u003C/button\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Makeup\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EMakeup \u0026amp; markings\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EMake it yours. Choose None to leave any makeup layer bare.\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E@foreach(var layer in MakeupCatalog.Layers){\u003Cbutton class=@($\u0022creatorbutton makeuplayer {(MakeupLayer==layer?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMakeupLayer(layer))\u003E@(layer==\u0022Tattoo\u0022?\u0022Markings\u0022:layer)\u003C/button\u003E}\u003C/div\u003E\r\n\r\n       @if(MakeupLayer==\u0022Tattoo\u0022)\r\n\r\n       {\r\n\r\n        \u003Cdiv class=\u0022markinglayers\u0022\u003E\u003Cdiv class=\u0022markinglist\u0022\u003E@for(var i=0;i\u003CAppearance.Makeup.Markings.Count;i\u002B\u002B){var index=i;var mark=Appearance.Makeup.Markings[i];\u003Cbutton class=@($\u0022creatorbutton markinglayer {(SelectedMarking==i?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESelectMarking(index))\u003E@($\u0022{i\u002B1} \u00B7 {MarkingCatalog.Style(mark.Style).Label}\u0022)\u003C/button\u003E}\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton addmarking\u0022 onclick=@AddMarking tooltip=\u0022Add an independent marking layer\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eadd\u003C/span\u003EAdd marking\u003C/button\u003E\u003C/div\u003E\r\n\r\n        @if(CurrentMarking is null)\r\n\r\n        {\u003Cdiv class=\u0022markingempty\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Ebrush\u003C/span\u003E\u003Cdiv class=\u0022fieldlabel\u0022\u003ENone\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EAdd a marking to choose its design, location and ink. Combine up to six.\u003C/div\u003E\u003C/div\u003E}\r\n\r\n        else\r\n\r\n        {\r\n\r\n         \u003Cdiv class=\u0022markingeditorheading\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet inspectmarking\u0022 onclick=@InspectMarking tooltip=\u0022Focus the preview on this marking\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Ecenter_focus_strong\u003C/span\u003E@MarkingCatalog.Region(CurrentMarking.Region).Label\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet deletemarking\u0022 onclick=@DeleteMarking tooltip=\u0022Delete this marking layer\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Edelete_outline\u003C/span\u003EDelete layer\u003C/button\u003E\u003C/div\u003E\r\n         \u003Cdiv class=\u0022markingsections\u0022\u003E@foreach(var section in new[]{\u0022Design\u0022,\u0022Placement\u0022,\u0022Ink\u0022}){\u003Cbutton class=@($\u0022creatorbutton markingsection {(MarkingSection==section?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EMarkingSection=section)\u003E@section\u003C/button\u003E}\u003C/div\u003E\r\n\r\n         @if(MarkingSection==\u0022Design\u0022)\r\n\r\n         {\r\n\r\n          \u003Cdiv class=\u0022markingdesigns\u0022\u003E@foreach(var style in MarkingCatalog.Styles){\u003Cbutton class=@($\u0022creatorbutton markingstyle style-{style.Id} {(CurrentMarking.Style==style.Id?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMarkingStyle(style.Id))\u003E\u003Cspan class=\u0022material-icons\u0022\u003E@MarkingSymbol(style.Id)\u003C/span\u003E\u003Cspan\u003E@style.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n\r\n          \u003Cdiv class=\u0022fieldlabel markinglocationlabel\u0022\u003ELocation\u003C/div\u003E\u003Cdiv class=\u0022markinglocations\u0022\u003E@foreach(var region in MarkingCatalog.Locations(CurrentMarking.Style)){\u003Cbutton class=@($\u0022creatorbutton markinglocation location-{region.Id} {(CurrentMarking.Region==region.Id?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMarkingRegion(region.Id))\u003E@region.Label\u003C/button\u003E}\u003C/div\u003E\r\n\r\n         }\r\n\r\n         else if(MarkingSection==\u0022Placement\u0022)\r\n\r\n         {\r\n\r\n          \u003Cdiv class=\u0022optiondescription\u0022\u003E@(MarkingCatalog.Style(CurrentMarking.Style).Band?\u0022This band wraps around the selected region. Slide it along the body and adjust its size and barb spacing.\u0022:\u0022Move, turn and resize this marking within the selected region.\u0022)\u003C/div\u003E\r\n\r\n          \u003Cdiv class=\u0022markingplacement\u0022\u003E@foreach(var control in MarkingCatalog.Style(CurrentMarking.Style).Band?new[]{\u0022Vertical\u0022,\u0022Scale\u0022,\u0022Spacing\u0022}:new[]{\u0022Horizontal\u0022,\u0022Vertical\u0022,\u0022Scale\u0022,\u0022Rotation\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@(control==\u0022Vertical\u0022 \u0026\u0026 MarkingCatalog.Style(CurrentMarking.Style).Band?\u0022Position along region\u0022:control==\u0022Scale\u0022?\u0022Size\u0022:control==\u0022Spacing\u0022?\u0022Barb spacing\u0022:control)\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider marking-{control.ToLowerInvariant()}\u0022) Value=@MarkingValue(control) OnValueChanged=@((float value)=\u003ESetMarkingValue(control,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E}\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton quiet resetmarkingplacement\u0022 onclick=@ResetMarkingPlacement\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003EReset placement\u003C/button\u003E\r\n\r\n         }\r\n\r\n        }\r\n\r\n       }\r\n\r\n       @if(MakeupLayer!=\u0022Tattoo\u0022 || CurrentMarking is not null \u0026\u0026 MarkingSection==\u0022Ink\u0022)\r\n\r\n       {\r\n\r\n        \u003Cdiv class=\u0022colorchoices\u0022\u003E\r\n\r\n         @if(MakeupLayer!=\u0022Tattoo\u0022){\u003Cbutton class=@($\u0022creatorbutton colorchoice makeupnone {(!CurrentMakeupActive?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@ChooseNoMakeup\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eblock\u003C/span\u003E\u003Cspan\u003ENone\u003C/span\u003E\u003C/button\u003E}\r\n\r\n         @foreach(var color in MakeupCatalog.Colors(MakeupLayer)){\u003Cbutton class=@($\u0022creatorbutton colorchoice makeupcolor makeup-{color.Id} {(CurrentMakeupActive \u0026\u0026 CurrentMakeup.Color==color.Color?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseMakeupColor(color.Color)) tooltip=@color.Label\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\r\n\r\n        \u003C/div\u003E\r\n\r\n        \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@(MakeupLayer==\u0022Eyeliner\u0022?\u0022Upper eyelid strength\u0022:\u0022Strength\u0022)\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(CurrentMakeup.Amount*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider makeupamount\u0022 Value=@CurrentMakeup.Amount OnValueChanged=@SetMakeupAmount Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n        @if(MakeupLayer==\u0022Eyeliner\u0022)\r\n        {\r\n         \u003Cdiv class=\u0022optiondescription\u0022\u003EUpper and lower liner share one color. Wings follow the upper liner strength; inner-corner liner is optional.\u003C/div\u003E\r\n         @foreach(var part in new[]{\u0022Lower eyelid\u0022,\u0022Inner corner\u0022})\r\n         {\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@part strength\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(EyelinerStrength(part)*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider eyeliner-{(part==\u0022Lower eyelid\u0022?\u0022lower\u0022:\u0022inner\u0022)}\u0022) Value=@EyelinerStrength(part) OnValueChanged=@((float value)=\u003ESetEyelinerStrength(part,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n         }\r\n         \u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003EWing\u003C/div\u003E\r\n         \u003Cdiv class=\u0022choicegrid\u0022\u003E@foreach(var wing in new[]{\u0022none\u0022,\u0022wing\u0022}){\u003Cbutton class=@($\u0022creatorbutton eyelinerwing {((wing==\u0022none\u0022)== (Appearance.Makeup.EyelinerWing==\u0022none\u0022)?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseEyelinerWing(wing))\u003E@(wing==\u0022none\u0022?\u0022No wing\u0022:\u0022Wing\u0022)\u003C/button\u003E}\u003C/div\u003E\r\n         @if(Appearance.Makeup.EyelinerWing!=\u0022none\u0022)\r\n         {\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing strength\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(Appearance.Makeup.EyelinerWingStrength*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing\u0022 Value=@Appearance.Makeup.EyelinerWingStrength OnValueChanged=@((float value)=\u003ESetEyelinerStrength(\u0022Wing\u0022,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing tip horizontal\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(Appearance.Makeup.WingTipHorizontal*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing-horizontal\u0022 Value=@Appearance.Makeup.WingTipHorizontal OnValueChanged=@((float value)=\u003ESetWingTip(false,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing tip vertical\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(Appearance.Makeup.WingTipVertical==0?\u0022Level\u0022:$\u0022{MathF.Round(MathF.Abs(Appearance.Makeup.WingTipVertical)*100)}% {(Appearance.Makeup.WingTipVertical\u003E0?\u0022up\u0022:\u0022down\u0022)}\u0022)\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing-vertical\u0022 Value=@Appearance.Makeup.WingTipVertical OnValueChanged=@((float value)=\u003ESetWingTip(true,value)) Min=@(-1f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n          \u003Cdiv class=\u0022skinchannel makeupstrength\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003EWing thickness\u003C/span\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(Appearance.Makeup.EyelinerWingThickness*100))%\u003C/span\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider eyeliner-wing-thickness\u0022 Value=@Appearance.Makeup.EyelinerWingThickness OnValueChanged=@SetWingThickness Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E\r\n         }\r\n        }\r\n\r\n        \u003Cbutton class=\u0022creatorbutton quiet makeupcustomtoggle\u0022 onclick=@(()=\u003ECustomMakeup=!CustomMakeup)\u003E\u003Cspan class=\u0022material-icons\u0022\u003Epalette\u003C/span\u003E@(CustomMakeup?\u0022Hide custom color\u0022:\u0022Custom color\u0022)\u003C/button\u003E\r\n\r\n        @if(CustomMakeup){\u003Cdiv class=\u0022makeupcustomgrid\u0022\u003E@foreach(var channel in new[]{\u0022Red\u0022,\u0022Green\u0022,\u0022Blue\u0022}){\u003Cdiv class=\u0022skinchannel\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E@channel\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider makeup-{channel.ToLowerInvariant()}\u0022) Value=@MakeupChannel(channel) OnValueChanged=@((float value)=\u003ESetMakeupChannel(channel,value)) Min=@(0f) Max=@(1f) Step=@(1f/255) ShowTextEntry=@false /\u003E\u003C/div\u003E}\u003C/div\u003E}\r\n\r\n       }\r\n\r\n       \u003Cdiv class=\u0022makeupresets\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet clearmakeup\u0022 onclick=@ClearMakeup\u003EClear all makeup \u0026amp; markings\u003C/button\u003E\u003C/div\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Clothing\u0022)\r\n      {\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EYour wardrobe\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003EClick an item to wear it, click again to remove it. Outfits replace tops and bottoms; jackets and accessories can be layered.\u003C/div\u003E\r\n       \u003Cbutton class=\u0022creatorbutton quiet browsecloud\u0022 onclick=@(()=\u003ECloudWardrobeOpen=!CloudWardrobeOpen)\u003E@(CloudWardrobeOpen?\u0022Close cloud browser\u0022:\u0022Browse cloud clothing \u0026 purchased cosmetics\u0022)\u003C/button\u003E\r\n       @if(CloudWardrobeOpen){\u003CCloudClothingBrowser Selected=@ChooseCloudClothing Appearance=@Appearance /\u003E}\r\n       \u003CTextEntry class=\u0022wardrobesearch\u0022 Value=@WardrobeSearch OnTextEdited=@((string value)=\u003E{WardrobeSearch=value;}) Placeholder=\u0022Search clothing\u0022 /\u003E\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E@foreach(var category in WardrobeCatalog.Categories){\u003Cbutton class=@($\u0022creatorbutton {(WardrobeCategory==category?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EWardrobeCategory=category)\u003E@category\u003C/button\u003E}\u003C/div\u003E\r\n       \u003Cdiv class=\u0022fieldlabel\u0022\u003EWearing @WardrobeCatalog.Paths(Appearance).Count() items\u003C/div\u003E\u003Cdiv class=\u0022choicegrid\u0022\u003E@foreach(var path in WardrobeCatalog.Paths(Appearance).ToArray()){var worn=ClothingResources.Available.FirstOrDefault(x=\u003Ex.Path==path);@if(worn is not null){\u003Cbutton class=\u0022creatorbutton chosen\u0022 onclick=@(()=\u003EChooseClothing(worn))\u003E@($\u0022{worn.Name} \u00D7\u0022)\u003C/button\u003E}}\u003C/div\u003E\r\n       \u003Cdiv class=\u0022fieldlabel\u0022\u003E@($\u0022{WardrobeCategory} \u00B7 {VisibleWardrobe.Count()} choices\u0022)\u003C/div\u003E\u003Cdiv class=\u0022choicegrid thumbnailgrid\u0022\u003E@foreach(var item in VisibleWardrobe){\u003Cbutton class=@($\u0022creatorbutton wardrobechoice {((WardrobeCatalog.Paths(Appearance).Contains(item.Path)||Appearance.HairPath==item.Path||Appearance.FacialHairPath==item.Path)?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseClothing(item))\u003E\u003Cdiv class=\u0022thumbnailframe\u0022\u003E@if(ClothingThumbnails.For(item.Path) is { Length: \u003E 0 } icon){\u003Cdiv class=\u0022itemthumbnail\u0022 style=@($\u0022background-image: url(\u0027{icon}\u0027);\u0022)\u003E\u003C/div\u003E}else{\u003Cspan class=\u0022material-icons thumbnailplaceholder\u0022\u003Eblock\u003C/span\u003E}\u003C/div\u003E\u003Cspan class=\u0022itemlabel\u0022\u003E@item.Name\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n       @if(!VisibleWardrobe.Any()){\u003Cdiv class=\u0022optiondescription\u0022\u003ENo clothing matches your search in this category.\u003C/div\u003E}\r\n       \u003Cdiv class=\u0022choicegrid\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@ClearClothing\u003ERemove clothing\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@ResetClothing\u003EDefault outfit\u003C/button\u003E\u003C/div\u003E\r\n      }\r\n      else if(Tab==\u0022Hair\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003EHair\u003C/div\u003E\u003Cdiv class=\u0022choicegrid thumbnailgrid\u0022\u003E@foreach(var hair in AvailableHair){\u003Cbutton class=@($\u0022creatorbutton hairchoice {(Appearance.HairPath==hair.ResourcePath?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseHair(hair))\u003E\u003Cdiv class=\u0022thumbnailframe\u0022\u003E@if(ClothingThumbnails.For(hair.ResourcePath) is { Length: \u003E 0 } icon){\u003Cdiv class=\u0022itemthumbnail\u0022 style=@($\u0022background-image: url(\u0027{icon}\u0027);\u0022)\u003E\u003C/div\u003E}else{\u003Cspan class=\u0022material-icons thumbnailplaceholder\u0022\u003Eblock\u003C/span\u003E}\u003C/div\u003E\u003Cspan class=\u0022itemlabel\u0022\u003E@hair.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003EHair color\u003C/div\u003E\u003Cdiv class=\u0022colorchoices\u0022\u003E@foreach(var color in AppearanceCatalog.Colors){\u003Cbutton class=@($\u0022creatorbutton colorchoice {(SameColor(color.Color)?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseColor(color)) tooltip=@color.Label\u003E\u003Cdiv class=\u0022colorswatch\u0022 style=@($\u0022background-color: {ColorCss(color.Color)};\u0022)\u003E\u003C/div\u003E\u003Cspan\u003E@color.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\u003Cdiv class=\u0022fieldlabel colorlabel\u0022\u003EFacial hair\u003C/div\u003E\u003Cdiv class=\u0022choicegrid thumbnailgrid\u0022\u003E@foreach(var facial in AvailableFacialHair.Where(x=\u003Ex.BodyType==\u0022\u0022||x.BodyType==BodyType)){\u003Cbutton class=@($\u0022creatorbutton facialchoice {(Appearance.FacialHairPath==facial.ResourcePath?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChooseFacial(facial))\u003E\u003Cdiv class=\u0022thumbnailframe\u0022\u003E@if(ClothingThumbnails.For(facial.ResourcePath) is { Length: \u003E 0 } icon){\u003Cdiv class=\u0022itemthumbnail\u0022 style=@($\u0022background-image: url(\u0027{icon}\u0027);\u0022)\u003E\u003C/div\u003E}else{\u003Cspan class=\u0022material-icons thumbnailplaceholder\u0022\u003Eblock\u003C/span\u003E}\u003C/div\u003E\u003Cspan class=\u0022itemlabel\u0022\u003E@facial.Label\u003C/span\u003E\u003C/button\u003E}\u003C/div\u003E\r\n\r\n      }\r\n\r\n      else if(Tab==\u0022Shape\u0022)\r\n\r\n      {\r\n\r\n       \u003Cdiv class=\u0022optionheading\u0022\u003ERefine your look\u003C/div\u003E\u003Cdiv class=\u0022optiondescription\u0022\u003ENeutral is 50%; ear taper and belly start at 0%.\u003C/div\u003E\r\n\r\n       \u003Cdiv class=\u0022shapecategories\u0022\u003E@foreach(var category in AppearanceCatalog.Sliders.Select(x=\u003Ex.Group).Distinct()){\u003Cbutton class=@($\u0022creatorbutton shapecategory {(ShapeCategory==category?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003EChangeShapeCategory(category))\u003E@category\u003C/button\u003E}\u003C/div\u003E\r\n\r\n       @if(ShapeCategory==\u0022Body\u0022 \u0026\u0026 Appearance.BaseShape is not null)\r\n       {\r\n        @foreach(var breast in new[]{true,false}){\u003Cdiv class=\u0022shaperow\u0022\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@(breast?\u0022Breast size\u0022:\u0022Bulge size\u0022)\u003C/span\u003E\u003Cspan\u003E@MathF.Round((breast?1-Appearance.BaseShape.MaleBody:Appearance.BaseShape.MaleBody)*100)% effect\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@(()=\u003ESetBodyFeature(breast,.5f))\u003EReset\u003C/button\u003E\u003C/div\u003E\u003CSliderControl class=@($\u0022shapeslider {(breast?\u0022breast\u0022:\u0022bulge\u0022)}-size\u0022) Value=@(breast?Appearance.BaseShape.BreastSize:Appearance.BaseShape.BulgeSize) OnValueChanged=@((float value)=\u003ESetBodyFeature(breast,value)) Min=@(0f) Max=@(1f) Step=@(.01f) ShowTextEntry=@false /\u003E\u003C/div\u003E}\r\n       }\r\n       \u003Cdiv class=\u0022shapegrid\u0022\u003E@foreach(var slider in AppearanceCatalog.Sliders.Where(x=\u003Ex.Group==ShapeCategory \u0026\u0026 (Appearance.BaseShape is null || x.Id!=\u0022bodyfeature\u0022)))\r\n\r\n       {\r\n\r\n        \u003Cdiv class=@($\u0022shaperow shape-{slider.Id}\u0022)\u003E\u003Cdiv class=\u0022shapelabel\u0022\u003E\u003Cspan\u003E@slider.LabelFor(Appearance)\u003C/span\u003E\u003Cdiv class=\u0022shapeactions\u0022\u003E\u003Cspan class=\u0022shapevalue\u0022\u003E@(MathF.Round(slider.Get(Appearance)*100))%\u003C/span\u003E\u003Cbutton class=\u0022creatorbutton quiet resetslider\u0022 onclick=@(()=\u003ESetShape(slider,slider.Neutral)) tooltip=@($\u0022Reset {slider.LabelFor(Appearance).ToLowerInvariant()} to {MathF.Round(slider.Neutral*100)}%\u0022)\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003E\u003C/button\u003E\u003C/div\u003E\u003C/div\u003E\u003CSliderControl class=\u0022shapeslider\u0022 Value=@slider.Get(Appearance) OnValueChanged=@((float value)=\u003ESetShape(slider,value)) Min=@slider.Min Max=@slider.Max Step=@(0.01f) ShowTextEntry=@false /\u003E@if(slider.Id==\u0022columella\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003EWidth between the nostrils\u003C/div\u003E}@if(slider.Id==\u0022philtrum\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003EWidth between nose and upper lip\u003C/div\u003E}@if(slider.Id==\u0022eartaper\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ENormal \u00B7 Pointed\u003C/div\u003E}@if(slider.Id==\u0022fitness\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ESoft \u00B7 Neutral \u00B7 Muscular\u003C/div\u003E}@if(slider.Id==\u0022nostrils\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003EWidth of the nostril wings\u003C/div\u003E}@if(slider.Id==\u0022nostrilfullness\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ESmaller \u00B7 Neutral \u00B7 Fuller wings\u003C/div\u003E}@if(slider.Id==\u0022noseheight\u0022 || slider.Id==\u0022chinheight\u0022){\u003Cdiv class=\u0022fitnesshint\u0022\u003ELower \u00B7 Neutral \u00B7 Higher\u003C/div\u003E}\u003C/div\u003E\r\n\r\n       }\u003C/div\u003E\r\n\r\n       \u003Cbutton class=\u0022creatorbutton quiet resetshape\u0022 onclick=@ResetShapes\u003EReset body \u0026amp; face\u003C/button\u003E\r\n\r\n      }\r\n\r\n      @if(ExtraTabs.Contains(Tab) \u0026\u0026 ExtraContent is not null){@ExtraContent(this)}\r\n\r\n     \u003C/div\u003E\r\n\r\n     \u003Cdiv class=\u0022creatorutility\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@Randomize\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eshuffle\u003C/span\u003ERandomize\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet\u0022 onclick=@Reset\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erestart_alt\u003C/span\u003EReset appearance\u003C/button\u003E\u003C/div\u003E\r\n\r\n    \u003C/div\u003E\r\n\r\n    \u003Cdiv class=\u0022creatorpresentation\u0022\u003E\u003Cdiv class=\u0022previewheading\u0022\u003E\u003Cdiv class=\u0022previewname\u0022\u003E@(string.IsNullOrWhiteSpace(CharacterName)?\u0022Your character\u0022:CharacterName.Trim())\u003C/div\u003E\u003Cdiv class=\u0022previewcaption\u0022\u003E@(PreviewPan.Length\u003E0.01f || MathF.Abs(PreviewPitch)\u003E0.01f?\u0022Free inspection\u0022:Zoom\u003ECharacterCreatorPreview.FaceZoom\u002B0.001f?\u0022Face detail\u0022:Portrait?\u0022Face preview\u0022:\u0022Character preview\u0022)\u003C/div\u003E\u003C/div\u003E\u003CCharacterCreatorPreview @ref=Preview class=\u0022mainpreview\u0022 Hero=@true Appearance=@Appearance Portrait=@Portrait Zoom=@Zoom Yaw=@Yaw Pitch=@PreviewPitch PitchChanged=@SetPitch PanOffset=@PreviewPan PanChanged=@SetPan Interactive=@(!LoadingCatalog) OrbitChanged=@SetYaw ZoomChanged=@SetZoom ShowClothing=@ShowClothing /\u003E\u003Cdiv class=\u0022previewcontrols\u0022\u003E\u003Cdiv class=\u0022previewviewrow\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet rotateleft\u0022 onclick=@(()=\u003ERotate(-30)) tooltip=\u0022Turn left\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erotate_left\u003C/span\u003E\u003C/button\u003E\u003Cbutton class=@($\u0022creatorbutton bodyview {(Zoom==0?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESetView(0))\u003EFull body\u003C/button\u003E\u003Cbutton class=@($\u0022creatorbutton previewzoom {(MathF.Abs(Zoom-CharacterCreatorPreview.FaceZoom)\u003C0.001f?\u0022chosen\u0022:\u0022\u0022)}\u0022) onclick=@(()=\u003ESetView(CharacterCreatorPreview.FaceZoom))\u003EFace view\u003C/button\u003E\u003Cbutton class=\u0022creatorbutton quiet rotateright\u0022 onclick=@(()=\u003ERotate(30)) tooltip=\u0022Turn right\u0022\u003E\u003Cspan class=\u0022material-icons\u0022\u003Erotate_right\u003C/span\u003E\u003C/button\u003E\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton quiet clothingtoggle\u0022 onclick=@ToggleClothing\u003E\u003Cspan class=\u0022material-icons\u0022\u003E@(ShowClothing?\u0022visibility_off\u0022:\u0022visibility\u0022)\u003C/span\u003E@(ShowClothing?\u0022Hide clothing\u0022:\u0022Show clothing\u0022)\u003C/button\u003E\u003C/div\u003E\u003Cdiv class=\u0022previewzoomrow\u0022\u003E\u003Cspan\u003EZoom\u003C/span\u003E\u003CSliderControl class=\u0022customzoom\u0022 Value=@Zoom OnValueChanged=@SetZoom Min=@(0f) Max=@CharacterCreatorPreview.MaxZoom Step=@(0.005f) ShowTextEntry=@false /\u003E\u003C/div\u003E\u003Cdiv class=\u0022previewrecenter\u0022\u003E\u003Cbutton class=\u0022creatorbutton quiet recenterpreview\u0022 onclick=@RecenterPreview\u003E\u003Cspan class=\u0022material-icons\u0022\u003Ecenter_focus_strong\u003C/span\u003ERecenter preview\u003C/button\u003E\u003C/div\u003E\u003Cdiv class=\u0022previewhint\u0022\u003ELeft drag: turn \u00B7 Right: tilt \u00B7 Middle: pan \u00B7 Scroll: zoom\u003C/div\u003E\u003C/div\u003E\r\n\r\n   \u003C/div\u003E\r\n\r\n   \u003Cdiv class=\u0022creatorfooter\u0022\u003E\u003Cdiv class=\u0022creatoridentity\u0022\u003E\u003Cdiv class=\u0022identityfields\u0022\u003E\u003Cdiv class=\u0022namefield\u0022\u003E\u003Cdiv class=\u0022fieldlabel\u0022\u003ECharacter name\u003C/div\u003E\u003CTextEntry class=\u0022charactername\u0022 Value=@CharacterName OnTextEdited=@SetCharacterName MaxLength=@(40) Placeholder=\u0022Your character\u0027s name\u0022 /\u003E\u003C/div\u003E@FooterContent\u003C/div\u003E\u003C/div\u003E\u003Cdiv class=\u0022creatorfeedback\u0022\u003E@(Feedback??(LoadingCatalog?\u0022Loading character options\u2026\u0022:Preview?.Error)??(\u0022\u0022))\u003C/div\u003E\u003Cbutton class=\u0022creatorbutton primary startplaythrough\u0022 disabled=@Disabled onclick=@Start\u003E\u003Cspan class=\u0022material-icons\u0022\u003Eplay_arrow\u003C/span\u003E@(SaveLabel)\u003C/button\u003E\u003C/div\u003E\r\n\r\n  \u003C/div\u003E\r\n\r\n }\r\n\r\n\u003C/root\u003E\r\n\r\n@code {\r\n\t/// \u003Csummary\u003EConsumer-specific pages; built-in categories remain owned by Character Kit.\u003C/summary\u003E\r\n\tpublic IReadOnlyList\u003Cstring\u003E ExtraTabs { get; set; } = Array.Empty\u003Cstring\u003E();\r\n\tpublic IReadOnlyList\u003Cstring\u003E HiddenTabs { get; set; } = Array.Empty\u003Cstring\u003E();\r\n\tpublic RenderFragment\u003CCharacterCreatorPanel\u003E ExtraContent { get; set; }\r\n\tpublic RenderFragment FooterContent { get; set; }\r\n\tpublic Action StopPreview { get; set; }\r\n\tpublic bool Disabled { get; set; }\r\n\tpublic string ProductLabel { get; set; } = \u0022Character Editor\u0022;\r\n\tpublic string LocationLabel { get; set; } = \u0022Studio\u0022;\r\n\tpublic string Title { get; set; } = \u0022Create your character\u0022;\r\n\tpublic CharacterAppearanceData EditableAppearance =\u003E Appearance;\r\n\tpublic SkinnedModelRenderer PreviewRenderer =\u003E Preview?.Body;\r\n\tpublic string SelectedTab =\u003E Tab;\r\n\tpublic string SaveLabel { get; set; } = \u0022Save character\u0022;\r\n\tpublic Func\u003Cstring, CharacterAppearanceData, string\u003E Saved { get; set; }\r\n\tpublic Action Closed { get; set; }\r\n\tpublic CharacterPreset InitialPreset { get; set; }\r\n\r\n\tCharacterPreset loadedPreset;\r\n\tpublic CharacterAppearanceData DraftAppearance =\u003E Appearance.DeepCopy();\r\n\tpublic string DraftCharacterName =\u003E CharacterName;\r\n\r\n\tpublic void LoadDraft(string name, CharacterAppearanceData data)\r\n\t{\r\n\t\tCharacterName = name;\r\n\t\tAppearance = data.DeepCopy();\r\n\t\tChosenPreset = null;\r\n\t\tTab = \u0022Face \u0026 body\u0022;\r\n\t}\r\n\r\n\tpublic bool AppearanceReady =\u003E CatalogReady \u0026\u0026 Preview?.Loading != true \u0026\u0026 CharacterAppearance.ValidateForApply(Appearance)is null;\r\n\r\n\tIReadOnlyList\u003Cstring\u003E Tabs =\u003E new[]\r\n\t{\r\n\t\t\u0022Presets\u0022,\r\n\t\t\u0022Skin\u0022,\r\n\t\t\u0022Face \u0026 body\u0022,\r\n\t\t\u0022Photos\u0022,\r\n\t\t\u0022Hair\u0022,\r\n\t\t\u0022Clothing\u0022,\r\n\t\t\u0022Shape\u0022,\r\n\t\t\u0022Makeup\u0022\r\n\t}.Where(x =\u003E !HiddenTabs.Contains(x)).Concat(ExtraTabs).Distinct().ToArray();\r\n\r\n\tbool CloudWardrobeOpen;\r\n\tstring WardrobeSearch = \u0022\u0022, WardrobeCategory = \u0022Tops\u0022;\r\n\tIEnumerable\u003CWardrobeItem\u003E VisibleWardrobe =\u003E ClothingResources.Available.Where(x =\u003E x.Category == WardrobeCategory \u0026\u0026 (string.IsNullOrWhiteSpace(WardrobeSearch) || x.Name.Contains(WardrobeSearch, StringComparison.OrdinalIgnoreCase))).OrderBy(x =\u003E x.Name);\r\n\r\n\tvoid ChooseCloudClothing(WardrobeItem item)\r\n\t{\r\n\t\tWardrobeCategory = item.Category;\r\n\t\tWardrobeSearch = \u0022\u0022;\r\n\t\tChooseClothing(item);\r\n\t\tAppearance.Version = 2;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t\tStateHasChanged();\r\n\t}\r\n\r\n\tvoid ChooseClothing(WardrobeItem item)\r\n\t{\r\n\t\tvar clothing = ClothingResources.Get(item.Path);\r\n\t\tif (clothing is not null \u0026\u0026 ClothingResources.IsHair(clothing))\r\n\t\t\tAppearance.HairPath = Appearance.HairPath == item.Path || ClothingResources.Get(Appearance.HairPath) == clothing ? \u0022\u0022 : item.Path;\r\n\t\telse if (clothing is not null \u0026\u0026 ClothingResources.IsFacialHair(clothing))\r\n\t\t\tAppearance.FacialHairPath = Appearance.FacialHairPath == item.Path || ClothingResources.Get(Appearance.FacialHairPath) == clothing ? \u0022\u0022 : item.Path;\r\n\t\telse\r\n\t\t\tWardrobeCatalog.Toggle(Appearance, item);\r\n\t\tAppearance.Version = 2;\r\n\t\tShowClothing = true;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ClearClothing()\r\n\t{\r\n\t\tAppearance.ClothingPaths = Array.Empty\u003Cstring\u003E();\r\n\t\tAppearance.Version = 2;\r\n\t\tShowClothing = true;\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ResetClothing()\r\n\t{\r\n\t\tAppearance.ClothingPaths = WardrobeCatalog.DefaultPaths.ToArray();\r\n\t\tAppearance.Version = 2;\r\n\t\tShowClothing = true;\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tCharacterAppearanceData Appearance = AppearanceCatalog.CreateDefault();\r\n\tstring CharacterName = \u0022Rowan\u0022, PlaythroughName = \u0022A new adventure\u0022, Tab = \u0022Presets\u0022, ShapeCategory = \u0022Body\u0022, ChosenPreset = \u0022traveller\u0022, Feedback;\r\n\tstring BlendRegion = \u0022overall\u0022;\r\n\tbool CatalogReady, LoadingCatalog;\r\n\tstring SkinPalette = \u0022Human\u0022;\r\n\tstring MakeupLayer = \u0022Lipstick\u0022;\r\n\tbool CustomMakeup;\r\n\tint SelectedMarking;\r\n\tstring MarkingSection = \u0022Design\u0022;\r\n\tCharacterMarkingData CurrentMarking =\u003E Appearance.Makeup.Markings.Count == 0 ? null : Appearance.Makeup.Markings[Math.Clamp(SelectedMarking, 0, Appearance.Makeup.Markings.Count - 1)];\r\n\r\n\tMakeupLayerData CurrentMakeup =\u003E MakeupLayer == \u0022Tattoo\u0022 ? CurrentMarking?.Ink ?? Appearance.Makeup.Tattoo : MakeupCatalog.Layer(Appearance.Makeup, MakeupLayer);\r\n\r\n\tvoid ChooseMakeupLayer(string layer)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tMakeupLayer = layer;\r\n\t\tif (layer == \u0022Tattoo\u0022)\r\n\t\t\tAppearance.Makeup.EditMarkings();\r\n\t}\r\n\r\n\tvoid SelectMarking(int index)\r\n\t{\r\n\t\tif (LoadingCatalog || index \u003C 0 || index \u003E= Appearance.Makeup.Markings.Count)\r\n\t\t\treturn;\r\n\t\tSelectedMarking = index;\r\n\t}\r\n\r\n\tvoid AddMarking()\r\n\t{\r\n\t\tif (LoadingCatalog || Appearance.Makeup.Markings.Count \u003E= MarkingCatalog.Maximum)\r\n\t\t{\r\n\t\t\tFeedback = \u0022You can combine up to six marking layers.\u0022;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tvar marking = new CharacterMarkingData();\r\n\t\tMarkingCatalog.ResetPosition(marking);\r\n\t\tAppearance.Makeup.Markings.Add(marking);\r\n\t\tSelectedMarking = Appearance.Makeup.Markings.Count - 1;\r\n\t\tMarkingSection = \u0022Design\u0022;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid DeleteMarking()\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tAppearance.Makeup.Markings.Remove(CurrentMarking);\r\n\t\tSelectedMarking = Math.Clamp(SelectedMarking, 0, Math.Max(0, Appearance.Makeup.Markings.Count - 1));\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tstring MarkingSymbol(string style) =\u003E style switch\r\n\t{\r\n\t\t\u0022crescent\u0022 =\u003E \u0022brightness_2\u0022,\r\n\t\t\u0022teardrop\u0022 =\u003E \u0022water_drop\u0022,\r\n\t\t\u0022star\u0022 =\u003E \u0022star_outline\u0022,\r\n\t\t\u0022rune\u0022 =\u003E \u0022gesture\u0022,\r\n\t\t\u0022barbed-band\u0022 =\u003E \u0022link\u0022,\r\n\t\t_ =\u003E \u0022block\u0022\r\n\t};\r\n\tvoid ChooseMarkingStyle(string style)\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tMarkingCatalog.ChangeStyle(CurrentMarking, style);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ChooseMarkingRegion(string region)\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tMarkingCatalog.ChangeRegion(CurrentMarking, region);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tfloat MarkingValue(string control) =\u003E control switch\r\n\t{\r\n\t\t\u0022Horizontal\u0022 =\u003E CurrentMarking.Horizontal,\r\n\t\t\u0022Vertical\u0022 =\u003E CurrentMarking.Vertical,\r\n\t\t\u0022Scale\u0022 =\u003E CurrentMarking.Scale,\r\n\t\t\u0022Spacing\u0022 =\u003E CurrentMarking.Spacing,\r\n\t\t_ =\u003E CurrentMarking.Rotation\r\n\t};\r\n\tvoid SetMarkingValue(string control, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (MarkingCatalog.Style(CurrentMarking.Style).Band \u0026\u0026 control is not \u0022Vertical\u0022 and not \u0022Scale\u0022 and not \u0022Spacing\u0022)\r\n\t\t\treturn;\r\n\t\tif (control == \u0022Horizontal\u0022)\r\n\t\t\tCurrentMarking.Horizontal = value;\r\n\t\telse if (control == \u0022Vertical\u0022)\r\n\t\t\tCurrentMarking.Vertical = value;\r\n\t\telse if (control == \u0022Scale\u0022)\r\n\t\t\tCurrentMarking.Scale = value;\r\n\t\telse if (control == \u0022Spacing\u0022)\r\n\t\t\tCurrentMarking.Spacing = value;\r\n\t\telse if (control == \u0022Rotation\u0022)\r\n\t\t\tCurrentMarking.Rotation = value;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ResetMarkingPlacement()\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tMarkingCatalog.ResetPosition(CurrentMarking);\r\n\t\tCurrentMarking.Scale = .5f;\r\n\t\tCurrentMarking.Rotation = .5f;\r\n\t\tCurrentMarking.Spacing = .5f;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid InspectMarking()\r\n\t{\r\n\t\tif (LoadingCatalog || CurrentMarking is null)\r\n\t\t\treturn;\r\n\t\tvar region = MarkingCatalog.Region(CurrentMarking.Region);\r\n\t\tvar center = MarkingCatalog.Focus(CurrentMarking);\r\n\t\tvar outward = MarkingCatalog.Outward(CurrentMarking);\r\n\t\tYaw = -MathF.Atan2(outward.y, outward.x) * 180 / MathF.PI;\r\n\t\tSetZoom(region.Head ? 1.55f : 1.15f);\r\n\t\tvar rotated = Rotation.FromYaw(Yaw) * center;\r\n\t\tvar targetHeight = 62 \u002B 1.5f * MathF.Max(0, Zoom - 1);\r\n\t\tPreviewPan = new(rotated.x, rotated.y, rotated.z - targetHeight);\r\n\t\tPreviewPitch = 0;\r\n\t}\r\n\r\n\tvoid ChangedCosmetics()\r\n\t{\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseNoMakeup()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tCurrentMakeup.Amount = 0;\r\n\t\tif (MakeupLayer == \u0022Eyeliner\u0022) MakeupCatalog.Reset(Appearance.Makeup, \u0022Eyeliner\u0022);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ChooseMakeupColor(Color color)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tCurrentMakeup.Color = color;\r\n\t\tif (!CurrentMakeupActive)\r\n\t\t\tCurrentMakeup.Amount = MakeupLayer == \u0022Eyeliner\u0022 ? 1f : .65f;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid SetMakeupAmount(float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tCurrentMakeup.Amount = Math.Clamp(value, 0, 1);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tbool CurrentMakeupActive =\u003E MakeupLayer == \u0022Eyeliner\u0022 ? Appearance.Makeup.EyelinerActive : CurrentMakeup.Amount \u003E 0;\r\n\r\n\tfloat EyelinerStrength(string part) =\u003E part == \u0022Lower eyelid\u0022 ? Appearance.Makeup.EyelinerLowerStrength : part == \u0022Wing\u0022 ? Appearance.Makeup.EyelinerWingStrength : Appearance.Makeup.EyelinerInnerStrength;\r\n\tvoid SetEyelinerStrength(string part, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value)) return;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (part == \u0022Lower eyelid\u0022) Appearance.Makeup.EyelinerLowerStrength = value;\r\n\t\telse if (part == \u0022Wing\u0022) Appearance.Makeup.EyelinerWingStrength = value;\r\n\t\telse Appearance.Makeup.EyelinerInnerStrength = value;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid PrepareAdjustableWing()\r\n\t{\r\n\t\tvar makeup = Appearance.Makeup;\r\n\t\tmakeup.EyelinerWingHorizontal = makeup.WingTipHorizontal;\r\n\t\tmakeup.EyelinerWingVertical = makeup.WingTipVertical;\r\n\t\tmakeup.EyelinerWing = \u0022wing\u0022;\r\n\t}\r\n\r\n\tvoid SetWingTip(bool vertical, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value)) return;\r\n\t\tPrepareAdjustableWing();\r\n\t\tif (vertical) Appearance.Makeup.EyelinerWingVertical = Math.Clamp(value, -1, 1);\r\n\t\telse Appearance.Makeup.EyelinerWingHorizontal = Math.Clamp(value, 0, 1);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid SetWingThickness(float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value)) return;\r\n\t\tPrepareAdjustableWing();\r\n\t\tAppearance.Makeup.EyelinerWingThickness = Math.Clamp(value, 0, 1);\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ChooseEyelinerWing(string wing)\r\n\t{\r\n\t\tif (LoadingCatalog) return;\r\n\t\tif (wing == \u0022wing\u0022) PrepareAdjustableWing();\r\n\t\telse Appearance.Makeup.EyelinerWing = \u0022none\u0022;\r\n\t\tif (wing != \u0022none\u0022 \u0026\u0026 CurrentMakeup.Amount == 0) CurrentMakeup.Amount = 1;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tfloat MakeupChannel(string channel) =\u003E channel == \u0022Red\u0022 ? CurrentMakeup.Red : channel == \u0022Green\u0022 ? CurrentMakeup.Green : CurrentMakeup.Blue;\r\n\tvoid SetMakeupChannel(string channel, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (channel == \u0022Red\u0022)\r\n\t\t\tCurrentMakeup.Red = value;\r\n\t\telse if (channel == \u0022Green\u0022)\r\n\t\t\tCurrentMakeup.Green = value;\r\n\t\telse\r\n\t\t\tCurrentMakeup.Blue = value;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tvoid ClearMakeup()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.Makeup = new();\r\n\t\tSelectedMarking = 0;\r\n\t\tChangedCosmetics();\r\n\t}\r\n\r\n\tbool Portrait = true, ShowClothing = true;\r\n\tfloat Yaw, Zoom = CharacterCreatorPreview.FaceZoom;\r\n\tCharacterCreatorPreview Preview;\r\n\tVector3 PreviewPan;\r\n\tfloat PreviewPitch;\r\n\tstring BodyType =\u003E AppearanceCatalog.IsFemale(Appearance) ? \u0022female\u0022 : \u0022male\u0022;\r\n\r\n\tIEnumerable\u003CAppearanceOption\u003E AvailableHair =\u003E AppearanceCatalog.Hair.Concat(ClothingResources.Available.Where(x=\u003Ex.Category==\u0022Hair\u0022).Select(x=\u003Enew AppearanceOption(x.Path,x.Name,x.Path))).DistinctBy(x=\u003Ex.ResourcePath);\r\n IEnumerable\u003CAppearanceOption\u003E AvailableFacialHair =\u003E AppearanceCatalog.FacialHair.Concat(ClothingResources.Available.Where(x=\u003Ex.Category==\u0022Facial hair\u0022).Select(x=\u003Enew AppearanceOption(x.Path,x.Name,x.Path))).DistinctBy(x=\u003Ex.ResourcePath);\r\n string HairLabel =\u003E AppearanceCatalog.Hair.FirstOrDefault(x =\u003E x.ResourcePath == Appearance.HairPath)?.Label ?? \u0022Hair\u0022;\r\n\r\n\tstring FaceLabel(AppearanceOption option) =\u003E option.Label.Replace(\u0022 \u00B7 male\u0022, \u0022\u0022).Replace(\u0022 \u00B7 female\u0022, \u0022\u0022);\r\n\tstring ColorCss(Color color) =\u003E $\u0022rgb({(int)(color.r * 255)},{(int)(color.g * 255)},{(int)(color.b * 255)})\u0022;\r\n\tvoid SetCharacterName(string value)\r\n\t{\r\n\t\tCharacterName = value;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid SetPlaythroughName(string value)\r\n\t{\r\n\t\tPlaythroughName = value;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tbool SameColor(Color color) =\u003E MathF.Abs(Appearance.HairColorR - color.r) \u003C 0.001f \u0026\u0026 MathF.Abs(Appearance.HairColorG - color.g) \u003C 0.001f \u0026\u0026 MathF.Abs(Appearance.HairColorB - color.b) \u003C 0.001f;\r\n\tvoid ChangeTab(string tab)\r\n\t{\r\n\t\tif (tab != Tab)\n\t\t\tStopVoicePreview();\r\n\t\tTab = tab;\r\n\t\tif (tab == \u0022Clothing\u0022)\r\n\t\t\tSetZoom(0);\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid StopVoicePreview()\r\n\t{\r\n\t\tStopPreview?.Invoke();\r\n\t}\r\n\r\n\tvoid ChangeShapeCategory(string category)\r\n\t{\r\n\t\tShapeCategory = category;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ApplyPreset(string id)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tvar voice = Appearance.VoiceId;\r\n\t\tbool unified = Appearance.BaseShape is not null;\r\n\t\tAppearance = AppearanceCatalog.ApplyPreset(id);\r\n\t\tif (unified)\r\n\t\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tAppearance.VoiceId = voice;\r\n\t\tChosenPreset = id;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid EnableBlending()\r\n\t{\r\n\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid AddPhotoFace(PhotoFace face)\r\n\t{\r\n\t\tif (!face.Valid)\r\n\t\t\treturn;\r\n\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tif (Appearance.BaseShape.PhotoFaces.Count \u003E= 4)\r\n\t\t\treturn;\r\n\t\tArray.Fill(Appearance.BaseShape.HeadWeights, 0f);\r\n\t\tforeach (var old in Appearance.BaseShape.PhotoFaces)\r\n\t\t\told.Influence = 0;\r\n\t\tAppearance.BaseShape.PhotoFaces.Add(face);\r\n\t\tChosenPreset = null;\r\n\t\tTab = \u0022Face \u0026 body\u0022;\r\n\t\tBlendRegion = \u0022overall\u0022;\r\n\t\tSetView(CharacterCreatorPreview.FaceZoom);\r\n\t\tFeedback = \u0022Custom base added. Blend it here or refine it in Shape, then save your character.\u0022;\r\n\t}\r\n\r\n\tFaceRegionBlend Region(bool create = false)\r\n\t{\r\n\t\tif (BlendRegion == \u0022overall\u0022)\r\n\t\t\treturn null;\r\n\t\tif (!Appearance.BaseShape.Regions.TryGetValue(BlendRegion, out var value) \u0026\u0026 create)\r\n\t\t{\r\n\t\t\tvalue = new()\r\n\t\t\t{\r\n\t\t\t\tOverallInfluence = .5f\r\n\t\t\t};\r\n\t\t\tAppearance.BaseShape.Regions.Add(BlendRegion, value);\r\n\t\t}\r\n\r\n\t\treturn value;\r\n\t}\r\n\r\n\tfloat OverallWeight =\u003E Region()?.OverallInfluence ?? .5f;\r\n\r\n\tvoid SetOverallWeight(float value)\r\n\t{\r\n\t\tif (!float.IsFinite(value) || BlendRegion == \u0022overall\u0022)\r\n\t\t\treturn;\r\n\t\tRegion(true).OverallInfluence = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tfloat HeadWeight(int index) =\u003E BlendRegion == \u0022overall\u0022 ? Appearance.BaseShape.HeadWeights[index] : Region()?.HeadWeights[index] ?? 0;\r\n\tfloat PhotoWeight(PhotoFace face) =\u003E BlendRegion == \u0022overall\u0022 ? face.Influence : Region()?.PhotoWeights.GetValueOrDefault(face.Id) ?? 0;\r\n\tstring BlendWeightLabel(int index)\r\n\t{\r\n\t\tfloat share = FaceRegions.NativeShare(Appearance.BaseShape, index, BlendRegion);\r\n\t\treturn $\u0022{MathF.Round(HeadWeight(index) * 100)} influence \u00B7 {MathF.Round(share * 100)}% of blend\u0022;\r\n\t}\r\n\r\n\tvoid SetHeadWeight(int index, float value)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\t(BlendRegion == \u0022overall\u0022 ? Appearance.BaseShape.HeadWeights : Region(true).HeadWeights)[index] = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid SetPhotoWeight(PhotoFace face, float value)\r\n\t{\r\n\t\tif (!float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tif (BlendRegion == \u0022overall\u0022)\r\n\t\t\tface.Influence = Math.Clamp(value, 0, 1);\r\n\t\telse\r\n\t\t\tRegion(true).PhotoWeights[face.Id] = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ResetRegion()\r\n\t{\r\n\t\tif (BlendRegion != \u0022overall\u0022)\r\n\t\t\tAppearance.BaseShape.Regions.Remove(BlendRegion);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid SetBodyBlend(float value)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tAppearance.BaseShape.MaleBody = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ToggleClothingVariant(string variant)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null)\r\n\t\t\treturn;\r\n\t\tAppearance.BaseShape.ClothingVariant = Appearance.BaseShape.ClothingVariant == variant ? null : variant;\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid SetBodyFeature(bool breast, float value)\r\n\t{\r\n\t\tif (Appearance.BaseShape is null || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tif (breast)\r\n\t\t\tAppearance.BaseShape.BreastSize = Math.Clamp(value, 0, 1);\r\n\t\telse\r\n\t\t\tAppearance.BaseShape.BulgeSize = Math.Clamp(value, 0, 1);\r\n\t\tChosenPreset = null;\r\n\t}\r\n\r\n\tvoid ChooseBody(string body)\r\n\t{\r\n\t\tvar skin = AppearanceCatalog.Skins.FirstOrDefault(x =\u003E x.BodyType == body);\r\n\t\tif (skin is not null)\r\n\t\t\tChooseSkin(skin);\r\n\t}\r\n\r\n\tvoid ChooseSkin(AppearanceOption skin)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.SkinPath = skin.ResourcePath;\r\n\t\tif (Appearance.BaseShape is null \u0026\u0026 !AppearanceCatalog.FacialHair.Any(x =\u003E x.ResourcePath == Appearance.FacialHairPath \u0026\u0026 (x.BodyType == \u0022\u0022 || x.BodyType == skin.BodyType)))\r\n\t\t\tAppearance.FacialHairPath = \u0022\u0022;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseHair(AppearanceOption hair)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.HairPath = hair.ResourcePath;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseFacial(AppearanceOption facial)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.FacialHairPath = facial.ResourcePath;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseColor(AppearanceColorOption color)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.HairColor = color.Color;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ChooseSkinColor(Color color, bool normalize = false)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.SkinColor = color;\r\n\t\tAppearance.NormalizeSkinTone = normalize;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tstatic IReadOnlyList\u003CAppearanceColorOption\u003E EyeColors =\u003E new[]\r\n\t{\r\n\t\tnew AppearanceColorOption(\u0022brown\u0022, \u0022Brown\u0022, new(.24f, .12f, .055f)),\r\n\t\tnew(\u0022hazel\u0022, \u0022Hazel\u0022, new(.34f, .3f, .11f)),\r\n\t\tnew(\u0022amber\u0022, \u0022Amber\u0022, new(.58f, .34f, .075f)),\r\n\t\tnew(\u0022green\u0022, \u0022Green\u0022, new(.13f, .34f, .19f)),\r\n\t\tnew(\u0022blue\u0022, \u0022Blue\u0022, new(.09f, .3f, .55f)),\r\n\t\tnew(\u0022grey\u0022, \u0022Grey\u0022, new(.32f, .36f, .4f)),\r\n\t\tnew(\u0022violet\u0022, \u0022Violet\u0022, new(.32f, .16f, .46f))\r\n\t};\r\n\r\n\tvoid ChooseEyeColor(Color? color)\r\n\t{\r\n\t\tif (LoadingCatalog || LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.CustomEyeColor = color.HasValue;\r\n\t\tif (color.HasValue)\r\n\t\t\tAppearance.EyeColor = color.Value;\r\n\t}\r\n\r\n\tfloat EyeChannel(string channel) =\u003E channel == \u0022Red\u0022 ? Appearance.EyeColorR : channel == \u0022Green\u0022 ? Appearance.EyeColorG : Appearance.EyeColorB;\r\n\tvoid SetEyeChannel(string channel, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearance.CustomEyeColor = true;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tif (channel == \u0022Red\u0022)\r\n\t\t\tAppearance.EyeColorR = value;\r\n\t\telse if (channel == \u0022Green\u0022)\r\n\t\t\tAppearance.EyeColorG = value;\r\n\t\telse\r\n\t\t\tAppearance.EyeColorB = value;\r\n\t}\r\n\r\n\tfloat SkinChannel(string channel) =\u003E channel == \u0022Red\u0022 ? Appearance.SkinColorR : channel == \u0022Green\u0022 ? Appearance.SkinColorG : Appearance.SkinColorB;\r\n\tvoid SetSkinChannel(string channel, float value)\r\n\t{\r\n\t\tif (LoadingCatalog || !float.IsFinite(value))\r\n\t\t\treturn;\r\n\t\tvalue = Math.Clamp(value, 0, 1);\r\n\t\tAppearance.NormalizeSkinTone = true;\r\n\t\tif (channel == \u0022Red\u0022)\r\n\t\t\tAppearance.SkinColorR = value;\r\n\t\telse if (channel == \u0022Green\u0022)\r\n\t\t\tAppearance.SkinColorG = value;\r\n\t\telse\r\n\t\t\tAppearance.SkinColorB = value;\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid SetShape(AppearanceSlider slider, float value)\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tslider.Set(Appearance, value);\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid ResetShapes()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tAppearanceCatalog.ResetShapes(Appearance);\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid Randomize()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tvar voice = Appearance.VoiceId;\r\n\t\tvar variant = Appearance.BaseShape?.ClothingVariant;\r\n\t\tvar photos = Appearance.BaseShape?.PhotoFaces.Select(x =\u003E x.DeepCopy()).ToList();\r\n\t\tAppearance = AppearanceCatalog.Randomize(blendHeads: Appearance.BaseShape is not null);\r\n\t\tAppearance.VoiceId = voice;\r\n\t\tif (Appearance.BaseShape is not null)\r\n\t\t{\r\n\t\t\tAppearance.BaseShape.ClothingVariant = variant;\r\n\t\t\tif (photos is not null)\r\n\t\t\t{\r\n\t\t\t\tforeach (var face in photos)\r\n\t\t\t\t\tface.Influence = 0;\r\n\t\t\t\tAppearance.BaseShape.PhotoFaces = photos;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tChosenPreset = null;\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid Reset()\r\n\t{\r\n\t\tif (LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tvar voice = Appearance.VoiceId;\r\n\t\tbool unified = Appearance.BaseShape is not null;\r\n\t\tAppearance = AppearanceCatalog.CreateDefault();\r\n\t\tif (unified)\r\n\t\t\tUnifiedHuman.Enable(Appearance);\r\n\t\tAppearance.VoiceId = voice;\r\n\t\tChosenPreset = \u0022traveller\u0022;\r\n\t\tYaw = 0;\r\n\t\tRecenterPreview();\r\n\t\tSetZoom(CharacterCreatorPreview.FaceZoom);\r\n\t\tFeedback = null;\r\n\t}\r\n\r\n\tvoid Rotate(float delta)\r\n\t{\r\n\t\tYaw = (Yaw \u002B delta) % 360;\r\n\t}\r\n\r\n\tvoid SetYaw(float value)\r\n\t{\r\n\t\tYaw = value;\r\n\t}\r\n\r\n\tvoid SetView(float value)\r\n\t{\r\n\t\tRecenterPreview();\r\n\t\tSetZoom(value);\r\n\t}\r\n\r\n\tvoid SetZoom(float value)\r\n\t{\r\n\t\tif (float.IsFinite(value))\r\n\t\t{\r\n\t\t\tZoom = Math.Clamp(value, 0, CharacterCreatorPreview.MaxZoom);\r\n\t\t\tPortrait = Zoom \u003E= CharacterCreatorPreview.FaceZoom - 0.001f;\r\n\t\t}\r\n\t}\r\n\r\n\tvoid SetPitch(float value)\r\n\t{\r\n\t\tPreviewPitch = value;\r\n\t}\r\n\r\n\tvoid SetPan(Vector3 value)\r\n\t{\r\n\t\tPreviewPan = value;\r\n\t}\r\n\r\n\tvoid RecenterPreview()\r\n\t{\r\n\t\tYaw = 0;\r\n\t\tPreviewPan = Vector3.Zero;\r\n\t\tPreviewPitch = 0;\r\n\t\tPreview?.Recenter();\r\n\t}\r\n\r\n\tvoid ToggleClothing()\r\n\t{\r\n\t\tShowClothing = !ShowClothing;\r\n\t}\r\n\r\n\tvoid Back()\r\n\t{\r\n\t\tif (!LoadingCatalog)\r\n\t\t{\r\n\t\t\tStopVoicePreview();\r\n\t\t\tClosed?.Invoke();\r\n\t\t}\r\n\t}\r\n\r\n\tvoid Start()\r\n\t{\r\n\t\tif (Disabled || LoadingCatalog || Preview?.Loading == true)\r\n\t\t\treturn;\r\n\t\tif (!CatalogReady)\r\n\t\t{\r\n\t\t\tFeedback = LoadingCatalog ? \u0022Loading character options\u2026\u0022 : \u0022Character options could not be loaded.\u0022;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif (CharacterAppearance.ValidateForApply(Appearance)is string error)\r\n\t\t{\r\n\t\t\tFeedback = error;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tStopVoicePreview();\r\n\t\tFeedback = Saved?.Invoke(CharacterName, Appearance.DeepCopy());\r\n\t}\r\n\r\n\tpublic override void Delete(bool immediate = false)\r\n\t{\r\n\t\tStopVoicePreview();\r\n\t\tbase.Delete(immediate);\r\n\t}\r\n\r\n\tprotected override async Task OnParametersSetAsync()\r\n\t{\r\n\t\tif (InitialPreset is not null \u0026\u0026 !ReferenceEquals(loadedPreset, InitialPreset))\r\n\t\t{\r\n\t\t\tLoadDraft(InitialPreset.Name, InitialPreset.Appearance);\r\n\t\t\tloadedPreset = InitialPreset;\r\n\t\t}\r\n\r\n\t\tif (CatalogReady || LoadingCatalog)\r\n\t\t\treturn;\r\n\t\tLoadingCatalog = true;\r\n\t\ttry\r\n\t\t{\r\n\t\t\tawait AppearanceCatalog.EnsureLoadedAsync();\r\n\t\t\tCatalogReady = true;\r\n\t\t}\r\n\t\tcatch (Exception)\r\n\t\t{\r\n\t\t\tFeedback = \u0022Character options could not be loaded. Please try again.\u0022;\r\n\t\t}\r\n\t\tfinally\r\n\t\t{\r\n\t\t\tLoadingCatalog = false;\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override int BuildHash() =\u003E HashCode.Combine(CharacterName, PlaythroughName, HashCode.Combine(Tab, ShapeCategory, SkinPalette, MakeupLayer, CustomMakeup, SelectedMarking, MarkingSection, BlendRegion), ChosenPreset, JsonSerializer.Serialize(Appearance) \u002B Appearance.Makeup.PresentationKey(), HashCode.Combine(Portrait, Yaw, Feedback, LoadingCatalog, SaveLabel, Preview?.Error, CatalogReady, HashCode.Combine(LoadingCatalog, Disabled, ProductLabel, LocationLabel, Title)), Zoom, HashCode.Combine(ShowClothing, PreviewPan, PreviewPitch, WardrobeSearch, WardrobeCategory, ClothingResources.Revision, CloudWardrobeOpen));\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/HumanMakeupPresentation.cs","FileName":"HumanMakeupPresentation.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"using System;\r\n\r\nnamespace CharacterEditor;\r\n/// \u003Csummary\u003ELocal GPU albedo composition retains the native skin shader and geometry.\u003C/summary\u003E\r\npublic sealed class HumanMakeupPresentation : IDisposable\r\n{\r\n\tstatic ComputeShader compositor;\r\n\tTexture result;\r\n\tMaterial original;\r\n\tCharacterMakeupData makeup;\r\n\tstring skinPath;\r\n\tbool head;\r\n\tint armSide;\r\n\treadonly MakeupRefreshWindow refresh = new();\r\n\tTexture source;\r\n\tTexture baseColorOverride;\r\n\tint sourceWidth, sourceHeight;\r\n\tTexture[] inputs = new Texture[8];\n\tdouble lastRefreshTime = double.NaN;\r\n\tpublic Texture Apply(Material original, CharacterMakeupData makeup, string skinPath, bool head = true, int armSide = 0, Texture baseColorOverride = null)\r\n\t{\r\n\t\tthis.original = original;\r\n\t\tthis.makeup = makeup.DeepCopy();\r\n\t\tthis.skinPath = skinPath;\r\n\t\tthis.head = head;\r\n\t\tthis.armSide = armSide;\r\n\t\tthis.baseColorOverride = baseColorOverride;\r\n\t\tsource = baseColorOverride ?? original.GetTexture(\u0022g_tColor\u0022);\r\n\t\trefresh.Reset();\r\n\t\tlastRefreshTime = double.NaN;\r\n\t\t// A material can exist while its native albedo is still loading. Keep that\r\n\t\t// original bound until the compute inputs are ready instead of baking its fallback.\r\n\t\treturn Refresh();\r\n\t}\r\n\r\n\tpublic Texture Refresh()\r\n\t{\r\n\t\tvar input = baseColorOverride ?? original.GetTexture(\u0022g_tColor\u0022);\r\n\t\tif (input != source || input?.Width != sourceWidth || input?.Height != sourceHeight)\r\n\t\t{\r\n\t\t\tsource = input;\r\n\t\t\tsourceWidth = input?.Width ?? 0;\r\n\t\t\tsourceHeight = input?.Height ?? 0;\r\n\t\t\trefresh.Reset();\r\n\t\t}\r\n\r\n\t\tvar inputs = Inputs();\r\n\t\tforeach (var texture in inputs)\r\n\t\t\tif (texture is not null)\r\n\t\t\t\ttexture.MarkUsed(Math.Max(texture.Width, texture.Height));\r\n\t\tbool ready = true;\r\n\t\tforeach (var texture in inputs)\r\n\t\t\tif (texture is not { IsValid: true, IsError: false, IsLoaded: true })\r\n\t\t\t{\r\n\t\t\t\tready = false;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\tvar now = Time.NowDouble;\r\n\t\tvar delta = now == lastRefreshTime ? 0 : Math.Clamp(Time.Delta, 0, .1f);\r\n\t\tlastRefreshTime = now;\r\n\t\t// Count actual presentation updates, not wall time spent in the client\u0027s load screen.\r\n\t\t// Streaming refresh is finite once the real inputs become ready.\r\n\t\tif (refresh.Step(ready, delta))\r\n\t\t\tCompose();\r\n\t\treturn ready \u0026\u0026 result is not null ? result : source;\r\n\t}\r\n\r\n\tTexture[] Inputs()\n\t{\n\t\t// Live hotload can retain an older presentation\u0027s input array.\n\t\tif (inputs is null || inputs.Length != 8) inputs = new Texture[8];\n\t\tinputs[0] = source;\r\n\t\tinputs[1] = HumanMakeupAssets.Mask(skinPath);\r\n\t\tinputs[2] = HumanMakeupAssets.Marking(skinPath);\r\n\t\tinputs[3] = HumanMakeupAssets.EyeDetail(skinPath);\r\n\t\tinputs[4] = HumanMakeupAssets.Surface(skinPath, \u0022position_hi\u0022, head);\r\n\t\tinputs[5] = HumanMakeupAssets.Surface(skinPath, \u0022position_lo\u0022, head);\r\n\t\tinputs[6] = HumanMakeupAssets.Surface(skinPath, \u0022normal\u0022, head);\n\t\tinputs[7] = HumanMakeupAssets.LinerDetail;\n\t\treturn inputs;\r\n\t}\r\n\r\n\tTexture Compose()\r\n\t{\r\n\t\tsource.MarkUsed(Math.Max(source.Width, source.Height));\r\n\t\tif (result is null || result.Width != source.Width || result.Height != source.Height)\r\n\t\t{\r\n\t\t\tresult?.Dispose();\r\n\t\t\tresult = Texture.Create(source.Width, source.Height).WithFormat(ImageFormat.RGBA8888).WithUAVBinding().WithName(\u0022Local human makeup\u0022).Finish();\r\n\t\t}\r\n\r\n\t\tvar mask = HumanMakeupAssets.Mask(skinPath);\r\n\t\tvar marking = HumanMakeupAssets.Marking(skinPath);\r\n\t\tmask.MarkUsed(Math.Max(mask.Width, mask.Height));\r\n\t\tmarking.MarkUsed(Math.Max(marking.Width, marking.Height));\r\n\t\tvar attributes = new RenderAttributes();\r\n\t\tattributes.Set(\u0022BaseColor\u0022, source);\r\n\t\tattributes.Set(\u0022Coverage\u0022, mask);\r\n\t\tattributes.Set(\u0022Marking\u0022, marking);\r\n\t\tattributes.Set(\u0022Result\u0022, result);\r\n\t\tvar eyeDetail = HumanMakeupAssets.EyeDetail(skinPath);\r\n\t\teyeDetail.MarkUsed(Math.Max(eyeDetail.Width, eyeDetail.Height));\r\n\t\tattributes.Set(\u0022EyeDetail\u0022, eyeDetail);\n\t\tattributes.Set(\u0022LinerDetail\u0022, HumanMakeupAssets.LinerDetail);\n\t\tattributes.Set(\u0022LinerOptions\u0022, new Vector4(makeup.EyelinerLowerStrength, makeup.EyelinerInnerStrength, makeup.EyelinerWing == \u0022wing\u0022 ? 4f : makeup.EyelinerWing == \u0022downward\u0022 ? 3f : makeup.EyelinerWing == \u0022cat-eye\u0022 ? 2f : makeup.EyelinerWing == \u0022flick\u0022 ? 1f : 0f, makeup.EyelinerWingStrength));\n\t\tattributes.Set(\u0022WingTip\u0022, new Vector4(makeup.WingTipHorizontal, makeup.WingTipVertical, makeup.EyelinerWingThickness, 0));\n\t\tattributes.Set(\u0022Liner\u0022, new Vector4(makeup.Eyeliner.Red, makeup.Eyeliner.Green, makeup.Eyeliner.Blue, makeup.Eyeliner.Amount));\r\n\t\tattributes.Set(\u0022LowerEye\u0022, new Vector4(makeup.LowerEyeshadow.Red, makeup.LowerEyeshadow.Green, makeup.LowerEyeshadow.Blue, makeup.LowerEyeshadow.Amount));\r\n\t\tattributes.Set(\u0022Lip\u0022, new Vector4(makeup.Lipstick.Red, makeup.Lipstick.Green, makeup.Lipstick.Blue, makeup.Lipstick.Amount));\r\n\t\tattributes.Set(\u0022Eye\u0022, new Vector4(makeup.Eyeshadow.Red, makeup.Eyeshadow.Green, makeup.Eyeshadow.Blue, makeup.Eyeshadow.Amount));\r\n\t\tattributes.Set(\u0022Blush\u0022, new Vector4(makeup.Blush.Red, makeup.Blush.Green, makeup.Blush.Blue, makeup.Blush.Amount));\r\n\t\tattributes.Set(\u0022Tattoo\u0022, new Vector4(makeup.Tattoo.Red, makeup.Tattoo.Green, makeup.Tattoo.Blue, makeup.TattooStyle == \u0022none\u0022 ? 0 : makeup.Tattoo.Amount));\r\n\t\tattributes.Set(\u0022IsHead\u0022, head ? 1f : 0f);\r\n\t\tattributes.Set(\u0022ArmSide\u0022, (float)armSide);\r\n\t\tforeach (var kind in new[]\r\n\t\t{\r\n\t\t\t\u0022position_hi\u0022,\r\n\t\t\t\u0022position_lo\u0022,\r\n\t\t\t\u0022normal\u0022\r\n\t\t}\r\n\r\n\t\t)\r\n\t\t{\r\n\t\t\tvar texture = HumanMakeupAssets.Surface(skinPath, kind, head);\r\n\t\t\ttexture.MarkUsed(Math.Max(texture.Width, texture.Height));\r\n\t\t\tattributes.Set(kind == \u0022position_hi\u0022 ? \u0022PositionHigh\u0022 : kind == \u0022position_lo\u0022 ? \u0022PositionLow\u0022 : \u0022SurfaceNormal\u0022, texture);\r\n\t\t}\r\n\r\n\t\tvar layers = makeup.Markings.Where(x =\u003E head ? MarkingCatalog.OnHead(x) : MarkingCatalog.OnMaterial(x, armSide)).ToArray();\r\n\t\tfor (var i = 0; i \u003C MarkingCatalog.Maximum; i\u002B\u002B)\r\n\t\t{\r\n\t\t\tvar name = i.ToString();\r\n\t\t\tif (i \u003E= layers.Length)\r\n\t\t\t{\r\n\t\t\t\tattributes.Set(\u0022Ink\u0022 \u002B name, new Vector4(0, 0, 0, 0));\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tvar layer = layers[i];\r\n\t\t\tvar region = MarkingCatalog.Region(layer.Region);\r\n\t\t\tvar style = MarkingCatalog.Style(layer.Style);\r\n\t\t\tvar frame = MarkingCatalog.Frame(layer);\r\n\t\t\tvar center = frame.Center;\r\n\t\t\tvar scale = region.Size * MarkingCatalog.StampScale(layer);\r\n\t\t\tvar width = style.Band ? MarkingCatalog.BandScale(layer) : .38f * scale;\r\n\t\t\tvar height = style.Band ? MarkingCatalog.BandSpacing(layer) : .45f * scale;\r\n\t\t\tattributes.Set(\u0022Center\u0022 \u002B name, new Vector4(center.x, center.y, center.z, style.ShaderId));\r\n\t\t\tattributes.Set(\u0022Right\u0022 \u002B name, new Vector4(frame.Right.x, frame.Right.y, frame.Right.z, width));\r\n\t\t\tattributes.Set(\u0022Up\u0022 \u002B name, new Vector4(frame.Up.x, frame.Up.y, frame.Up.z, height));\r\n\t\t\tattributes.Set(\u0022Normal\u0022 \u002B name, new Vector4(frame.Normal.x, frame.Normal.y, frame.Normal.z, (layer.Rotation - .5f) * MathF.PI * 2));\r\n\t\t\tattributes.Set(\u0022Min\u0022 \u002B name, new Vector4(region.Min.x, region.Min.y, region.Min.z, frame.Projection));\r\n\t\t\tattributes.Set(\u0022Max\u0022 \u002B name, new Vector4(region.Max.x, region.Max.y, region.Max.z, style.Band ? Math.Clamp(MathF.Round(MathF.PI * 2 * region.Radius / (1.6f * height)), 3, 36) : frame.Phase));\r\n\t\t\tattributes.Set(\u0022Ink\u0022 \u002B name, new Vector4(layer.Ink.Red, layer.Ink.Green, layer.Ink.Blue, style.ShaderId == 0 ? 0 : layer.Ink.Amount));\r\n\t\t}\r\n\r\n\t\tcompositor ??= new(HumanMakeupAssets.ShaderPath);\r\n\t\tcompositor.DispatchWithAttributes(attributes, source.Width, source.Height, 1);\r\n\t\treturn result;\r\n\t}\r\n\r\n\tpublic void Dispose()\r\n\t{\r\n\t\tresult?.Dispose();\r\n\t\tresult = null;\r\n\t}\r\n}\r\n\r\n/// \u003Csummary\u003EFinite mip refresh starts when native input textures are ready, measured in presentation updates.\u003C/summary\u003E\npublic sealed class MakeupRefreshWindow\r\n{\r\n\tbool ready;\r\n\tfloat elapsed, next;\r\n\tpublic void Reset()\r\n\t{\r\n\t\tready = false;\r\n\t\telapsed = 0;\r\n\t\tnext = 0;\r\n\t}\r\n\r\n\tpublic bool Step(bool inputsReady, float delta)\r\n\t{\r\n\t\tif (!inputsReady)\r\n\t\t{\r\n\t\t\tReset();\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tif (!ready)\r\n\t\t{\r\n\t\t\tready = true;\r\n\t\t\telapsed = 0;\r\n\t\t\tnext = 0;\r\n\t\t}\r\n\r\n\t\telapsed \u002B= Math.Clamp(delta, 0, .1f);\r\n\t\tif (elapsed \u003E 3 || elapsed \u003C next)\r\n\t\t\treturn false;\r\n\t\tnext = elapsed \u002B .25f;\r\n\t\treturn true;\r\n\t}\r\n}\r\n"},{"Ident":"vendetta.characterkit","Path":"Code/CharacterKit/UnifiedWardrobe.cs","FileName":"UnifiedWardrobe.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":421600,"IsPrivate":false,"Code":"namespace CharacterEditor;\r\n/// \u003Csummary\u003EAuthored human cuts fitted automatically to both unified body endpoints.\u003C/summary\u003E\r\npublic static partial class UnifiedWardrobe\r\n{\r\n\t/// \u003Csummary\u003EHead replacements can retain skin or deliberately replace the entire neck.\u003C/summary\u003E\r\n\tpublic static bool RetainsHumanNeck(Clothing item)\r\n\t{\r\n\t\tif (string.IsNullOrEmpty(item.HumanSkinModel)) return true;\r\n\t\tvar skin = Model.Load(item.HumanSkinModel);\r\n\t\t// Use the replacement\u0027s native skin presentation. A transparent/non-skin\r\n\t\t// head material must not be replaced with the unified human\u0027s skin neck.\r\n\t\treturn skin.MeshInfo.Meshes.Where(m =\u003E m.Name == \u0022head_01\u0022)\r\n\t\t\t.SelectMany(m =\u003E m.DrawCalls).Any(d =\u003E d.Material?.ShaderName == \u0022shaders/skin.shader\u0022);\r\n\t}\r\n\r\n\tpublic static string ModelPath(Clothing item, IEnumerable\u003CClothing\u003E selected, TagSet tags, CharacterAppearanceData data)\r\n\t{\r\n\t\tvar native = item.GetModel(selected.ToArray(), tags);\r\n\t\tif (data.BaseShape is null)\r\n\t\t\treturn native;\r\n\t\t// Preserve conditional model overrides selected by native clothing metadata.\r\n\t\tif (native != item.Model \u0026\u0026 native != item.HumanAltModel \u0026\u0026 native != item.HumanAltFemaleModel)\r\n\t\t\treturn native;\r\n\t\t// Explicit preference selects only the authored cut; the morph still uses\r\n\t\t// the actual body blend. Single-variant items retain their usual fallback.\r\n\t\tif (data.BaseShape.ClothingVariant is not null \u0026\u0026 !string.IsNullOrEmpty(item.HumanAltModel) \u0026\u0026 !string.IsNullOrEmpty(item.HumanAltFemaleModel))\r\n\t\t{\r\n\t\t\tvar choice = data.BaseShape.ClothingVariant == \u0022male\u0022 ? item.HumanAltModel : item.HumanAltFemaleModel;\r\n\t\t\treturn choice;\r\n\t\t}\r\n\r\n\t\t// A single authored human variant is usable across the full body range.\r\n\t\tvar preferred = data.BaseShape.MaleBody \u003E= .5f ? item.HumanAltModel : item.HumanAltFemaleModel;\r\n\t\tif (string.IsNullOrEmpty(preferred))\r\n\t\t\tpreferred = string.IsNullOrEmpty(item.HumanAltModel) ? item.HumanAltFemaleModel : item.HumanAltModel;\r\n\t\treturn !string.IsNullOrEmpty(preferred) ? preferred : native;\r\n\t}\r\n\r\n\tpublic static Model Resolve(Clothing item, IEnumerable\u003CClothing\u003E selected, TagSet tags, CharacterAppearanceData data)\r\n\t{\r\n\t\tvar path = ModelPath(item, selected, tags, data);\r\n\t\tvar native = Model.Load(path);\r\n\t\tif (data.BaseShape is null || string.IsNullOrEmpty(path) || (string.IsNullOrEmpty(item.HumanAltModel) \u0026\u0026 string.IsNullOrEmpty(item.HumanAltFemaleModel)))\r\n\t\t\treturn native;\r\n\t\tif (item.Category.ToString().StartsWith(\u0022FacialHair\u0022) || item.Category.ToString().StartsWith(\u0022Hair\u0022))\r\n\t\t\treturn native;\r\n\t\tbool female = path == item.HumanAltFemaleModel \u0026\u0026 path != item.HumanAltModel;\r\n\t\tif (path != item.HumanAltModel \u0026\u0026 path != item.HumanAltFemaleModel)\r\n\t\t\tfemale = tags.Has(\u0022female\u0022);\r\n\t\treturn AutomaticWardrobeFit.Fit(native, female, fitFace: (item.HideBody \u0026 Clothing.BodyGroups.Head) == 0);\r\n\t}\r\n\r\n\tpublic static void Apply(SkinnedModelRenderer renderer, CharacterAppearanceData data)\r\n\t{\r\n\t\tif (data.BaseShape is not null \u0026\u0026 AutomaticWardrobeFit.IsFitted(renderer.Model))\r\n\t\t\trenderer.Morphs.Set(\u0022ce_body_female\u0022, 1 - data.BaseShape.MaleBody);\r\n\t}\r\n}// Keep hair outside body fitting.\r\n\r\n"}]}