Урны: череп-ретранслятор, прилив прячет духов, меры нагрузки; проверки без мыши
- Череп-ретранслятор: нить урны проходит через черепа-декор (цепочка), каждый пролёт до 15 м; черепа хранятся у нити (запись урны v5, старые - без черепов). ПКМ ножом по черепу - провести нить, ЛКМ - снять нити через него; убрали череп - нить снимается. - Прилив: ночью кровавой луны духи всех урн исчезают, работа стоит, утром - сама. - Нагрузка: общий бюджет дел духов на кадр (3), анимируются только 16 ближайших духов; лимит урн сборщика смягчён - одна на 200 убийств (было 500), тексты на 13 языках. - Описание логиста (кадило, два вида, маленький дух, мигание), черепа-декора (передаёт нить), подсказки ножа - на 13 языках. - Разработка: necrogod, necrofps [uncap], necrotide, necroshot серией, necroclick по клетке, necrobox fill, necrourn с черепами на пути. Проверено в игре (test8): мигание, нить 28 м через череп и её разрыв, прилив, 63 урны сборщика: 13.0 мс на кадр против 11.8 мс без работы урн. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6693d96d5e
commit
85d8036e81
+116
-10
@@ -25,6 +25,10 @@ namespace NecromancerTome
|
||||
/// Урна рабочего (18.10): нити ящик -> урна - откуда брать ингредиенты и топливо, урна -> станция - где изготавливать,
|
||||
/// урна -> ящик - куда класть готовое; станций и ящиков сколько угодно (до MaxFrom в каждую сторону). Нить между станцией
|
||||
/// и урной рабочего - всегда «от урны», в каком бы порядке её ни тянули.
|
||||
///
|
||||
/// Череп-ретранслятор (18.10): череп-декор (блок necroSkullDecorCenter/Back) в середине нити. Начал нить у ящика или урны,
|
||||
/// ПКМ по черепу - нить проходит через него, дальше - следующий череп или конец; каждый пролёт - до MaxLength. Черепа
|
||||
/// хранятся у нити в урне (TEFeatureCinerarium.FromVia/ToVia/OutVia), ЛКМ по черепу - снять все нити через него.
|
||||
/// </summary>
|
||||
public static class CinerariumThreads
|
||||
{
|
||||
@@ -39,6 +43,13 @@ namespace NecromancerTome
|
||||
public static readonly Color SparkColor = new Color(0.8f, 0.95f, 1f, 1f);
|
||||
|
||||
public static bool IsKnife(ItemClass _i) => _i != null && (_i.Name == KnifeName || _i.Name == ThreadKnifeName);
|
||||
|
||||
/// <summary>Череп-ретранслятор - череп-декор в мире (оба вида и старый блок-помощник).</summary>
|
||||
public static bool IsRelay(Block _b)
|
||||
{
|
||||
string n = _b?.GetBlockName();
|
||||
return n == SkullDecorPlace.CenterBlock || n == SkullDecorPlace.BackBlock || n == "necroSkullDecor";
|
||||
}
|
||||
public static bool IsThreadKnife(ItemClass _i) => _i != null && _i.Name == ThreadKnifeName;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
@@ -72,13 +83,19 @@ namespace NecromancerTome
|
||||
static bool _startUrn;
|
||||
static bool _startStation;
|
||||
static GameObject _preview;
|
||||
/// <summary>Черепа, через которые уже прошла тянущаяся нить, по порядку от начала.</summary>
|
||||
static readonly List<Vector3i> _via = new List<Vector3i>();
|
||||
|
||||
public static Vector3i? Start => _start;
|
||||
public static bool StartIsUrn => _startUrn;
|
||||
/// <summary>Откуда сейчас тянется нить: последний череп или начало.</summary>
|
||||
public static Vector3i? LastPoint => _via.Count > 0 ? _via[_via.Count - 1] : _start;
|
||||
public static bool LastIsRelay => _via.Count > 0;
|
||||
|
||||
public static void Cancel()
|
||||
{
|
||||
_start = null;
|
||||
_via.Clear();
|
||||
if (_preview != null)
|
||||
{
|
||||
Object.Destroy(_preview);
|
||||
@@ -127,9 +144,20 @@ namespace NecromancerTome
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool ok = Resolve(w, hi.hit.blockPos, out Vector3i owner, out TEFeatureCinerarium urn);
|
||||
ClickAt(_pl, w, hi.hit.blockPos, _connect);
|
||||
}
|
||||
|
||||
/// <summary>Щелчок ножом для нитей по клетке _pos (луч уже разобран; команда necroclick - напрямую).</summary>
|
||||
public static void ClickAt(EntityPlayerLocal _pl, World w, Vector3i _pos, bool _connect)
|
||||
{
|
||||
if (IsRelay(w.GetBlock(_pos).Block))
|
||||
{
|
||||
ClickRelay(_pl, w, _pos, _connect);
|
||||
return;
|
||||
}
|
||||
bool ok = Resolve(w, _pos, out Vector3i owner, out TEFeatureCinerarium urn);
|
||||
bool station = false;
|
||||
if (!ok && StationAt(w, hi.hit.blockPos, out Vector3i stOwner) != null)
|
||||
if (!ok && StationAt(w, _pos, out Vector3i stOwner) != null)
|
||||
{
|
||||
ok = true;
|
||||
station = true;
|
||||
@@ -174,12 +202,19 @@ namespace NecromancerTome
|
||||
Vector3i s = _start.Value;
|
||||
bool sUrn = _startUrn;
|
||||
bool sStation = _startStation;
|
||||
Vector3i last = LastPoint.Value;
|
||||
// Черепа - по порядку от урны: начали у урны - как шли, начали у ящика или станции - в обратном порядке.
|
||||
List<Vector3i> via = new List<Vector3i>(_via);
|
||||
Cancel();
|
||||
if (s == owner)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadCancel"));
|
||||
return;
|
||||
}
|
||||
if (!sUrn)
|
||||
{
|
||||
via.Reverse();
|
||||
}
|
||||
// Станция - только с урной рабочего, нить всегда «от урны».
|
||||
if (sStation || station)
|
||||
{
|
||||
@@ -190,12 +225,12 @@ namespace NecromancerTome
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadNeedWorker"));
|
||||
return;
|
||||
}
|
||||
if (Vector3.Distance(s.ToVector3(), owner.ToVector3()) > MaxLength)
|
||||
if (Vector3.Distance(last.ToVector3(), owner.ToVector3()) > MaxLength)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadTooFar"));
|
||||
return;
|
||||
}
|
||||
if (!wu.AddThreadOut(st))
|
||||
if (!wu.AddThreadOut(st, via))
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadLimit"));
|
||||
return;
|
||||
@@ -215,7 +250,7 @@ namespace NecromancerTome
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadNeedLogist"));
|
||||
return;
|
||||
}
|
||||
if (Vector3.Distance(s.ToVector3(), owner.ToVector3()) > MaxLength)
|
||||
if (Vector3.Distance(last.ToVector3(), owner.ToVector3()) > MaxLength)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadTooFar"));
|
||||
return;
|
||||
@@ -230,7 +265,7 @@ namespace NecromancerTome
|
||||
if (u.IsWorker)
|
||||
{
|
||||
// У рабочего ящиков «куда» сколько угодно - туда готовое.
|
||||
if (!u.AddThreadOut(owner))
|
||||
if (!u.AddThreadOut(owner, via))
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadLimit"));
|
||||
return;
|
||||
@@ -239,13 +274,13 @@ namespace NecromancerTome
|
||||
}
|
||||
else
|
||||
{
|
||||
u.SetThreadTo(owner);
|
||||
u.SetThreadTo(owner, via);
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadTo"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!urn.AddThreadFrom(s))
|
||||
if (!urn.AddThreadFrom(s, via))
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadLimit"));
|
||||
return;
|
||||
@@ -255,6 +290,76 @@ namespace NecromancerTome
|
||||
_pl.PlayOneShot("wiretool_place");
|
||||
}
|
||||
|
||||
/// <summary>Щелчок ножом по черепу-ретранслятору: ПКМ при тянущейся нити - провести её через череп; ЛКМ - снять все нити
|
||||
/// через него (или бросить тянущуюся).</summary>
|
||||
static void ClickRelay(EntityPlayerLocal _pl, World _w, Vector3i _skull, bool _connect)
|
||||
{
|
||||
if (!_connect)
|
||||
{
|
||||
if (_start.HasValue)
|
||||
{
|
||||
Cancel();
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadCancel"));
|
||||
return;
|
||||
}
|
||||
int n = 0;
|
||||
foreach (Vector3i p in CinerariumRegistry.All())
|
||||
{
|
||||
TEFeatureCinerarium u = UrnAt(_w, p);
|
||||
if (u != null)
|
||||
{
|
||||
n += u.RemoveThreadsVia(_skull);
|
||||
}
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
_pl.PlayOneShot("wiretool_grab");
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadRemoved"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!_start.HasValue)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadRelayFirst"));
|
||||
return;
|
||||
}
|
||||
if (_via.Contains(_skull))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Vector3.Distance(LastPoint.Value.ToVector3(), _skull.ToVector3()) > MaxLength)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadTooFar"));
|
||||
return;
|
||||
}
|
||||
_via.Add(_skull);
|
||||
_pl.PlayOneShot("wiretool_place");
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadRelay"));
|
||||
}
|
||||
|
||||
/// <summary>Нить урны _urn к _end через черепа _via (по порядку от урны) - пролётами в _pairs, огоньки бегут по направлению
|
||||
/// нити: _toUrn - от конца к урне (нить «откуда»), иначе от урны к концу.</summary>
|
||||
public static void AddPath(List<(Vector3 a, Vector3 b)> _pairs, Vector3i _urn, List<Vector3i> _via, Vector3i _end, bool _toUrn)
|
||||
{
|
||||
List<Vector3> pts = new List<Vector3> { UrnPoint(_urn) };
|
||||
if (_via != null)
|
||||
{
|
||||
foreach (Vector3i v in _via)
|
||||
{
|
||||
pts.Add(RelayPoint(v));
|
||||
}
|
||||
}
|
||||
Vector3i before = _via != null && _via.Count > 0 ? _via[_via.Count - 1] : _urn;
|
||||
pts.Add(BoxPoint(_end, before));
|
||||
for (int i = 0; i + 1 < pts.Count; i++)
|
||||
{
|
||||
_pairs.Add(_toUrn ? (pts[i + 1], pts[i]) : (pts[i], pts[i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Точка нити у черепа - над черепом-декором (он лежит на дне клетки). Сцена (без Origin).</summary>
|
||||
public static Vector3 RelayPoint(Vector3i _skull) => World.blockToTransformPos(_skull) - Origin.position + Vector3.up * 0.3f;
|
||||
|
||||
/// <summary>Снять все нити урны или все нити, идущие к ящику (у всех урн сейва). Сколько снято.</summary>
|
||||
static int RemoveAll(World _w, Vector3i _owner, TEFeatureCinerarium _urn)
|
||||
{
|
||||
@@ -444,8 +549,9 @@ namespace NecromancerTome
|
||||
{
|
||||
return;
|
||||
}
|
||||
Vector3i s = CinerariumThreads.Start.Value;
|
||||
Vector3 a = World.blockToTransformPos(s) - Origin.position + Vector3.up * (CinerariumThreads.StartIsUrn ? 0.5f : 0.6f);
|
||||
Vector3i s = CinerariumThreads.LastPoint.Value;
|
||||
Vector3 a = CinerariumThreads.LastIsRelay ? CinerariumThreads.RelayPoint(s)
|
||||
: World.blockToTransformPos(s) - Origin.position + Vector3.up * (CinerariumThreads.StartIsUrn ? 0.5f : 0.6f);
|
||||
Transform cam = pl.playerCamera != null ? pl.playerCamera.transform : null;
|
||||
Vector3 b = cam != null ? cam.position + cam.forward * 0.6f + cam.right * 0.2f - cam.up * 0.25f : pl.GetPosition() - Origin.position + Vector3.up * 1.2f;
|
||||
bool far = Vector3.Distance(s.ToVector3(), pl.GetPosition()) > CinerariumThreads.MaxLength;
|
||||
|
||||
Reference in New Issue
Block a user