using System; using HarmonyLib; using UnityEngine; namespace NecromancerTome { /// /// Грифельная доска (necroChalkboard, 09.10.2026): надписи мелом - как на табличках (TEFeatureSignable), но /// «игрок этого делать не будет, а нам для сюжета пригодится». Поэтому команда «написать» у доски есть /// только в редакторе префабов, в творческом режиме и в режиме отладки (так мы и подписываем её - текст /// сохраняется в префабе локации, IFeatureSavedInPrefab). В обычной игре доску только читают: без команды /// нет и подсказки «E - использовать». /// public static class Chalkboard { /// Сюжетная доска (чертоги, локации): пишет мод или автор префаба. public const string BlockName = "necroChalkboard"; /// Доска игрока (10.10): крафтится, пишется в обычной игре - записи свои у каждой доски. public const string PlayerBlockName = "necroChalkboardPlayer"; static string NameOf(TEFeatureSignable _sign) { return _sign?.Parent?.TeData?.Block?.GetBlockName(); } /// Любая из досок - для материала надписи. public static bool Is(TEFeatureSignable _sign) { string n = NameOf(_sign); return n == BlockName || n == PlayerBlockName; } /// Сюжетная доска - писать только в редакторе, творческом режиме, отладке. public static bool IsLocked(TEFeatureSignable _sign) { return NameOf(_sign) == BlockName && !CanWrite(); } /// Кто может писать: редактор префабов, творческий режим, отладка. public static bool CanWrite() { return GameManager.Instance != null && GameManager.Instance.IsEditMode() || GameStats.GetBool(EnumGameStats.IsCreativeMenuEnabled) || GamePrefs.GetBool(EnumGamePrefs.DebugMenuEnabled); } /// Материал надписи - из бандла, свой шейдер NecroTome/ChalkText с ZTest LEqual (тест 09.10: надпись /// просвечивала сквозь стены - встроенный GUI/Text Shader берёт ZTest из глобального unity_GUIZTestMode, который /// интерфейс держит «Always», и переопределение в материале не помогло). Шрифт динамический: его текстура /// растёт и пересоздаётся при новых буквах - держим её в материале надписи (один материал на все доски). static Material _mat; static Font _font; static bool _hooked; public static void BindText(TextMesh _tm, MeshRenderer _r) { _mat = _r.sharedMaterial; _font = _tm.font; if (_mat == null || _font == null) { return; } _mat.mainTexture = _font.material.mainTexture; if (!_hooked) { _hooked = true; Font.textureRebuilt += _f => { if (_mat != null && _f == _font) { _mat.mainTexture = _f.material.mainTexture; } }; } } } [HarmonyPatch(typeof(TEFeatureSignable), nameof(TEFeatureSignable.SetBlockEntityData))] public static class Patch_TEFeatureSignable_SetBlockEntityData_Chalkboard { public static void Postfix(TEFeatureSignable __instance, BlockEntityData _blockEntityData) { if (_blockEntityData == null || !_blockEntityData.bHasTransform || !Chalkboard.Is(__instance)) { return; } foreach (TextMesh tm in _blockEntityData.transform.GetComponentsInChildren()) { MeshRenderer r = tm.GetComponent(); if (r != null && tm.font != null) { Chalkboard.BindText(tm, r); } } } } [HarmonyPatch(typeof(TEFeatureSignable), nameof(TEFeatureSignable.AllowBlockActivationCommand))] public static class Patch_TEFeatureSignable_AllowBlockActivationCommand_Chalkboard { public static void Postfix(TEFeatureSignable __instance, ITileEntityFeature _module, ReadOnlySpan _commandName, ref bool __result) { if (__result && __instance.Equals(_module) && _commandName.SequenceEqual("edit".AsSpan()) && Chalkboard.IsLocked(__instance)) { __result = false; } } } [HarmonyPatch(typeof(TEFeatureSignable), nameof(TEFeatureSignable.GetActivationText))] public static class Patch_TEFeatureSignable_GetActivationText_Chalkboard { public static void Postfix(TEFeatureSignable __instance, ref string __result) { if (Chalkboard.IsLocked(__instance)) { __result = null; } } } }