- Книга некроманта: главы «Нож некроманта» и «Могильная крыса» - лист-чертёж сверху (Textures/, три вида из Blender, подписи на эсперанто), управление и лор; окно книги непрозрачное, текст с контуром - «Дух крысы» переименован в «Могильную крысу» на 13 языках (ключи прежние) - Блоки (Dev): плакат-синька ножа (necroPosterKnife, 3:2 под полотно рамки), грифельная доска necroChalkboard (надписи - TEFeatureSignable, писать только в редакторе/творческом; шрифт Neucha OFL, свой шейдер текста с тестом глубины), дверь-книжный шкаф necroBookDoor (своя модель с аниматором, хитбокс створки через RootTransformRefParent) - Чертоги: префаб 25x8x25 (промежуточный); открываются только межкомнатные двери; надписи на досках по дням игры: «Отомсти архимагу!», с 5-го дня «Берегись излучения красной луны» - Нож: силовая атака стоит 20 здоровья, с «Мёртвой бурей» 40; не хватает - itembreak, «Недостаточно здоровья» и обычный удар; описания на 13 языках - Консольная команда necromodel - устройство модели блока в лог Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
129 lines
4.8 KiB
C#
129 lines
4.8 KiB
C#
using System.Collections.Generic;
|
||
using System.Text;
|
||
using UnityEngine;
|
||
using UnityEngine.Scripting;
|
||
|
||
namespace NecromancerTome
|
||
{
|
||
/// <summary>
|
||
/// necromodel <блок> - выписать в лог устройство модели блока (09.10.2026, для своей двери-книжного
|
||
/// шкафа: где у ванильной двери петля, куда она открывается, какие коллайдеры). Иерархия с местом, поворотом,
|
||
/// масштабом, мешами и коллайдерами; у аниматоров - параметры и клипы, а створка в состояниях «Open» и
|
||
/// «Close» - пробой клипа на копии префаба. Копия создаётся и тут же уничтожается.
|
||
/// </summary>
|
||
[Preserve]
|
||
public class ConsoleCmdNecroModel : ConsoleCmdAbstract
|
||
{
|
||
public override string[] getCommands() => new[] { "necromodel" };
|
||
public override string getDescription() => "NecromancerTome: устройство модели блока в лог (necromodel <имя блока>)";
|
||
|
||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo)
|
||
{
|
||
if (_params.Count < 1)
|
||
{
|
||
SdtdConsole.Instance.Output("necromodel <имя блока>");
|
||
return;
|
||
}
|
||
Block block = Block.GetBlockByName(_params[0]);
|
||
string model = block?.Properties.GetString("Model");
|
||
if (string.IsNullOrEmpty(model))
|
||
{
|
||
SdtdConsole.Instance.Output("Нет блока или у него нет Model: " + _params[0]);
|
||
return;
|
||
}
|
||
if (!model.StartsWith("@") && !model.StartsWith("#"))
|
||
{
|
||
model = "@:" + model;
|
||
}
|
||
GameObject prefab = DataLoader.LoadAsset<GameObject>(model);
|
||
if (prefab == null)
|
||
{
|
||
SdtdConsole.Instance.Output("Модель не загрузилась: " + model);
|
||
return;
|
||
}
|
||
var sb = new StringBuilder();
|
||
sb.AppendLine("[NecromancerTome] necromodel " + _params[0] + " = " + model + ", ModelOffset " +
|
||
block.Properties.GetString("ModelOffset") + ", MultiBlockDim " + block.Properties.GetString("MultiBlockDim"));
|
||
Dump(prefab.transform, 0, sb);
|
||
GameObject copy = Object.Instantiate(prefab);
|
||
try
|
||
{
|
||
foreach (Animator a in copy.GetComponentsInChildren<Animator>(true))
|
||
{
|
||
RuntimeAnimatorController rc = a.runtimeAnimatorController;
|
||
sb.AppendLine("Animator на " + a.name + ": " + (rc != null ? rc.name : "нет контроллера"));
|
||
foreach (AnimatorControllerParameter p in a.parameters)
|
||
{
|
||
sb.AppendLine(" параметр " + p.name + " " + p.type);
|
||
}
|
||
if (rc == null)
|
||
{
|
||
continue;
|
||
}
|
||
foreach (AnimationClip clip in rc.animationClips)
|
||
{
|
||
sb.AppendLine(" клип " + clip.name + ", " + clip.length.ToString("F2") + " с");
|
||
clip.SampleAnimation(copy, clip.length);
|
||
foreach (Transform t in copy.GetComponentsInChildren<Transform>(true))
|
||
{
|
||
sb.AppendLine(" в конце: " + Path(t, copy.transform) + " pos " + t.localPosition.ToString("F3") +
|
||
" rot " + t.localEulerAngles.ToString("F1"));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
Object.Destroy(copy);
|
||
}
|
||
Debug.Log(sb.ToString());
|
||
SdtdConsole.Instance.Output("necromodel: выписано в лог");
|
||
}
|
||
|
||
static void Dump(Transform _t, int _depth, StringBuilder _sb)
|
||
{
|
||
var line = new StringBuilder(new string(' ', _depth * 2));
|
||
line.Append(_t.name).Append(" pos ").Append(_t.localPosition.ToString("F3")).Append(" rot ")
|
||
.Append(_t.localEulerAngles.ToString("F1")).Append(" scale ").Append(_t.localScale.ToString("F2"))
|
||
.Append(" tag ").Append(_t.tag).Append(" layer ").Append(_t.gameObject.layer);
|
||
MeshFilter mf = _t.GetComponent<MeshFilter>();
|
||
if (mf != null && mf.sharedMesh != null)
|
||
{
|
||
line.Append(" mesh ").Append(mf.sharedMesh.name).Append(" bounds c").Append(mf.sharedMesh.bounds.center.ToString("F3"))
|
||
.Append(" size").Append(mf.sharedMesh.bounds.size.ToString("F3"));
|
||
}
|
||
foreach (Collider c in _t.GetComponents<Collider>())
|
||
{
|
||
line.Append(" | ").Append(c.GetType().Name).Append(c.isTrigger ? "(trigger)" : "");
|
||
if (c is BoxCollider b)
|
||
{
|
||
line.Append(" c").Append(b.center.ToString("F3")).Append(" s").Append(b.size.ToString("F3"));
|
||
}
|
||
}
|
||
foreach (Component comp in _t.GetComponents<Component>())
|
||
{
|
||
if (!(comp is Transform) && !(comp is MeshFilter) && !(comp is MeshRenderer) && !(comp is Collider))
|
||
{
|
||
line.Append(" [").Append(comp != null ? comp.GetType().Name : "null").Append("]");
|
||
}
|
||
}
|
||
_sb.AppendLine(line.ToString());
|
||
foreach (Transform ch in _t)
|
||
{
|
||
Dump(ch, _depth + 1, _sb);
|
||
}
|
||
}
|
||
|
||
static string Path(Transform _t, Transform _root)
|
||
{
|
||
string p = _t.name;
|
||
while (_t.parent != null && _t != _root)
|
||
{
|
||
_t = _t.parent;
|
||
p = _t.name + "/" + p;
|
||
}
|
||
return p;
|
||
}
|
||
}
|
||
}
|