using System.Collections.Generic;
using HarmonyLib;
using UnityEngine;
namespace NecromancerTome
{
///
/// Новый оссуарий-котёл (#67, 22.10.2026): свечение модели и отладочная команда necroossuary.
/// Свечение - руны на котле, знаки на крышке, прах и кости внутри (карта _e, цвет - EmissionColor через MaterialPropertyBlock,
/// общий материал не трогается) и свет glow внутри котла (дочерний объект префаба, выключен) - пока оссуарий исправно работает
/// (22.10: «Когда оссуарий исправно работает, он должен подсвечиваться изнутри. Чтобы сразу было видно если что-то не так»;
/// «Пусть руны тоже загораются»). Сфера в держателе (sphere) видна, пока в топливе есть сфера или камень. Следит компонент
/// OssuaryVisual на модели - поэтому работает и у клиентов сервера. Выключатель - necroossuary off (модель как в префабе).
///
public static class OssuaryGlow
{
public static bool Enabled = true;
/// Дикий дух-огонёк (#69): летает, пока оссуарий работает. Выключатель - necroossuary wisp off.
public static bool WispEnabled = true;
/// necroossuary wisp log - писать в лог вылет и влёт (чтобы успеть снять).
public static bool WispLog;
/// necroossuary wisp trace - трассер: линии обоих путей огонька (вылет и влёт) у каждого котла (22.10: «А не проще
/// для отладки нарисовать трассер, по которому будет лететь огонёк?»).
public static bool WispTrace;
public const string WispPath = "#@modfolder(NecromancerTome):Resources/necrowisp?necroWispPrefab.prefab";
static GameObject _wispPrefab;
static bool _wispTried;
public static GameObject WispPrefab()
{
if (!_wispTried)
{
_wispTried = true;
_wispPrefab = DataLoader.LoadAsset(WispPath);
Debug.Log("[NecromancerTome] OssuaryGlow: огонёк " + (_wispPrefab != null ? "загружен" : "НЕ загружен: " + WispPath));
}
return _wispPrefab;
}
/// Цвет свечения (HDR). 22.10: было (2, 0.24, 0.12) - «Руны нарисованы красным. Это не выглядит как свечение»:
/// днём смотрелось краской. Теперь ярче и теплее, как раскалённый уголь, с пульсацией. Подбор - necroossuary color / pulse.
public static Color GlowColor = new Color(6f, 1.4f, 0.35f, 1f);
/// Размах пульсации свечения (доля яркости) и её скорость (радиан в секунду).
public static float GlowPulse = 0.3f, GlowPulseSpeed = 1.6f;
static readonly MaterialPropertyBlock _block = new MaterialPropertyBlock();
static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor");
public static Transform Model(World _w, Vector3i _pos)
{
Chunk chunk = _w.GetChunkFromWorldPos(_pos) as Chunk;
BlockEntityData be = chunk?.GetBlockEntity(_pos);
return be != null && be.bHasTransform ? be.transform : null;
}
/// true - модель найдена и переключена.
public static bool Set(Transform _model, bool _on, bool _sphere = true)
{
if (_model == null)
{
return false;
}
foreach (string part in new[] { "body", "lid" })
{
Transform t = _model.Find(part);
Renderer r = t != null ? t.GetComponent() : null;
if (r == null)
{
continue;
}
r.GetPropertyBlock(_block);
_block.SetColor(EmissionColorId, _on ? GlowColor : Color.black);
r.SetPropertyBlock(_block);
}
Transform glow = _model.Find("glow");
if (glow != null)
{
glow.gameObject.SetActive(_on);
}
Transform sphere = _model.Find("sphere");
if (sphere != null)
{
sphere.gameObject.SetActive(_sphere);
}
return true;
}
/// Пульсация: свечение рун и нутра - GlowColor с множителем _k (каждый кадр, пока светится).
public static void Pulse(Transform _model, float _k)
{
foreach (string part in new[] { "body", "lid" })
{
Transform t = _model.Find(part);
Renderer r = t != null ? t.GetComponent() : null;
if (r == null)
{
continue;
}
r.GetPropertyBlock(_block);
_block.SetColor(EmissionColorId, GlowColor * _k);
r.SetPropertyBlock(_block);
}
}
/// Оссуарии (главные блоки) в кубе вокруг точки.
public static List Find(World _w, Vector3i _c, int _r, int _ry)
{
var res = new List();
for (int x = _c.x - _r; x <= _c.x + _r; x++)
{
for (int z = _c.z - _r; z <= _c.z + _r; z++)
{
for (int y = Mathf.Max(1, _c.y - _ry); y <= Mathf.Min(254, _c.y + _ry); y++)
{
var p = new Vector3i(x, y, z);
BlockValue bv = _w.GetBlock(p);
if (!bv.ischild && GeoOssuary.Is(bv.Block))
{
res.Add(p);
}
}
}
}
return res;
}
}
/// На модели оссуария: два раза в секунду смотрит на коллектор и переключает сферу и свечение, только при смене.
/// anyEnabled - ванильная проверка «есть рабочий выход»: учитывает топливо, заполненный выход, воду и пустые ячейки
/// модификаций (GeoOssuaryPatch.isDisabled).
public class OssuaryVisual : MonoBehaviour
{
public Vector3i Pos;
float _next;
int _state = -1;
readonly float _phase = Random.Range(0f, 6.28f); // соседние котлы пульсируют не в такт
void Update()
{
bool lit = _state > 0 && (_state & 1) != 0 && OssuaryGlow.Enabled; // -1 - ещё не смотрели
_wisp.Tick(transform, lit && OssuaryGlow.WispEnabled);
if (lit && OssuaryGlow.GlowPulse > 0f)
{
OssuaryGlow.Pulse(transform, 1f + OssuaryGlow.GlowPulse * Mathf.Sin(Time.time * OssuaryGlow.GlowPulseSpeed + _phase));
}
if (Time.time < _next)
{
return;
}
_next = Time.time + 0.5f;
World w = GameManager.Instance?.World;
if (w == null)
{
return;
}
int st; // бит 1 - светится, бит 2 - сфера видна; выключатель - как в префабе (2)
if (OssuaryGlow.Enabled)
{
st = 0;
if (w.GetTileEntity(Pos) is TileEntityCollector te)
{
bool fuel = false;
foreach (ItemStack s in te.FuelSlots)
{
if (s != null && !s.IsEmpty())
{
fuel = true;
break;
}
}
st = (fuel ? 2 : 0) + (fuel && te.anyEnabled() ? 1 : 0);
}
}
else
{
st = 2;
}
if (st == _state)
{
return;
}
_state = st;
OssuaryGlow.Set(transform, (st & 1) != 0, (st & 2) != 0);
}
readonly OssuaryWisp _wisp = new OssuaryWisp();
void OnDestroy()
{
_wisp.Destroy();
}
}
/// Дикий дух оссуария (#69; 20.10: «Хватит одного огонька. Пусть светится сам. Производительность нам важна»):
/// чистая анимация, без сущностей (22.10: «периодически из окошка в котле вылетает дух и уходит вниз, увеличивая прозрачность
/// до полной, а через какое-то время с другой стороны снизу вылетает полностью прозрачный дух, плавно уменьшая прозрачность,
/// и влетает в противоположное окошко»). Один огонёк на котёл (бандл necrowisp), только пока оссуарий исправно работает
/// и игрок ближе WispViewRange. На добычу не влияет.
public class OssuaryWisp
{
public const float WispViewRange = 60f;
/// Окошки крышки (модель #67): шесть, через 60°, со сдвигом WindowAngle от оси X модели (рамки окошек в сетке -
/// на 30°, 90°...; на 0°, 60°... - металлические полосы: 22.10 «Светлячок заходит в полосы металла, и игнорирует окошки»).
/// По высоте - на WindowUp от низа крышки, от оси - на WindowR (доли размеров крышки; в модели окошко 0.46-0.56 м
/// при низе крышки 0.382, радиус 0.37 при полуширине 0.418).
const float WindowAngle = 30f;
const float WindowUp = 0.13f / 0.836f, WindowR = 0.37f / 0.418f;
/// Подъём и спуск - вертикально снаружи, на 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");
static readonly int ColorId = Shader.PropertyToID("_Color");
static readonly MaterialPropertyBlock _mpb = new MaterialPropertyBlock();
GameObject _go;
Renderer[] _rs;
Color[] _base;
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);
public void Tick(Transform _model, bool _work)
{
EntityPlayerLocal pl = GameManager.Instance?.World?.GetPrimaryPlayer();
bool near = pl != null && (pl.position - Origin.position - _model.position).sqrMagnitude < WispViewRange * WispViewRange;
float now = Time.time;
if (!_work || !near)
{
if (_phase >= 0)
{
Hide();
_phase = -1;
}
_until = Mathf.Max(_until, now + Random.Range(1f, 4f));
return;
}
if (_go == null && !Spawn(_model))
{
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)
{
case -1:
if (now < _until || !Places(_model))
{
return;
}
Show(_pathOut[0]);
if (OssuaryGlow.WispLog)
{
Debug.Log("[NecromancerTome] OssuaryWisp: вылет " + _pathOut[1]);
}
Next(0, FlyTime);
return;
case 0: // из окошка наружу и вниз, в землю; тает
Fly(_pathOut, k);
Alpha(1f - Ease(Mathf.InverseLerp(0.45f, 1f, k)));
if (k >= 1f)
{
Hide();
Next(1, Random.Range(12f, 20f));
}
return;
case 1: // под землёй
if (now >= _until)
{
Alpha(0f);
Show(_pathIn[0]);
if (OssuaryGlow.WispLog)
{
Debug.Log("[NecromancerTome] OssuaryWisp: влёт " + _pathIn[_pathIn.Length - 2]);
}
Next(2, FlyTime);
}
return;
case 2: // с другой стороны снизу вверх, проявляется, влетает в противоположное окошко
Fly(_pathIn, k);
Alpha(Ease(Mathf.InverseLerp(0f, 0.55f, k)));
if (k >= 1f)
{
Hide();
_phase = -1;
_until = now + Random.Range(3f, 6f);
}
return;
}
}
void Next(int _p, float _dur)
{
_phase = _p;
_t0 = Time.time;
_until = _t0 + _dur;
}
/// Гладкая кривая (Catmull-Rom) точно через точки пути; скорость по пути ровная (время - по длинам отрезков).
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;
}
/// Точка пути на доле длины _s (0..1).
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 = _s01 * 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)];
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);
}
/// Окошко вылета - случайное из шести, влёта - напротив. Путь: нутро, центр окошка, точка дуги снаружи на высоте
/// окошка, вертикаль снаружи котла чуть ниже окошка, та же вертикаль под землёй (высота земли - лучом).
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;
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, 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;
}
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 // в земле
};
}
/// Трассер: путь вылета и путь влёта линиями, точками той же кривой, по которой летит огонёк.
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();
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))
{
_p.y = hit.point.y;
}
else
{
_p.y -= 1f;
}
return _p;
}
bool Spawn(Transform _model)
{
GameObject prefab = OssuaryGlow.WispPrefab();
if (prefab == null)
{
return false;
}
_go = Object.Instantiate(prefab, _model, false);
_go.name = "NecroOssuaryWisp";
_trail = _go.GetComponent();
_rs = _go.GetComponentsInChildren(true);
_base = new Color[_rs.Length];
for (int i = 0; i < _rs.Length; i++)
{
Material m = _rs[i].sharedMaterial;
_base[i] = m == null ? Color.white : m.HasProperty(TintId) ? m.GetColor(TintId) : m.HasProperty(ColorId) ? m.GetColor(ColorId) : Color.white;
}
_go.SetActive(false);
return true;
}
/// Прозрачность - цветом материала через MaterialPropertyBlock (шейдер аддитивный: тусклее = прозрачнее),
/// общий материал не трогается.
void Alpha(float _a)
{
for (int i = 0; i < _rs.Length; i++)
{
Material m = _rs[i].sharedMaterial;
if (m == null)
{
continue;
}
Color c = _base[i] * _a;
_rs[i].GetPropertyBlock(_mpb);
if (m.HasProperty(TintId))
{
_mpb.SetColor(TintId, c);
}
if (m.HasProperty(ColorId))
{
_mpb.SetColor(ColorId, c);
}
_rs[i].SetPropertyBlock(_mpb);
}
}
void Show(Vector3 _at)
{
_go.transform.position = _at;
if (_trail != null)
{
_trail.Clear();
}
_go.SetActive(true);
if (_phase != 1)
{
Alpha(1f);
}
}
void Hide()
{
if (_go != null)
{
_go.SetActive(false);
}
}
public void Destroy()
{
if (_go != null)
{
Object.Destroy(_go);
_go = null;
}
foreach (LineRenderer l in new[] { _traceOut, _traceIn })
{
if (l != null)
{
Object.Destroy(l.gameObject);
}
}
_traceOut = _traceIn = null;
}
}
/// Модель оссуария появилась в мире - повесить на неё OssuaryVisual.
[HarmonyPatch(typeof(BlockCollector), nameof(BlockCollector.OnBlockEntityTransformBeforeActivated))]
public static class Patch_BlockCollector_OnBlockEntityTransformBeforeActivated_OssuaryVisual
{
public static void Postfix(Vector3i _blockPos, BlockValue _blockValue, BlockEntityData _ebcd)
{
if (!GeoOssuary.Is(_blockValue.Block) || _ebcd?.transform == null || _ebcd.transform.Find("body") == null)
{
return;
}
OssuaryVisual v = _ebcd.transform.GetComponent() ?? _ebcd.transform.gameObject.AddComponent();
v.Pos = _blockPos;
}
}
public class ConsoleCmdNecroOssuary : ConsoleCmdAbstract
{
public override bool IsExecuteOnClient => true;
public override bool AllowedInMainMenu => false;
public override string[] getCommands()
{
return new string[] { "necroossuary" };
}
public override string getDescription()
{
return "Necromancer's Tome: cauldron ossuary - find, remove, sphere/glow switch.";
}
public override string getHelp()
{
return "necroossuary find [r] - ossuaries around the player (r blocks, default 30)\n" +
"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 - [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" +
"necroossuary pulse a [speed] - glow pulse amplitude (0 - steady) and speed";
}
public override void Execute(List _params, CommandSenderInfo _senderInfo)
{
SdtdConsole con = SingletonMonoBehaviour.Instance;
World w = GameManager.Instance?.World;
EntityPlayerLocal pl = w?.GetPrimaryPlayer();
if (w == null || pl == null || _params.Count == 0)
{
con.Output(getHelp());
return;
}
Vector3i me = World.worldToBlockPos(pl.position);
switch (_params[0])
{
case "find":
{
int r = _params.Count > 1 ? int.Parse(_params[1]) : 30;
List found = OssuaryGlow.Find(w, me, r, 20);
con.Output("necroossuary: найдено " + found.Count + (found.Count > 0 ? ": " + string.Join("; ", found) : ""));
break;
}
case "remove":
{
var p = new Vector3i(int.Parse(_params[1]), int.Parse(_params[2]), int.Parse(_params[3]));
BlockValue bv = w.GetBlock(p);
if (!GeoOssuary.Is(bv.Block))
{
con.Output("necroossuary: в " + p + " не оссуарий (" + bv.Block?.GetBlockName() + ") - не трогаю");
break;
}
if (bv.ischild)
{
p = p + bv.parent;
}
w.SetBlockRPC(p, BlockValue.Air);
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":
{
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;
}
if (!(w.GetTileEntity(p) is TileEntityCollector te) || !GeoOssuary.Is(te))
{
con.Output("necroossuary: в " + p + " не оссуарий");
break;
}
if (_params[0] == "put")
{
ItemStack[] slots = _params[4] == "fuel" ? te.FuelSlots : te.ModSlots;
int i = int.Parse(_params[5]) - 1;
bool none = _params[6] == "none";
ItemValue iv = none ? ItemValue.None : ItemClass.GetItem(_params[6]);
if (i < 0 || i >= slots.Length || iv == null || (!none && iv.IsEmpty()))
{
con.Output("necroossuary: нет ячейки " + (i + 1) + " или предмета " + _params[6]);
break;
}
slots[i] = none ? ItemStack.Empty.Clone() : new ItemStack(iv, _params.Count > 7 ? int.Parse(_params[7]) : 1);
te.SetModified();
}
string S(ItemStack[] _a)
{
var parts = new List();
for (int i = 0; i < _a.Length; i++)
{
parts.Add(_a[i] == null || _a[i].IsEmpty() ? "-" : _a[i].itemValue.ItemClass?.Name + " x" + _a[i].count);
}
return string.Join(", ", parts);
}
con.Output("necroossuary " + p + ": образцы [" + S(te.ModSlots) + "]; топливо [" + S(te.FuelSlots) + "]; хранилище [" +
S(te.Items) + "]; работает " + te.anyEnabled());
break;
}
case "color":
if (_params.Count > 3)
{
OssuaryGlow.GlowColor = new Color(float.Parse(_params[1], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(_params[2], System.Globalization.CultureInfo.InvariantCulture),
float.Parse(_params[3], System.Globalization.CultureInfo.InvariantCulture), 1f);
}
con.Output("necroossuary: цвет свечения " + OssuaryGlow.GlowColor);
break;
case "pulse":
if (_params.Count > 1)
{
OssuaryGlow.GlowPulse = float.Parse(_params[1], System.Globalization.CultureInfo.InvariantCulture);
}
if (_params.Count > 2)
{
OssuaryGlow.GlowPulseSpeed = float.Parse(_params[2], System.Globalization.CultureInfo.InvariantCulture);
}
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;
con.Output("necroossuary: лог огонька " + (OssuaryGlow.WispLog ? "включён" : "выключен"));
break;
}
if (_params.Count > 1)
{
OssuaryGlow.WispEnabled = _params[1] == "on";
}
con.Output("necroossuary: огонёк " + (OssuaryGlow.WispEnabled ? "летает" : "выключен"));
break;
case "on":
case "off":
OssuaryGlow.Enabled = _params[0] == "on";
con.Output("necroossuary: сфера и свечение по работе " + (OssuaryGlow.Enabled ? "включены" : "выключены (модель как в префабе)"));
break;
default:
con.Output(getHelp());
break;
}
}
}
}