using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.Scripting; namespace NecromancerTome { /// /// necromodel <блок> - выписать в лог устройство модели блока (09.10.2026, для своей двери-книжного /// шкафа: где у ванильной двери петля, куда она открывается, какие коллайдеры). Иерархия с местом, поворотом, /// масштабом, мешами и коллайдерами; у аниматоров - параметры и клипы, а створка в состояниях «Open» и /// «Close» - пробой клипа на копии префаба. Копия создаётся и тут же уничтожается. /// [Preserve] public class ConsoleCmdNecroModel : ConsoleCmdAbstract { public override string[] getCommands() => new[] { "necromodel" }; public override string getDescription() => "NecromancerTome: устройство модели блока в лог (necromodel <имя блока>)"; public override void Execute(List _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(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(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(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(); 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()) { 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()) { 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; } } }