Files
necromants-tome-7d2d-3-2/HarmonySrc/ModelDumpCommand.cs
T
AlexCubeandClaude Opus 5.5 4e14a0562b Книга: главы ножа и Могильной крысы; плакат ножа, грифельная доска, дверь-шкаф; чертоги 25x8x25; плата силовой ножа 20/40
- Книга некроманта: главы «Нож некроманта» и «Могильная крыса» - лист-чертёж сверху (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>
2026-10-09 18:26:17 +03:00

129 lines
4.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Scripting;
namespace NecromancerTome
{
/// <summary>
/// necromodel &lt;блок&gt; - выписать в лог устройство модели блока (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;
}
}
}