using System.Collections.Generic; using HarmonyLib; using UnityEngine; namespace NecromancerTome { /// /// Костяной скакун (1.6.0, 01.10.2026): мотоцикл из костей, призывается книгой, ездит на /// кровавых сферах. Сущность necroBoneSteed в entityclasses.xml - наследник vehicleMotorcycle /// со своей моделью (бандл necrobonesteed, собран из копии ванильного VMotorcycleP.prefab). /// /// Топливо - кровавые сферы в четырёх ячейках модификаций, ремонт - костями: всё это в /// BoneSteedFuelPatch. Здесь только запрет заправки канистрой (GetFuelItem). /// /// Модель выросла из рипнутого префаба, и у неё те же две беды, что у оссуария: игровые теги /// коллайдеров потеряны (без них на скакуна не сесть и по нему не попасть) и шейдер-заглушка. /// Оба лечатся с ванильного мотоцикла - теги по путям объектов, шейдер у его материала. /// public static class BoneSteed { public const string EntityClassName = "necroBoneSteed"; public const string FuelItem = "resourceBloodSphere"; public const string ItemName = "necroBoneSteedPlaceable"; const string VanillaPrefab = "@:Entities/Vehicles/VMotorcycle/VMotorcycleP.prefab"; public static bool Is(Entity e) { return e != null && EntityClass.list.TryGetValue(e.entityClass, out EntityClass cls) && cls != null && cls.entityClassName == EntityClassName; } static Transform vanilla; static Shader vanillaShader; public static void FixModel(EntityVehicle ev) { if (vanilla == null) { vanilla = DataLoader.LoadAsset(VanillaPrefab); if (vanilla == null) { Debug.LogWarning("[NecromancerTome] BoneSteed: ванильный мотоцикл не найден - теги и шейдер не поправлены"); return; } foreach (Renderer r in vanilla.GetComponentsInChildren(true)) { if (r.sharedMaterial != null && r.sharedMaterial.HasProperty("_RMOM")) { vanillaShader = r.sharedMaterial.shader; break; } } } var byPath = new Dictionary(); foreach (Transform t in vanilla.GetComponentsInChildren(true)) byPath[PathOf(t, vanilla)] = t; int tags = 0, mats = 0; foreach (Transform t in ev.transform.GetComponentsInChildren(true)) { if (!byPath.TryGetValue(PathOf(t, ev.transform), out Transform src)) continue; if (t.tag != src.tag || t.gameObject.layer != src.gameObject.layer) { t.tag = src.tag; t.gameObject.layer = src.gameObject.layer; tags++; } } if (vanillaShader != null) { foreach (Renderer r in ev.transform.GetComponentsInChildren(true)) { foreach (Material m in r.sharedMaterials) { if (m != null && m.HasProperty("_RMOM") && m.shader != vanillaShader) { m.shader = vanillaShader; m.enableInstancing = true; mats++; } } } } Debug.Log("[NecromancerTome] BoneSteed: теги поправлены у " + tags + " объектов, шейдер '" + (vanillaShader != null ? vanillaShader.name : "-") + "' поставлен " + mats + " материалам"); } /// Путь объекта от корня префаба. Корень - пустая строка: у экземпляра корень /// называется иначе (necroBoneSteedP(Clone)), чем у ванильного префаба. static string PathOf(Transform t, Transform root) { if (t == root) return ""; string p = t.name; for (Transform x = t.parent; x != null && x != root; x = x.parent) p = x.name + "/" + p; return p; } } [HarmonyPatch(typeof(EntityVehicle), nameof(EntityVehicle.PostInit))] public static class Patch_EntityVehicle_PostInit_BoneSteed { public static void Postfix(EntityVehicle __instance) { if (BoneSteed.Is(__instance)) BoneSteed.FixModel(__instance); } } /// Канистрой и вообще из рюкзака скакуна не заправить (указание 01.10): кровь /// вставляется только в ячейки. Пустой предмет топлива прячет пункт «Заправить» в меню. [HarmonyPatch(typeof(Vehicle), nameof(Vehicle.GetFuelItem))] public static class Patch_Vehicle_GetFuelItem_BoneSteed { public static void Postfix(Vehicle __instance, ref string __result) { if (BoneSteed.Is(__instance.entity)) __result = ""; } } }