diff --git a/HarmonySrc/OssuaryGlow.cs b/HarmonySrc/OssuaryGlow.cs
index c7b8e2f..38607be 100644
--- a/HarmonySrc/OssuaryGlow.cs
+++ b/HarmonySrc/OssuaryGlow.cs
@@ -17,6 +17,8 @@ namespace NecromancerTome
public static bool Enabled = true;
/// Дикий дух-огонёк (#69): летает, пока оссуарий работает. Выключатель - necroossuary wisp off.
public static bool WispEnabled = true;
+ /// necroossuary wisp log - писать в лог вылет и влёт (чтобы успеть снять).
+ public static bool WispLog;
public const string WispPath = "#@modfolder(NecromancerTome):Resources/necrowisp?necroWispPrefab.prefab";
static GameObject _wispPrefab;
static bool _wispTried;
@@ -196,7 +198,10 @@ namespace NecromancerTome
/// Окошки крышки (модель #67): шесть, между швами, через 60°; по высоте - на WindowUp от низа крышки,
/// от оси - на WindowR (доли полуширины крышки; в модели 0.14 м и 0.37 м при полуширине 0.418).
const float WindowUp = 0.14f / 0.836f, WindowR = 0.37f / 0.418f;
- const float Out = 0.9f; // насколько дальше от оси котла дух уходит в землю, м
+ /// Подъём и спуск - вертикально снаружи, на Clear дальше самого широкого места котла (22.10: «Огонёк летит сквозь
+ /// котёл. А надо чтобы поднимался почти на уровень окошка, и по дуге летел прямо через его центр»).
+ const float Clear = 0.25f;
+ const float BelowWindow = 0.1f; // вертикаль кончается на столько ниже окошка, м; дальше дуга
const float Under = 0.35f; // насколько под землю, м
const float FlyTime = 2.4f;
static readonly int TintId = Shader.PropertyToID("_TintColor");
@@ -208,7 +213,7 @@ namespace NecromancerTome
TrailRenderer _trail;
int _phase = -1; // -1 дома, 0 вылет и уход в землю, 1 под землёй, 2 подъём и влёт
float _t0, _until;
- Vector3 _winOut, _winIn, _inOut, _inIn, _down, _up;
+ Vector3[] _pathOut, _pathIn; // вылет: нутро -> окошко -> дуга -> вниз в землю; влёт - обратный порядок с другой стороны
static float Ease(float _k) => _k * _k * (3f - 2f * _k);
@@ -239,12 +244,16 @@ namespace NecromancerTome
{
return;
}
- Show(_inOut);
+ Show(_pathOut[0]);
+ if (OssuaryGlow.WispLog)
+ {
+ Debug.Log("[NecromancerTome] OssuaryWisp: вылет " + _pathOut[1]);
+ }
Next(0, FlyTime);
return;
case 0: // из окошка наружу и вниз, в землю; тает
- Fly(_inOut, _winOut, _down, k);
- Alpha(1f - Ease(Mathf.InverseLerp(0.25f, 1f, k)));
+ Fly(_pathOut, k);
+ Alpha(1f - Ease(Mathf.InverseLerp(0.45f, 1f, k)));
if (k >= 1f)
{
Hide();
@@ -255,13 +264,17 @@ namespace NecromancerTome
if (now >= _until)
{
Alpha(0f);
- Show(_up);
+ Show(_pathIn[0]);
+ if (OssuaryGlow.WispLog)
+ {
+ Debug.Log("[NecromancerTome] OssuaryWisp: влёт " + _pathIn[_pathIn.Length - 2]);
+ }
Next(2, FlyTime);
}
return;
case 2: // с другой стороны снизу вверх, проявляется, влетает в противоположное окошко
- Fly(_up, _winIn, _inIn, k);
- Alpha(Ease(Mathf.InverseLerp(0f, 0.75f, k)));
+ Fly(_pathIn, k);
+ Alpha(Ease(Mathf.InverseLerp(0f, 0.55f, k)));
if (k >= 1f)
{
Hide();
@@ -279,41 +292,77 @@ namespace NecromancerTome
_until = _t0 + _dur;
}
- /// Квадратичная кривая _a -> _b -> _c: проходит окошко _b по касательной.
- void Fly(Vector3 _a, Vector3 _b, Vector3 _c, float _k)
+ /// Гладкая кривая (Catmull-Rom) точно через точки пути; скорость по пути ровная (время - по длинам отрезков).
+ void Fly(Vector3[] _path, float _k)
{
- float k = Ease(_k);
- Vector3 p = (1f - k) * (1f - k) * _a + 2f * k * (1f - k) * _b + k * k * _c;
- p.y += 0.02f * Mathf.Sin(Time.time * 7f); // лёгкое дрожание огонька
+ float total = 0f;
+ for (int i = 1; i < _path.Length; i++)
+ {
+ total += Vector3.Distance(_path[i - 1], _path[i]);
+ }
+ float s = Ease(_k) * total;
+ int seg = 0;
+ float len = 0f;
+ for (; seg < _path.Length - 2; seg++)
+ {
+ len = Vector3.Distance(_path[seg], _path[seg + 1]);
+ if (s <= len)
+ {
+ break;
+ }
+ s -= len;
+ }
+ len = Vector3.Distance(_path[seg], _path[seg + 1]);
+ float u = len > 0.0001f ? Mathf.Clamp01(s / len) : 1f;
+ Vector3 p0 = _path[Mathf.Max(0, seg - 1)], p1 = _path[seg], p2 = _path[seg + 1], p3 = _path[Mathf.Min(_path.Length - 1, seg + 2)];
+ Vector3 p = 0.5f * (2f * p1 + (p2 - p0) * u + (2f * p0 - 5f * p1 + 4f * p2 - p3) * u * u + (3f * p1 - p0 - 3f * p2 + p3) * u * u * u);
+ p.y += 0.015f * Mathf.Sin(Time.time * 7f); // лёгкое дрожание огонька
_go.transform.position = p;
}
- /// Окошко вылета - случайное из шести, влёта - напротив; точки в земле - снаружи от них, высота земли - лучом.
+ /// Окошко вылета - случайное из шести, влёта - напротив. Путь: нутро, центр окошка, точка дуги снаружи на высоте
+ /// окошка, вертикаль снаружи котла чуть ниже окошка, та же вертикаль под землёй (высота земли - лучом).
bool Places(Transform _model)
{
Transform lid = _model.Find("lid");
+ Transform body = _model.Find("body");
Renderer lr = lid != null ? lid.GetComponent() : null;
+ Renderer br = body != null ? body.GetComponent() : null;
if (lr == null)
{
return false;
}
Bounds b = lr.bounds;
- Vector3 axis = new Vector3(b.center.x, b.min.y + WindowUp * (b.size.x + b.size.z) * 0.5f, b.center.z);
float half = (b.size.x + b.size.z) * 0.25f;
+ float bodyHalf = br != null ? (br.bounds.size.x + br.bounds.size.z) * 0.25f : half * 1.1f;
+ float winY = b.min.y + WindowUp * half * 2f;
+ Vector3 axis = new Vector3(b.center.x, winY, b.center.z);
int w = Random.Range(0, 6);
Vector3 dOut = lid.rotation * Quaternion.Euler(0f, w * 60f, 0f) * Vector3.right;
dOut.y = 0f;
dOut.Normalize();
- Vector3 dIn = -dOut;
- _winOut = axis + dOut * (half * WindowR);
- _winIn = axis + dIn * (half * WindowR);
- _inOut = axis + dOut * (half * WindowR * 0.4f);
- _inIn = axis + dIn * (half * WindowR * 0.4f);
- _down = Ground(axis + dOut * (half + Out)) + Vector3.down * Under;
- _up = Ground(axis + dIn * (half + Out)) + Vector3.down * Under;
+ _pathOut = Path(axis, dOut, half, Mathf.Max(bodyHalf, half) + Clear);
+ _pathIn = Path(axis, -dOut, half, Mathf.Max(bodyHalf, half) + Clear);
+ System.Array.Reverse(_pathIn);
return true;
}
+ Vector3[] Path(Vector3 _axis, Vector3 _dir, float _half, float _rOut)
+ {
+ float rWin = _half * WindowR;
+ Vector3 low = _axis + _dir * _rOut + Vector3.down * BelowWindow;
+ Vector3 under = Ground(_axis + _dir * _rOut) + Vector3.down * Under;
+ return new[]
+ {
+ _axis + _dir * (rWin * 0.45f), // внутри котла
+ _axis + _dir * rWin, // центр окошка
+ _axis + _dir * (rWin + (_rOut - rWin) * 0.55f), // дуга: снаружи на высоте окошка
+ low, // вертикаль снаружи, чуть ниже окошка
+ new Vector3(low.x, Mathf.Lerp(low.y, under.y, 0.5f), low.z),
+ under // в земле
+ };
+ }
+
Vector3 Ground(Vector3 _p)
{
if (Physics.Raycast(_p + Vector3.up * 1.5f, Vector3.down, out RaycastHit hit, 8f, SnakeControl.WallMask, QueryTriggerInteraction.Ignore))
@@ -547,6 +596,12 @@ namespace NecromancerTome
con.Output("necroossuary: пульсация " + OssuaryGlow.GlowPulse + ", скорость " + OssuaryGlow.GlowPulseSpeed);
break;
case "wisp":
+ if (_params.Count > 1 && _params[1] == "log")
+ {
+ OssuaryGlow.WispLog = !OssuaryGlow.WispLog;
+ con.Output("necroossuary: лог огонька " + (OssuaryGlow.WispLog ? "включён" : "выключен"));
+ break;
+ }
if (_params.Count > 1)
{
OssuaryGlow.WispEnabled = _params[1] == "on";
diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll
index e344fd4..26cb5cc 100644
Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ
diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb
index 9be68c6..dbdee98 100644
Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ