Логист - латунное кадило: два вида, дух 1/3 из окон, мигание; команды разработки

- Блоки логиста на модели кадила: по центру на подставке (necroCinerariumLogist) и новый вид у
  стены на кронштейне (necroCinerariumLogistWall/+Skull); выбор вида - R с урной в руке.
- Дух логиста втрое меньше, в покое сидит в кадиле, на перевозку вылетает через окно крышки
  (у стены - не в стену) и возвращается тем же окном.
- Застрял (ящик «куда» полон) - урна мигает (CinerariumBlink) вместо почерневшего духа;
  прежняя зелёная подмена свечения старой модели убрана.
- Команды разработки без мыши: necrospawn, necroexit [quit], necrolook, necroaim, necroplace,
  necrohold, necrobox, necrourn, necroclick.

Проверено в игре (test8): перевозка обоими видами, вылет духа, вид у стены от стены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-18 14:19:50 +03:00
co-authored by Claude Opus 5.5
parent b5bd4e4b2c
commit 6693d96d5e
11 changed files with 630 additions and 71 deletions
+87 -12
View File
@@ -74,7 +74,17 @@ namespace NecromancerTome
return _prefab != null;
}
public static GameObject Spawn(Transform _urnModel, float _drop)
/// <summary>Дух логиста (18.10: «Пусть дух вылетает когда нужно что-то нести. Но уменьшим его в три раза»): размер.</summary>
public static float LogistScale = 1f / 3f;
/// <summary>Дом духа логиста в осях модели урны - корень модели духа внутри кадила, середина тела на высоте окон крышки
/// (по центру - окна на 0.63 м, у стены - 0.73 м, кадило в 0.22 м от стены, стена - -z).</summary>
public static Vector3 LogistHome = new Vector3(0f, 0.25f, 0f);
public static Vector3 LogistWallHome = new Vector3(0f, 0.35f, -0.22f);
/// <summary>Окна крышки - на расстоянии LogistExit от оси кадила (радиус крышки 0.21 м), по осям модели.</summary>
public static float LogistExit = 0.3f;
/// <summary>_logist: 0 - обычный дух над горлышком, 1 - дух логиста в кадиле по центру, 2 - в кадиле у стены.</summary>
public static GameObject Spawn(Transform _urnModel, float _drop, int _logist = 0)
{
if (_urnModel == null || !Load())
{
@@ -107,6 +117,12 @@ namespace NecromancerTome
}
CinerariumGenieMotion m = g.AddComponent<CinerariumGenieMotion>();
m.Drop = _drop;
if (_logist > 0)
{
m.Small = true;
m.Wall = _logist == 2;
g.transform.localScale = Vector3.one * LogistScale;
}
m.Pose();
return g;
}
@@ -273,8 +289,14 @@ namespace NecromancerTome
{
public Vector3 Hover, Look; // в осях модели урны
public float Fly;
public bool Pass; // точка пути (окно кадила) - без жеста
}
/// <summary>Дух логиста (18.10): втрое меньше, дома (в кадиле) не виден, вылетает и влетает через окно крышки.</summary>
public bool Small;
/// <summary>Кадило у стены: дом сдвинут к кронштейну, окно в стену (-z) не используется.</summary>
public bool Wall;
readonly List<Stop> _stops = new List<Stop>();
float _backFly;
float _t = -1f;
@@ -374,11 +396,23 @@ namespace NecromancerTome
Vector3 home = Home();
Vector3 prev = home;
float delay = 0f;
Vector3 exit = home;
if (Small)
{
// Сначала - к окну крышки в сторону первого места (у стены окно в стену не годится), оттуда - к месту.
exit = Window(home, transform.parent.InverseTransformPoint(_list[0].stand) - home);
_stops.Add(new Stop { Hover = exit, Look = exit + (exit - home), Fly = 0.35f, Pass = true });
delay += 0.35f;
prev = exit;
}
foreach ((Vector3 stand, Vector3 look, bool beside) in _list)
{
// Высота корня духа над низом клетки места: обычный дух - над станцией 0.8 (сбоку 0.15) с поправкой урны;
// маленький - над верхом клетки (1.05) или у её середины сбоку (0.4).
float up = Small ? (beside ? 0.4f : 1.05f) : CinerariumGenie.OffsetY + Drop + (beside ? 0.15f : 0.8f);
Stop st = new Stop
{
Hover = transform.parent.InverseTransformPoint(stand) + new Vector3(0f, CinerariumGenie.OffsetY + Drop + (beside ? 0.15f : 0.8f), 0f),
Hover = transform.parent.InverseTransformPoint(stand) + new Vector3(0f, up, 0f),
Look = transform.parent.InverseTransformPoint(look)
};
st.Fly = Mathf.Max(OutSeconds, Vector3.Distance(prev, st.Hover) / FlySpeed);
@@ -386,13 +420,44 @@ namespace NecromancerTome
prev = st.Hover;
_stops.Add(st);
}
_backFly = Mathf.Max(BackSeconds, Vector3.Distance(prev, home) / FlySpeed);
if (Small)
{
// Назад - тем же окном.
_stops.Add(new Stop { Hover = exit, Look = home, Fly = Mathf.Max(BackSeconds, Vector3.Distance(prev, exit) / FlySpeed), Pass = true });
prev = exit;
}
_backFly = Small ? 0.35f : Mathf.Max(BackSeconds, Vector3.Distance(prev, home) / FlySpeed);
_t = 0f;
return delay - WorkSeconds * 0.5f;
}
/// <summary>Окно крышки кадила по направлению _dir (оси модели): одно из четырёх по осям x/z, у стены - не в стену.</summary>
Vector3 Window(Vector3 _home, Vector3 _dir)
{
Vector3 best = Vector3.forward;
float bestDot = float.MinValue;
foreach (Vector3 w in new[] { Vector3.forward, Vector3.back, Vector3.left, Vector3.right })
{
if (Wall && w == Vector3.back)
{
continue;
}
float d = Vector3.Dot(w, new Vector3(_dir.x, 0f, _dir.z));
if (d > bestDot)
{
bestDot = d;
best = w;
}
}
return _home + best * CinerariumGenie.LogistExit;
}
Vector3 Home()
{
if (Small)
{
return Wall ? CinerariumGenie.LogistWallHome : CinerariumGenie.LogistHome;
}
return new Vector3(0f, CinerariumGenie.OffsetY + Drop, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward);
}
@@ -483,14 +548,16 @@ namespace NecromancerTome
transform.localPosition = Home();
}
_asleep = asleep;
if (_hiddenApplied != CinerariumGenie.Hidden)
}
// Виден ли: не скрыт командой; дух логиста - только в полёте (дома сидит в кадиле). Рендереры - только при смене
// (16.10, проверка на утечки: раньше список рендереров собирался каждые 0.5 с - мусор для GC).
bool show = !CinerariumGenie.Hidden && (!Small || _t >= 0f);
if (_hiddenApplied != !show)
{
_hiddenApplied = !show;
foreach (Renderer r in GetComponentsInChildren<Renderer>(true))
{
// Только при смене (16.10, проверка на утечки: раньше список рендереров собирался каждые 0.5 с - мусор для GC).
_hiddenApplied = CinerariumGenie.Hidden;
foreach (Renderer r in GetComponentsInChildren<Renderer>(true))
{
r.enabled = !_hiddenApplied;
}
r.enabled = show;
}
}
if (_stuck || _asleep)
@@ -515,7 +582,7 @@ namespace NecromancerTome
return;
}
acc += st.Fly;
if (_t < acc + WorkSeconds)
if (!st.Pass && _t < acc + WorkSeconds)
{
transform.localPosition = st.Hover;
Face(st.Look - st.Hover); // за работой - лицом к месту
@@ -523,7 +590,10 @@ namespace NecromancerTome
transform.localRotation = Quaternion.Euler(0f, _yaw, 0f);
return;
}
acc += WorkSeconds;
if (!st.Pass)
{
acc += WorkSeconds;
}
from = st.Hover;
}
if (_t < acc + _backFly)
@@ -537,6 +607,11 @@ namespace NecromancerTome
_t = -1f;
Gesture(0f);
}
if (Small)
{
transform.localPosition = home; // в кадиле, не виден
return;
}
_yaw = yawTarget % 360f;
float bob = CinerariumGenie.BobPeriod > 0.01f
? Mathf.Sin((Time.time + _phase) * Mathf.PI * 2f / CinerariumGenie.BobPeriod) * CinerariumGenie.BobHeight