Оссуарий: три образца - столбец хранилища под каждым; одна ячейка топлива; огонёк в окошки; трассер (#66, #69)
- 22.10: «Пусть можно будет положить 3 разных образца, и тогда в ячейки под ними пойдут такие же ресурсы»: выходы os1/os4 - образец 1, os2/os5 - 2, os3/os6 - 3. Описание на 13 языках поправлено. - Лишние ячейки топлива снова показывались: сетка топлива каждый кадр выставляет видимость по высоте - прятать в Update. Видна одна. - Огонёк: окошки на 30°, 90°... (на 0°, 60°... - полосы металла; 22.10 «Светлячок заходит в полосы металла, и игнорирует окошки»). - necroossuary wisp trace - линии путей огонька; necroossuary open - окно как по E. Проверено в test8: окно (3 образца, 1 топливо, хранилище 2x3), уголь во втором образце -> стак угля в 5-й ячейке; трассер - путь через центр окошка. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ead6823c8a
commit
be7c203abe
+101
-8
@@ -19,6 +19,9 @@ namespace NecromancerTome
|
||||
public static bool WispEnabled = true;
|
||||
/// <summary>necroossuary wisp log - писать в лог вылет и влёт (чтобы успеть снять).</summary>
|
||||
public static bool WispLog;
|
||||
/// <summary>necroossuary wisp trace - трассер: линии обоих путей огонька (вылет и влёт) у каждого котла (22.10: «А не проще
|
||||
/// для отладки нарисовать трассер, по которому будет лететь огонёк?»).</summary>
|
||||
public static bool WispTrace;
|
||||
public const string WispPath = "#@modfolder(NecromancerTome):Resources/necrowisp?necroWispPrefab.prefab";
|
||||
static GameObject _wispPrefab;
|
||||
static bool _wispTried;
|
||||
@@ -195,9 +198,12 @@ namespace NecromancerTome
|
||||
public class OssuaryWisp
|
||||
{
|
||||
public const float WispViewRange = 60f;
|
||||
/// <summary>Окошки крышки (модель #67): шесть, между швами, через 60°; по высоте - на WindowUp от низа крышки,
|
||||
/// от оси - на WindowR (доли полуширины крышки; в модели 0.14 м и 0.37 м при полуширине 0.418).</summary>
|
||||
const float WindowUp = 0.14f / 0.836f, WindowR = 0.37f / 0.418f;
|
||||
/// <summary>Окошки крышки (модель #67): шесть, через 60°, со сдвигом WindowAngle от оси X модели (рамки окошек в сетке -
|
||||
/// на 30°, 90°...; на 0°, 60°... - металлические полосы: 22.10 «Светлячок заходит в полосы металла, и игнорирует окошки»).
|
||||
/// По высоте - на WindowUp от низа крышки, от оси - на WindowR (доли размеров крышки; в модели окошко 0.46-0.56 м
|
||||
/// при низе крышки 0.382, радиус 0.37 при полуширине 0.418).</summary>
|
||||
const float WindowAngle = 30f;
|
||||
const float WindowUp = 0.13f / 0.836f, WindowR = 0.37f / 0.418f;
|
||||
/// <summary>Подъём и спуск - вертикально снаружи, на Clear дальше самого широкого места котла (22.10: «Огонёк летит сквозь
|
||||
/// котёл. А надо чтобы поднимался почти на уровень окошка, и по дуге летел прямо через его центр»).</summary>
|
||||
const float Clear = 0.25f;
|
||||
@@ -213,6 +219,7 @@ namespace NecromancerTome
|
||||
TrailRenderer _trail;
|
||||
int _phase = -1; // -1 дома, 0 вылет и уход в землю, 1 под землёй, 2 подъём и влёт
|
||||
float _t0, _until;
|
||||
LineRenderer _traceOut, _traceIn;
|
||||
Vector3[] _pathOut, _pathIn; // вылет: нутро -> окошко -> дуга -> вниз в землю; влёт - обратный порядок с другой стороны
|
||||
|
||||
static float Ease(float _k) => _k * _k * (3f - 2f * _k);
|
||||
@@ -236,6 +243,14 @@ namespace NecromancerTome
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!OssuaryGlow.WispTrace && _traceOut != null && _traceOut.enabled)
|
||||
{
|
||||
_traceOut.enabled = _traceIn.enabled = false;
|
||||
}
|
||||
else if (OssuaryGlow.WispTrace && _pathOut == null)
|
||||
{
|
||||
Places(_model);
|
||||
}
|
||||
float k = Mathf.Clamp01((now - _t0) / Mathf.Max(0.01f, _until - _t0));
|
||||
switch (_phase)
|
||||
{
|
||||
@@ -294,13 +309,21 @@ namespace NecromancerTome
|
||||
|
||||
/// <summary>Гладкая кривая (Catmull-Rom) точно через точки пути; скорость по пути ровная (время - по длинам отрезков).</summary>
|
||||
void Fly(Vector3[] _path, float _k)
|
||||
{
|
||||
Vector3 p = At(_path, Ease(_k));
|
||||
p.y += 0.015f * Mathf.Sin(Time.time * 7f); // лёгкое дрожание огонька
|
||||
_go.transform.position = p;
|
||||
}
|
||||
|
||||
/// <summary>Точка пути на доле длины _s (0..1).</summary>
|
||||
static Vector3 At(Vector3[] _path, float _s01)
|
||||
{
|
||||
float total = 0f;
|
||||
for (int i = 1; i < _path.Length; i++)
|
||||
{
|
||||
total += Vector3.Distance(_path[i - 1], _path[i]);
|
||||
}
|
||||
float s = Ease(_k) * total;
|
||||
float s = _s01 * total;
|
||||
int seg = 0;
|
||||
float len = 0f;
|
||||
for (; seg < _path.Length - 2; seg++)
|
||||
@@ -315,9 +338,7 @@ namespace NecromancerTome
|
||||
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;
|
||||
return 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);
|
||||
}
|
||||
|
||||
/// <summary>Окошко вылета - случайное из шести, влёта - напротив. Путь: нутро, центр окошка, точка дуги снаружи на высоте
|
||||
@@ -338,12 +359,13 @@ namespace NecromancerTome
|
||||
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;
|
||||
Vector3 dOut = lid.rotation * Quaternion.Euler(0f, WindowAngle + w * 60f, 0f) * Vector3.right;
|
||||
dOut.y = 0f;
|
||||
dOut.Normalize();
|
||||
_pathOut = Path(axis, dOut, half, Mathf.Max(bodyHalf, half) + Clear);
|
||||
_pathIn = Path(axis, -dOut, half, Mathf.Max(bodyHalf, half) + Clear);
|
||||
System.Array.Reverse(_pathIn);
|
||||
Trace(_model);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -363,6 +385,49 @@ namespace NecromancerTome
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Трассер: путь вылета и путь влёта линиями, точками той же кривой, по которой летит огонёк.</summary>
|
||||
void Trace(Transform _model)
|
||||
{
|
||||
if (!OssuaryGlow.WispTrace || _trail == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_traceOut == null)
|
||||
{
|
||||
_traceOut = Line(_model, "NecroWispTraceOut", new Color(1f, 0.6f, 0.1f));
|
||||
_traceIn = Line(_model, "NecroWispTraceIn", new Color(0.2f, 0.8f, 1f));
|
||||
}
|
||||
Fill(_traceOut, _pathOut);
|
||||
Fill(_traceIn, _pathIn);
|
||||
}
|
||||
|
||||
LineRenderer Line(Transform _model, string _name, Color _c)
|
||||
{
|
||||
var g = new GameObject(_name);
|
||||
g.transform.SetParent(_model, false);
|
||||
LineRenderer l = g.AddComponent<LineRenderer>();
|
||||
l.sharedMaterial = _trail.sharedMaterial;
|
||||
l.useWorldSpace = true;
|
||||
l.widthMultiplier = 0.015f;
|
||||
l.startColor = l.endColor = _c;
|
||||
l.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
l.receiveShadows = false;
|
||||
return l;
|
||||
}
|
||||
|
||||
void Fill(LineRenderer _l, Vector3[] _path)
|
||||
{
|
||||
const int n = 60;
|
||||
var pts = new Vector3[n + 1];
|
||||
for (int i = 0; i <= n; i++)
|
||||
{
|
||||
pts[i] = At(_path, (float)i / n);
|
||||
}
|
||||
_l.positionCount = pts.Length;
|
||||
_l.SetPositions(pts);
|
||||
_l.enabled = true;
|
||||
}
|
||||
|
||||
Vector3 Ground(Vector3 _p)
|
||||
{
|
||||
if (Physics.Raycast(_p + Vector3.up * 1.5f, Vector3.down, out RaycastHit hit, 8f, SnakeControl.WallMask, QueryTriggerInteraction.Ignore))
|
||||
@@ -451,6 +516,14 @@ namespace NecromancerTome
|
||||
Object.Destroy(_go);
|
||||
_go = null;
|
||||
}
|
||||
foreach (LineRenderer l in new[] { _traceOut, _traceIn })
|
||||
{
|
||||
if (l != null)
|
||||
{
|
||||
Object.Destroy(l.gameObject);
|
||||
}
|
||||
}
|
||||
_traceOut = _traceIn = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,6 +564,8 @@ namespace NecromancerTome
|
||||
"necroossuary remove x y z - remove the ossuary at x y z (only if it is an ossuary)\n" +
|
||||
"necroossuary on|off - off: sphere and glow no longer follow the ossuary state\n" +
|
||||
"necroossuary wisp on|off - wild spirit wisp flying while the ossuary works\n" +
|
||||
"necroossuary wisp trace|log - dev: draw the wisp paths / log take-off and return\n" +
|
||||
"necroossuary open x y z - dev: open the ossuary window as with E\n" +
|
||||
"necroossuary info x y z - slots of the ossuary (samples, fuel, storage) and whether it works\n" +
|
||||
"necroossuary put x y z mod|fuel <slot 1..> <item|none> [count] - dev: put an item into a slot (none - empty it)\n" +
|
||||
"necroossuary color r g b - glow color (HDR, e.g. 6 1.4 0.35)\n" +
|
||||
@@ -534,6 +609,18 @@ namespace NecromancerTome
|
||||
con.Output("necroossuary: убран оссуарий в " + p);
|
||||
break;
|
||||
}
|
||||
case "open":
|
||||
{
|
||||
var p = new Vector3i(int.Parse(_params[1]), int.Parse(_params[2]), int.Parse(_params[3]));
|
||||
BlockValue bv = w.GetBlock(p);
|
||||
if (bv.ischild)
|
||||
{
|
||||
p = p + bv.parent;
|
||||
bv = w.GetBlock(p);
|
||||
}
|
||||
con.Output("necroossuary: открыть " + p + " - " + (GeoOssuary.Is(bv.Block) && bv.Block.OnBlockActivated(w, p, bv, pl)));
|
||||
break;
|
||||
}
|
||||
case "info":
|
||||
case "put":
|
||||
{
|
||||
@@ -596,6 +683,12 @@ namespace NecromancerTome
|
||||
con.Output("necroossuary: пульсация " + OssuaryGlow.GlowPulse + ", скорость " + OssuaryGlow.GlowPulseSpeed);
|
||||
break;
|
||||
case "wisp":
|
||||
if (_params.Count > 1 && _params[1] == "trace")
|
||||
{
|
||||
OssuaryGlow.WispTrace = !OssuaryGlow.WispTrace;
|
||||
con.Output("necroossuary: трассер огонька " + (OssuaryGlow.WispTrace ? "включён (оранжевый - вылет, голубой - влёт)" : "выключен"));
|
||||
break;
|
||||
}
|
||||
if (_params.Count > 1 && _params[1] == "log")
|
||||
{
|
||||
OssuaryGlow.WispLog = !OssuaryGlow.WispLog;
|
||||
|
||||
Reference in New Issue
Block a user