Нити цинерария: нож «хватом для нитей» (R), нити урна-ящик как у стриппера, урна-логист перевозит по образцу, зелёные глазницы
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fad57a3756
commit
d8e7c8c66a
+244
-6
@@ -36,10 +36,15 @@ namespace NecromancerTome
|
||||
public const int Below = 1;
|
||||
public const int Above = 4;
|
||||
public const float KillsPerUrn = 500f;
|
||||
const byte SaveVersion = 1;
|
||||
const byte SaveVersion = 2; // 2 - нити (16.10)
|
||||
|
||||
public bool HasSkull;
|
||||
public bool ZoneShown;
|
||||
/// <summary>Нити (CinerariumThreads.cs): ящики «откуда» и ящик «куда», главные клетки блоков. Есть нити - урна-логист.</summary>
|
||||
public readonly List<Vector3i> ThreadFrom = new List<Vector3i>();
|
||||
public bool HasThreadTo;
|
||||
public Vector3i ThreadTo;
|
||||
public bool IsLogist => HasThreadTo || ThreadFrom.Count > 0;
|
||||
|
||||
public override void CopyFromInternal(TileEntityComposite _other)
|
||||
{
|
||||
@@ -47,6 +52,11 @@ namespace NecromancerTome
|
||||
{
|
||||
HasSkull = other.HasSkull;
|
||||
ZoneShown = other.ZoneShown;
|
||||
ThreadFrom.Clear();
|
||||
ThreadFrom.AddRange(other.ThreadFrom);
|
||||
HasThreadTo = other.HasThreadTo;
|
||||
ThreadTo = other.ThreadTo;
|
||||
_threadsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +79,7 @@ namespace NecromancerTome
|
||||
{
|
||||
base.OnRemove(_world);
|
||||
RemoveBounds();
|
||||
RemoveThreadView();
|
||||
RemoveGenie(false);
|
||||
CinerariumContents.CloseIfShowing(this);
|
||||
if (!ConnectionManager.Instance.IsServer)
|
||||
@@ -95,6 +106,8 @@ namespace NecromancerTome
|
||||
{
|
||||
base.OnUnload(_world);
|
||||
RemoveBounds();
|
||||
RemoveThreadView();
|
||||
_threadsDirty = true; // чанк вернётся - нити построить заново
|
||||
RemoveGenie(false);
|
||||
CinerariumContents.CloseIfShowing(this);
|
||||
}
|
||||
@@ -103,6 +116,14 @@ namespace NecromancerTome
|
||||
{
|
||||
base.Write(_bw, _eStreamMode);
|
||||
CinerariumWriteHelper.Write(_bw, SaveVersion, HasSkull, ZoneShown);
|
||||
List<int> t = new List<int> { ThreadFrom.Count };
|
||||
foreach (Vector3i p in ThreadFrom)
|
||||
{
|
||||
t.Add(p.x); t.Add(p.y); t.Add(p.z);
|
||||
}
|
||||
t.Add(HasThreadTo ? 1 : 0);
|
||||
t.Add(ThreadTo.x); t.Add(ThreadTo.y); t.Add(ThreadTo.z);
|
||||
CinerariumWriteHelper.WriteInts(_bw, t.ToArray());
|
||||
}
|
||||
|
||||
public override void Read(PooledBinaryReader _br, StreamModeRead _eStreamMode)
|
||||
@@ -111,6 +132,29 @@ namespace NecromancerTome
|
||||
byte version = _br.ReadByte();
|
||||
HasSkull = _br.ReadBoolean();
|
||||
ZoneShown = _br.ReadBoolean();
|
||||
ThreadFrom.Clear();
|
||||
HasThreadTo = false;
|
||||
if (version >= 2)
|
||||
{
|
||||
int n = _br.ReadInt32();
|
||||
int[] v = new int[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
v[i] = _br.ReadInt32();
|
||||
}
|
||||
int k = 0;
|
||||
int from = n > 0 ? v[k++] : 0;
|
||||
for (int i = 0; i < from && k + 2 < n; i++, k += 3)
|
||||
{
|
||||
ThreadFrom.Add(new Vector3i(v[k], v[k + 1], v[k + 2]));
|
||||
}
|
||||
if (k + 3 < n)
|
||||
{
|
||||
HasThreadTo = v[k] != 0;
|
||||
ThreadTo = new Vector3i(v[k + 1], v[k + 2], v[k + 3]);
|
||||
}
|
||||
}
|
||||
_threadsDirty = true;
|
||||
if (version > SaveVersion)
|
||||
{
|
||||
Debug.LogWarning("[NecromancerTome] Cinerarium: урна из более новой версии мода (" + version + ") - прочитано только известное");
|
||||
@@ -148,9 +192,148 @@ namespace NecromancerTome
|
||||
SpawnBounds();
|
||||
}
|
||||
SyncGenie(false);
|
||||
SyncThreads(_world);
|
||||
Work(_world);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Нити (CinerariumThreads.cs).
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
bool _threadsDirty = true;
|
||||
GameObject _threadView;
|
||||
float _nextThreadCheck;
|
||||
|
||||
public bool AddThreadFrom(Vector3i _box)
|
||||
{
|
||||
if (ThreadFrom.Contains(_box))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (ThreadFrom.Count >= CinerariumThreads.MaxFrom)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ThreadFrom.Add(_box);
|
||||
ThreadsChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetThreadTo(Vector3i _box)
|
||||
{
|
||||
HasThreadTo = true;
|
||||
ThreadTo = _box;
|
||||
ThreadsChanged();
|
||||
}
|
||||
|
||||
public int ClearThreads()
|
||||
{
|
||||
int n = ThreadFrom.Count + (HasThreadTo ? 1 : 0);
|
||||
ThreadFrom.Clear();
|
||||
HasThreadTo = false;
|
||||
if (n > 0)
|
||||
{
|
||||
ThreadsChanged();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public int RemoveThreadsTo(Vector3i _box)
|
||||
{
|
||||
int n = ThreadFrom.RemoveAll(p => p == _box);
|
||||
if (HasThreadTo && ThreadTo == _box)
|
||||
{
|
||||
HasThreadTo = false;
|
||||
n++;
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
ThreadsChanged();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void ThreadsChanged()
|
||||
{
|
||||
_threadsDirty = true;
|
||||
_glowState = -1; // логист - другие глаза
|
||||
SetModified();
|
||||
Debug.Log("[NecromancerTome] Cinerarium: нити урны " + ToWorldPos() + " - откуда " + ThreadFrom.Count + ", куда " + (HasThreadTo ? ThreadTo.ToString() : "нет"));
|
||||
}
|
||||
|
||||
/// <summary>Нити к убранным ящикам снимаются (раз в 2 с); вид перестраивается после изменений.</summary>
|
||||
void SyncThreads(World _world)
|
||||
{
|
||||
if (_world != null && ConnectionManager.Instance.IsServer && IsLogist && Time.time >= _nextThreadCheck)
|
||||
{
|
||||
_nextThreadCheck = Time.time + 2f;
|
||||
bool gone = false;
|
||||
foreach (Vector3i p in ThreadFrom.ToArray())
|
||||
{
|
||||
if (!CinerariumThreads.Resolve(_world, p, out Vector3i _, out TEFeatureCinerarium u) || u != null)
|
||||
{
|
||||
if (_world.GetChunkFromWorldPos(p) != null) // чанк не загружен - не снимать
|
||||
{
|
||||
ThreadFrom.Remove(p);
|
||||
gone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (HasThreadTo && (!CinerariumThreads.Resolve(_world, ThreadTo, out Vector3i _, out TEFeatureCinerarium u2) || u2 != null) &&
|
||||
_world.GetChunkFromWorldPos(ThreadTo) != null)
|
||||
{
|
||||
HasThreadTo = false;
|
||||
gone = true;
|
||||
}
|
||||
if (gone)
|
||||
{
|
||||
ThreadsChanged();
|
||||
}
|
||||
}
|
||||
if (!_threadsDirty && (_threadView != null || !IsLogist))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Transform t = ModelTransform();
|
||||
if (t == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_threadsDirty = false;
|
||||
SyncGlow();
|
||||
if (!IsLogist)
|
||||
{
|
||||
RemoveThreadView();
|
||||
return;
|
||||
}
|
||||
if (_threadView == null)
|
||||
{
|
||||
_threadView = new GameObject("NecroCinerariumThreads");
|
||||
_threadView.transform.SetParent(t, false);
|
||||
_threadView.AddComponent<CinerariumThreadView>();
|
||||
}
|
||||
Vector3i me = ToWorldPos();
|
||||
List<(Vector3 a, Vector3 b)> pairs = new List<(Vector3, Vector3)>();
|
||||
foreach (Vector3i p in ThreadFrom)
|
||||
{
|
||||
pairs.Add((CinerariumThreads.BoxPoint(p, me), CinerariumThreads.UrnPoint(me)));
|
||||
}
|
||||
if (HasThreadTo)
|
||||
{
|
||||
pairs.Add((CinerariumThreads.UrnPoint(me), CinerariumThreads.BoxPoint(ThreadTo, me)));
|
||||
}
|
||||
_threadView.GetComponent<CinerariumThreadView>().Build(pairs);
|
||||
}
|
||||
|
||||
void RemoveThreadView()
|
||||
{
|
||||
if (_threadView != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(_threadView);
|
||||
_threadView = null;
|
||||
}
|
||||
}
|
||||
|
||||
float _nextWork;
|
||||
public bool Stuck;
|
||||
/// <summary>Дело найдено пробным шагом, дух полетел; выполнить в _actAt (16.10: «делал действие не сразу»).</summary>
|
||||
@@ -197,11 +380,17 @@ namespace NecromancerTome
|
||||
if (at.HasValue)
|
||||
{
|
||||
_pending = true;
|
||||
_actAt = Time.time + (motion != null ? CinerariumGenieMotion.ActDelay : 0f);
|
||||
_actAt = Time.time;
|
||||
if (motion != null)
|
||||
{
|
||||
Vector3 look = World.blockToTransformPos(at.Value) - Origin.position;
|
||||
motion.Visit(Stand(_world, at.Value, out bool beside) - Origin.position, look, beside);
|
||||
Vector3i? from = CinerariumWork.LastFrom;
|
||||
List<(Vector3, Vector3, bool)> stops = new List<(Vector3, Vector3, bool)>();
|
||||
if (from.HasValue)
|
||||
{
|
||||
stops.Add((Stand(_world, from.Value, out bool b0) - Origin.position, World.blockToTransformPos(from.Value) - Origin.position, b0));
|
||||
}
|
||||
stops.Add((Stand(_world, at.Value, out bool beside) - Origin.position, World.blockToTransformPos(at.Value) - Origin.position, beside));
|
||||
_actAt = Time.time + motion.Visit(stops);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,23 +519,67 @@ namespace NecromancerTome
|
||||
void SyncGlow()
|
||||
{
|
||||
Transform t = ModelTransform();
|
||||
int want = HasSkull ? 1 : 0;
|
||||
int want = HasSkull ? (IsLogist ? 2 : 1) : 0;
|
||||
Texture logist = want == 2 ? LogistEmission() : null;
|
||||
if (t == null || (_glowState == want && _glowModel == t))
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (Renderer r in t.GetComponentsInChildren<Renderer>(true))
|
||||
{
|
||||
if (_genie != null && r.transform.IsChildOf(_genie.transform) || _bounds != null && r.transform.IsChildOf(_bounds.transform))
|
||||
if (_genie != null && r.transform.IsChildOf(_genie.transform) || _bounds != null && r.transform.IsChildOf(_bounds.transform) ||
|
||||
_threadView != null && r.transform.IsChildOf(_threadView.transform))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
r.GetPropertyBlock(_glowBlock);
|
||||
_glowBlock.SetColor(EmissionColorId, HasSkull ? GlowColor : Color.black);
|
||||
if (logist != null)
|
||||
{
|
||||
_glowBlock.SetTexture(EmissionMapId, logist);
|
||||
}
|
||||
else if (_hadLogistTex)
|
||||
{
|
||||
_glowBlock.Clear(); // вернуть текстуру материала
|
||||
_glowBlock.SetColor(EmissionColorId, HasSkull ? GlowColor : Color.black);
|
||||
}
|
||||
r.SetPropertyBlock(_glowBlock);
|
||||
}
|
||||
_glowState = want;
|
||||
_glowModel = t;
|
||||
_hadLogistTex = logist != null;
|
||||
}
|
||||
|
||||
bool _hadLogistTex;
|
||||
static readonly int EmissionMapId = Shader.PropertyToID("_EmissionMap");
|
||||
static Texture2D _logistTex;
|
||||
static bool _logistTried;
|
||||
public const string LogistTextureFile = "necroCinerarium_e_logist.png";
|
||||
|
||||
/// <summary>Свечение урны-логиста (16.10: «пусть глаза черепов светятся другим цветом. Зелёным»): копия запечённой
|
||||
/// necroCinerarium_e с зелёными глазницами (руны - те же), из папки Resources мода - бандл не пересобирается.</summary>
|
||||
static Texture2D LogistEmission()
|
||||
{
|
||||
if (!_logistTried)
|
||||
{
|
||||
_logistTried = true;
|
||||
try
|
||||
{
|
||||
string path = Path.Combine(ModEntry.Instance.Path, "Resources", LogistTextureFile);
|
||||
Texture2D tex = new Texture2D(2, 2, TextureFormat.RGBA32, true);
|
||||
if (tex.LoadImage(File.ReadAllBytes(path)))
|
||||
{
|
||||
tex.name = "necroCinerarium_e_logist";
|
||||
tex.anisoLevel = 4;
|
||||
_logistTex = tex;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("[NecromancerTome] Cinerarium: свечение логиста не загружено (" + e.Message + ")");
|
||||
}
|
||||
}
|
||||
return _logistTex;
|
||||
}
|
||||
|
||||
Transform _glowModel;
|
||||
@@ -563,6 +796,11 @@ namespace NecromancerTome
|
||||
return n;
|
||||
}
|
||||
|
||||
public static List<Vector3i> All()
|
||||
{
|
||||
return new List<Vector3i>(Urns().Keys);
|
||||
}
|
||||
|
||||
/// <summary>Пересекутся ли зоны урны в _pos и уже стоящей: по горизонтали ближе 5 блоков по обеим осям,
|
||||
/// по высоте ближе 6 (зона от -1 до +4).</summary>
|
||||
public static bool Overlaps(Vector3i _pos)
|
||||
|
||||
Reference in New Issue
Block a user