diff --git a/HarmonySrc/Cinerarium.cs b/HarmonySrc/Cinerarium.cs index 1a2f7af..38eac46 100644 --- a/HarmonySrc/Cinerarium.cs +++ b/HarmonySrc/Cinerarium.cs @@ -198,10 +198,47 @@ namespace NecromancerTome { _pending = true; _actAt = Time.time + (motion != null ? CinerariumGenieMotion.ActDelay : 0f); - motion?.Visit(World.blockToTransformPos(at.Value) - Origin.position); + if (motion != null) + { + Vector3 look = World.blockToTransformPos(at.Value) - Origin.position; + motion.Visit(Stand(_world, at.Value, out bool beside) - Origin.position, look, beside); + } } } + /// Где духу встать у места работы (16.10: «влетает в станцию в самый центр... логичнее к краю станции с + /// доступной стороны»): из четырёх соседних клеток - свободная (воздух или растение), ближайшая к урне; дух встаёт у + /// её края со стороны места. Свободной нет - над местом. Возвращает низ точки в координатах мира (blockToTransformPos). + Vector3 Stand(World _world, Vector3i _at, out bool _beside) + { + Vector3i urn = ToWorldPos(); + Vector3i best = _at; + int bestD = int.MaxValue; + foreach (Vector3i dir in new[] { Vector3i.forward, Vector3i.back, Vector3i.left, Vector3i.right }) + { + Vector3i n = _at + dir; + BlockValue bv = _world.GetBlock(n); + if (!bv.isair && !(bv.Block is BlockPlant)) + { + continue; + } + int d = (n.x - urn.x) * (n.x - urn.x) + (n.z - urn.z) * (n.z - urn.z); + if (d < bestD) + { + bestD = d; + best = n; + } + } + _beside = best != _at; + Vector3 at = World.blockToTransformPos(_at); + if (!_beside) + { + return at; + } + Vector3 n0 = World.blockToTransformPos(best); + return Vector3.Lerp(n0, at, 0.2f); // чуть ближе к месту - у самого края + } + // ------------------------------------------------------------------ // Череп. // ------------------------------------------------------------------ diff --git a/HarmonySrc/CinerariumGenie.cs b/HarmonySrc/CinerariumGenie.cs index e7cb347..055609b 100644 --- a/HarmonySrc/CinerariumGenie.cs +++ b/HarmonySrc/CinerariumGenie.cs @@ -309,14 +309,20 @@ namespace NecromancerTome /// Светящиеся материалы рендереров - вернуть после почернения. readonly Dictionary _glowing = new Dictionary(); - /// _worldUnity - точка в координатах сцены (без Origin): низ блока, где дух работал. - public void Visit(Vector3 _worldUnity) + /// Куда смотреть за работой (в координатах модели урны); _beside - дух стоит сбоку от места, а не над ним. + Vector3 _look; + bool _beside; + + /// Точки - в координатах сцены (без Origin): _stand - где встать (низ клетки), _look - низ клетки места работы. + public void Visit(Vector3 _stand, Vector3 _lookAt, bool _besideIt) { if (transform.parent == null) { return; } - _visit = transform.parent.InverseTransformPoint(_worldUnity); + _visit = transform.parent.InverseTransformPoint(_stand); + _look = transform.parent.InverseTransformPoint(_lookAt); + _beside = _besideIt; _t = 0f; } @@ -379,10 +385,13 @@ namespace NecromancerTome _t += Time.deltaTime; if (_t < OutSeconds + WorkSeconds + BackSeconds) { - Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + 0.6f, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward); + // Сбоку - на уровне места (тело вровень со станцией), над местом - выше него. + Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + (_beside ? 0.15f : 0.8f), 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward); Vector3 d = _visit - new Vector3(0f, CinerariumGenie.OffsetY, 0f); bool far = d.x * d.x + d.z * d.z > 0.01f; float toPlace = far ? Mathf.Atan2(d.x, d.z) * Mathf.Rad2Deg : _yaw; + Vector3 dl = _look - _visit; + float toLook = dl.x * dl.x + dl.z * dl.z > 0.01f ? Mathf.Atan2(dl.x, dl.z) * Mathf.Rad2Deg : toPlace; float k, face, w = 0f; if (_t < OutSeconds) { @@ -392,7 +401,7 @@ namespace NecromancerTome else if (_t < OutSeconds + WorkSeconds) { k = 1f; - face = toPlace; + face = toLook; // за работой - лицом к станции w = Mathf.Sin((_t - OutSeconds) / WorkSeconds * Mathf.PI); } else