Чертоги разума, хваты пустой руки, одержимость, книга с оглавлением
- Чертоги разума: дух в префабе chertogi под землёй, книга памяти, нож и плакат; кровать префаба - мебель necroPalaceBed (иначе становилась точкой возрождения); охрана выбрасывает и игроков (их нет в EntityAlives). - Хваты пустой руки по R: «отрицание» и «пиф-паф» - поза рук костями поверх аниматора, остальные кости заморожены, ЛКМ - заклинание, прицел краснеет на зомби. - «Отрицание»: зомби в ужасе бежит дальше 40 м и теряет игрока как цель. - «Пиф-паф»: одержимость зомби-гуманоидом - бег, прыжок, удар, дальняя атака, подрыв, крик крикуньи; тело цепенеет, зомби танкуют одержимого; область вокруг тела сжата; полоска здоровья; спавн и прорисовка у одержимого. - Книга с оглавлением (окно necroTome), глава «Некроэнергия». - Прорисовка дверей у змеи и одержимого (CheckLODs от камеры, а не от тела). - Почтовый ящик консоли и команды necrobones/necropose/necroshot для разработки. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c828202c66
commit
66a0dcf403
@@ -0,0 +1,200 @@
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user