Files
necromants-tome-7d2d-3-2/HarmonySrc/BlockNecroMemoryBook.cs
T
AlexCubeandClaude Opus 5.5 66a0dcf403 Чертоги разума, хваты пустой руки, одержимость, книга с оглавлением
- Чертоги разума: дух в префабе chertogi под землёй, книга памяти, нож и плакат;
  кровать префаба - мебель necroPalaceBed (иначе становилась точкой возрождения);
  охрана выбрасывает и игроков (их нет в EntityAlives).
- Хваты пустой руки по R: «отрицание» и «пиф-паф» - поза рук костями поверх аниматора,
  остальные кости заморожены, ЛКМ - заклинание, прицел краснеет на зомби.
- «Отрицание»: зомби в ужасе бежит дальше 40 м и теряет игрока как цель.
- «Пиф-паф»: одержимость зомби-гуманоидом - бег, прыжок, удар, дальняя атака,
  подрыв, крик крикуньи; тело цепенеет, зомби танкуют одержимого; область
  вокруг тела сжата; полоска здоровья; спавн и прорисовка у одержимого.
- Книга с оглавлением (окно necroTome), глава «Некроэнергия».
- Прорисовка дверей у змеи и одержимого (CheckLODs от камеры, а не от тела).
- Почтовый ящик консоли и команды necrobones/necropose/necroshot для разработки.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:10:05 +03:00

201 lines
8.5 KiB
C#

using UnityEngine;
using UnityEngine.Scripting;
namespace NecromancerTome
{
/// <summary>
/// «Книга памяти» в чертогах разума (указание 03.10): единственный предмет чертогов, с которым
/// можно взаимодействовать. При наведении - «Нажмите E, чтобы прочитать», по E - окно «Память»
/// (потом доделаем, как окна концовки в FinalSlides.cs). Блок подключается в blocks.xml через
/// Class="NecromancerTome.NecroMemoryBook, NecromancerHarmony": игра (ReflectionHelpers.
/// GetTypeWithPrefix) вставляет приставку Block перед именем после последней точки.
/// Вид - одиночная книга игры (bookInvisibleKillerPrefab, как cntBookPile09 на столе префаба
/// chertogi), но без хранилища и лута.
/// </summary>
[Preserve]
public class BlockNecroMemoryBook : Block
{
public const string ReadCommand = "read";
static readonly BlockActivationCommand[] Commands =
{
new BlockActivationCommand(ReadCommand, "book", true)
};
public override bool HasBlockActivationCommands(WorldBase _world, BlockValue _blockValue, Vector3i _blockPos, EntityAlive _entityFocusing)
{
return true;
}
public override BlockActivationCommand[] GetBlockActivationCommands(WorldBase _world, BlockValue _blockValue, Vector3i _blockPos, EntityAlive _entityFocusing)
{
return Commands;
}
public override string GetActivationText(WorldBase _world, BlockValue _blockValue, Vector3i _blockPos, EntityAlive _entityFocusing)
{
return Localization.Get("necroMemoryBookPrompt");
}
public override bool OnBlockActivated(string _commandName, WorldBase _world, Vector3i _blockPos, BlockValue _blockValue, EntityPlayerLocal _player)
{
return _commandName == ReadCommand && OnBlockActivated(_world, _blockPos, _blockValue, _player);
}
public override bool OnBlockActivated(WorldBase _world, Vector3i _blockPos, BlockValue _blockValue, EntityPlayerLocal _player)
{
Open(_player);
return true;
}
/// <summary>Окно книги (оглавление, главы). Отдельным методом - его же позовёт дух в чертогах (своё E).</summary>
public static void Open(EntityPlayerLocal _player)
{
LocalPlayerUI ui = LocalPlayerUI.GetUIForPlayer(_player);
if (ui == null)
{
return;
}
// 03.10: вместо окна-сообщения - книга с оглавлением (NecroTome.cs).
NecroTomeBook.Open(_player);
}
// ---------------------------------------------------------------- автор на обложке
/// <summary>Шрифт надписи - тот же готический, что у названия в текстуре (UnifrakturMaguntia, OFL).</summary>
const string FontAsset = "#@modfolder(NecromancerTome):Resources/necrobooks?UnifrakturMaguntia-Book.ttf";
const string AuthorChild = "NecroAuthor";
// Место под автора - просвет под рисунком (make_rat_book_textures.py, Mind), в осях корня модели
// necroBookMindPrefab: из Blender (x, y, z) -> Unity (-x, z, -y); книга в модели повёрнута на -65°.
// Подбирается на ходу командой necroauthor (x y z, размер, ширина).
public static Vector3 AuthorPos = new Vector3(0.0944f, 0.0885f, -0.1534f);
public static Vector3 AuthorUp = new Vector3(-0.4226f, 0f, 0.9063f);
public static float AuthorHeight = 0.030f, AuthorMaxWidth = 0.21f;
public static Color AuthorColor = new Color(0.85f, 0.06f, 0.05f, 1f);
static Font _font;
public override void OnBlockEntityTransformAfterActivated(WorldBase _world, Vector3i _blockPos, BlockValue _blockValue, BlockEntityData _ebcd)
{
base.OnBlockEntityTransformAfterActivated(_world, _blockPos, _blockValue, _ebcd);
try
{
if (_ebcd?.transform != null)
{
PlaceAuthor(_ebcd.transform);
}
}
catch (System.Exception e)
{
Debug.LogWarning("[NecromancerTome] книга памяти: автор не встал - " + e.Message);
}
}
/// <summary>Ник игрока на обложке, как подпись на ящике: TextMesh поверх модели (модель общая,
/// а ник у каждого свой, в текстуру его не запечь). Чертоги у каждого свои - пишем ник своего игрока.</summary>
public static void PlaceAuthor(Transform _root)
{
EntityPlayerLocal player = GameManager.Instance?.World?.GetPrimaryPlayer();
string name = player != null ? player.PlayerDisplayName : null;
Transform old = _root.Find(AuthorChild);
if (string.IsNullOrEmpty(name))
{
if (old != null) Object.Destroy(old.gameObject);
return;
}
if (_font == null)
{
_font = DataLoader.LoadAsset<Font>(FontAsset);
}
GameObject go = old != null ? old.gameObject : new GameObject(AuthorChild);
go.layer = _root.gameObject.layer;
Transform t = go.transform;
t.SetParent(_root, false);
t.localPosition = AuthorPos;
// лицом вверх (обложка), низ строки - к нижнему краю обложки
t.localRotation = Quaternion.LookRotation(Vector3.down, AuthorUp.normalized);
t.localScale = Vector3.one;
TextMesh tm = go.GetComponent<TextMesh>() ?? go.AddComponent<TextMesh>();
MeshRenderer mr = go.GetComponent<MeshRenderer>();
if (_font != null)
{
tm.font = _font;
mr.sharedMaterial = _font.material;
if (mr.sharedMaterial != null && (mr.sharedMaterial.shader == null || !mr.sharedMaterial.shader.isSupported))
{
Shader sh = Shader.Find("GUI/Text Shader");
if (sh != null) mr.sharedMaterial.shader = sh;
}
}
tm.text = name;
tm.anchor = TextAnchor.MiddleCenter;
tm.alignment = TextAlignment.Center;
tm.fontSize = 96;
tm.color = AuthorColor;
mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
mr.receiveShadows = false;
tm.characterSize = 1f;
// Размер подгоняется кадром позже: меш надписи TextMesh строит не сразу.
NecroAuthorFit fit = go.GetComponent<NecroAuthorFit>() ?? go.AddComponent<NecroAuthorFit>();
fit.enabled = true;
}
}
/// <summary>Подгонка надписи автора: высота строки AuthorHeight, длинный ник ужимается до AuthorMaxWidth.</summary>
public class NecroAuthorFit : MonoBehaviour
{
void LateUpdate()
{
MeshRenderer mr = GetComponent<MeshRenderer>();
Bounds b = mr != null ? mr.localBounds : default;
if (b.size.y < 0.0001f)
{
return; // меш ещё не построен
}
float k = BlockNecroMemoryBook.AuthorHeight / b.size.y;
if (b.size.x * k > BlockNecroMemoryBook.AuthorMaxWidth)
{
k = BlockNecroMemoryBook.AuthorMaxWidth / b.size.x;
}
transform.localScale = new Vector3(k, k, k);
enabled = false;
}
}
/// <summary>necroauthor [x y z] [h N] [w N] - подобрать место и размер ника на книге памяти на ходу.</summary>
[Preserve]
public class ConsoleCmdNecroAuthor : ConsoleCmdAbstract
{
public override string[] getCommands() => new[] { "necroauthor" };
public override string getDescription() => "NecromancerTome: ник на книге памяти - x y z | h N | w N";
public override void Execute(System.Collections.Generic.List<string> _params, CommandSenderInfo _senderInfo)
{
System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.InvariantCulture;
if (_params.Count == 3)
{
BlockNecroMemoryBook.AuthorPos = new Vector3(float.Parse(_params[0], ci), float.Parse(_params[1], ci), float.Parse(_params[2], ci));
}
else if (_params.Count == 2 && _params[0] == "h")
{
BlockNecroMemoryBook.AuthorHeight = float.Parse(_params[1], ci);
}
else if (_params.Count == 2 && _params[0] == "w")
{
BlockNecroMemoryBook.AuthorMaxWidth = float.Parse(_params[1], ci);
}
int n = 0;
foreach (NecroAuthorFit f in Object.FindObjectsOfType<NecroAuthorFit>(true))
{
BlockNecroMemoryBook.PlaceAuthor(f.transform.parent);
n++;
}
Vector3 p = BlockNecroMemoryBook.AuthorPos;
SdtdConsole.Instance.Output(string.Format(ci, "автор: {0:0.####} {1:0.####} {2:0.####}, h {3}, w {4}; надписей {5}",
p.x, p.y, p.z, BlockNecroMemoryBook.AuthorHeight, BlockNecroMemoryBook.AuthorMaxWidth, n));
}
}
}