using HarmonyLib; using UnityEngine; namespace NecromancerTome { /// /// Двигатель костяного скакуна (тест 01.10.2026): /// /// - «ДАЖЕ БЕЗ СФЕР ЕДЕТ БЫСТРО». Ванильный VPEngine.startEngine ставит isRunning без /// проверки топлива, а глохнет только по событию FuelEmpty - то есть когда бак пустеет на /// ходу. Заведённый с пустым баком мотор так и работал на полной тяге. Здесь: без крови мотор /// не заводится, кончилась на ходу - глохнет; вставили сферу, сидя в седле, - заводится сам. /// Без мотора у ванили остаётся тяга x50 - еле ползёт, как пустой мотоцикл. /// - «ЗВУК МОТОЦИКЛА УБРАТЬ». Все звуки мотора (заводка, глушение, обороты) у скакуна /// молчат - слышно только то, что слышно у мотоцикла с пустым баком (колёса). Стук педалей /// пробовали - «тоже не нужен» (01.10). /// Дым и огонь (розовые кубы - частицы без материалов) убраны в vehicles.xml и префабе. /// public static class BoneSteedEngine { public static bool Is(VehiclePart part) => part?.vehicle != null && BoneSteed.Is(part.vehicle.entity); public static bool HasBlood(Vehicle v) => BoneSteedFuel.FuelOf(v) > 0f; } [HarmonyPatch(typeof(VPEngine), nameof(VPEngine.startEngine))] public static class Patch_VPEngine_startEngine_BoneSteed { public static bool Prefix(VPEngine __instance) => !BoneSteedEngine.Is(__instance) || BoneSteedEngine.HasBlood(__instance.vehicle); } [HarmonyPatch(typeof(VPEngine), nameof(VPEngine.Update))] public static class Patch_VPEngine_Update_BoneSteed { public static void Postfix(VPEngine __instance, float _dt) { if (!BoneSteedEngine.Is(__instance)) return; Vehicle v = __instance.vehicle; bool blood = BoneSteedEngine.HasBlood(v); if (__instance.isRunning && !blood) __instance.stopEngine(true); else if (!__instance.isRunning && blood && v.entity.HasDriver && !__instance.IsBroken()) __instance.startEngine(); } } [HarmonyPatch(typeof(VPEngine), nameof(VPEngine.playSound))] public static class Patch_VPEngine_playSound_BoneSteed { public static bool Prefix(VPEngine __instance) => !BoneSteedEngine.Is(__instance); } [HarmonyPatch(typeof(VPEngine), nameof(VPEngine.updateEngineSounds))] public static class Patch_VPEngine_updateEngineSounds_BoneSteed { public static bool Prefix(VPEngine __instance) => !BoneSteedEngine.Is(__instance); } /// Глазница черепа гаснет вместе с фарой (01.10). Свечение глазницы - карта эмиссии /// в текстуре вилки, ваниль её не переключает. На все уровни детализации вилки ставится /// MaterialPropertyBlock (материал не копируется): эмиссия белая при включённой фаре, чёрная - /// при выключенной. Обе ручки - _EmissionColor и _EmissionMultiply: какую из них читает /// шейдер транспорта, по декомпилятору не видно, а лишняя не мешает. public static class BoneSteedEye { static readonly MaterialPropertyBlock block = new MaterialPropertyBlock(); public static void Apply(EntityVehicle e, bool on) { if (e == null) return; foreach (Renderer r in e.transform.GetComponentsInChildren(true)) { if (!r.name.StartsWith("frontFork_LOD")) continue; r.GetPropertyBlock(block); block.SetColor("_EmissionColor", on ? Color.white : Color.black); block.SetFloat("_EmissionMultiply", on ? 1f : 0f); r.SetPropertyBlock(block); } } } [HarmonyPatch(typeof(VPHeadlight), nameof(VPHeadlight.UpdateOn))] public static class Patch_VPHeadlight_UpdateOn_BoneSteedEye { public static void Postfix(VPHeadlight __instance) { if (BoneSteedEngine.Is(__instance)) BoneSteedEye.Apply(__instance.vehicle.entity, __instance.IsOn()); } } /// При появлении скакуна фара выключена - глазница тоже тёмная. [HarmonyPatch(typeof(EntityVehicle), nameof(EntityVehicle.PostInit))] public static class Patch_EntityVehicle_PostInit_BoneSteedEye { public static void Postfix(EntityVehicle __instance) { if (!BoneSteed.Is(__instance)) return; VPHeadlight light = null; foreach (VehiclePart part in __instance.GetVehicle().vehicleParts) if (part is VPHeadlight h) light = h; BoneSteedEye.Apply(__instance, light != null && light.IsOn()); } } }