using System.Collections.Generic; using HarmonyLib; using UnityEngine; namespace NecromancerTome { /// /// Костяной скакун (1.7.0, 01.10.2026): мотоцикл из костей, призывается книгой, ездит на /// кровавых сферах. Сущность necroBoneSteed в entityclasses.xml - наследник vehicleMotorcycle /// со своей моделью (бандл necrobonesteed, собран из копии ванильного VMotorcycleP.prefab). /// /// Топливо у транспорта игры в XML не задаётся: Vehicle.GetFuelItem() возвращает "ammoGasCan" /// строкой в коде. Отсюда два патча: предмет топлива - сфера, и заправка - одна сфера на полный /// бак (ваниль берёт по канистре на 1/25 единицы бака - 120 сфер на бак было бы абсурдом). /// Без сфер скакун глохнет как обычный транспорт (решение 01.10) - это ваниль, кода не нужно. /// /// Модель выросла из рипнутого префаба, и у неё те же две беды, что у оссуария: игровые теги /// коллайдеров потеряны (без них на скакуна не сесть и по нему не попасть) и шейдер-заглушка. /// Оба лечатся с ванильного мотоцикла - теги по путям объектов, шейдер у его материала. /// public static class BoneSteed { public const string EntityClassName = "necroBoneSteed"; public const string FuelItem = "resourceBloodSphere"; 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); } } [HarmonyPatch(typeof(Vehicle), nameof(Vehicle.GetFuelItem))] public static class Patch_Vehicle_GetFuelItem_BoneSteed { public static void Postfix(Vehicle __instance, ref string __result) { if (__result != "" && BoneSteed.Is(__instance.entity)) __result = BoneSteed.FuelItem; } } /// Одна сфера - полный бак. [HarmonyPatch(typeof(EntityVehicle), nameof(EntityVehicle.AddFuelFromInventory))] public static class Patch_EntityVehicle_AddFuelFromInventory_BoneSteed { public static bool Prefix(EntityVehicle __instance, EntityAlive entity, ref bool __result) { if (!BoneSteed.Is(__instance)) return true; __result = false; Vehicle v = __instance.GetVehicle(); if (v.GetFuelPercent() >= 1f || !(entity is EntityPlayer player)) return false; ItemValue sphere = ItemClass.GetItem(BoneSteed.FuelItem); int taken = player.inventory.DecItem(sphere, 1); if (taken == 0) taken = player.bag.DecItem(sphere, 1); if (taken == 0) return false; v.AddFuel(v.GetMaxFuelLevel() - v.GetFuelLevel()); __instance.PlayOneShot("useactions/gas_refill"); Debug.Log("[NecromancerTome] BoneSteed: сфера залита - полный бак"); __result = true; return false; } } }