- Чертоги разума: дух в префабе chertogi под землёй, книга памяти, нож и плакат; кровать префаба - мебель necroPalaceBed (иначе становилась точкой возрождения); охрана выбрасывает и игроков (их нет в EntityAlives). - Хваты пустой руки по R: «отрицание» и «пиф-паф» - поза рук костями поверх аниматора, остальные кости заморожены, ЛКМ - заклинание, прицел краснеет на зомби. - «Отрицание»: зомби в ужасе бежит дальше 40 м и теряет игрока как цель. - «Пиф-паф»: одержимость зомби-гуманоидом - бег, прыжок, удар, дальняя атака, подрыв, крик крикуньи; тело цепенеет, зомби танкуют одержимого; область вокруг тела сжата; полоска здоровья; спавн и прорисовка у одержимого. - Книга с оглавлением (окно necroTome), глава «Некроэнергия». - Прорисовка дверей у змеи и одержимого (CheckLODs от камеры, а не от тела). - Почтовый ящик консоли и команды necrobones/necropose/necroshot для разработки. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
201 lines
8.5 KiB
C#
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));
|
|
}
|
|
|
|
}
|
|
}
|