Урна-логист - отдельный блок: без зоны (ставится вплотную), нити только к ней, зелёные руны и глазницы, своя иконка; рецепт с набором для шитья, лимит общий

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-16 21:00:19 +03:00
co-authored by Claude Opus 5.5
parent adc9371a09
commit f4ae9cbcb8
9 changed files with 116 additions and 23 deletions
+67 -21
View File
@@ -31,6 +31,16 @@ namespace NecromancerTome
{
public const string BlockName = "necroCinerarium";
public const string SkullBlockName = "necroCinerariumSkull";
/// <summary>Урна-логист (16.10: «Я не могу ставить урны рядом. Может нам для логиста вообще сделать отдельные урны?»):
/// без зоны - ставится вплотную к любым урнам, только нити и перевозка, глазницы всегда зелёные. Лимит - общий.</summary>
public const string LogistBlockName = "necroCinerariumLogist";
public const string LogistSkullBlockName = "necroCinerariumLogistSkull";
public static bool IsLogistBlock(Block _b)
{
string n = _b?.GetBlockName();
return n == LogistBlockName || n == LogistSkullBlockName;
}
public const string SkullItemName = "necroSkull";
public const int HalfWidth = 2;
public const int Below = 1;
@@ -44,7 +54,26 @@ namespace NecromancerTome
public readonly List<Vector3i> ThreadFrom = new List<Vector3i>();
public bool HasThreadTo;
public Vector3i ThreadTo;
public bool IsLogist => HasThreadTo || ThreadFrom.Count > 0;
public bool HasThreads => HasThreadTo || ThreadFrom.Count > 0;
/// <summary>Урна-логист - по блоку (necroCinerariumLogist / ...Skull); узнаётся при постановке или по миру.</summary>
public bool IsLogist
{
get
{
if (!_logistKnown)
{
World w = GameManager.Instance?.World;
BlockValue bv = w != null ? w.GetBlock(ToWorldPos()) : BlockValue.Air;
if (IsUrn(bv.Block))
{
_logist = IsLogistBlock(bv.Block);
_logistKnown = true;
}
}
return _logist;
}
}
bool _logist, _logistKnown;
/// <summary>Фильтр логиста: бит на группу CinerariumGroups; по умолчанию все включены.</summary>
public uint GroupMask = CinerariumGroups.All;
@@ -76,7 +105,10 @@ namespace NecromancerTome
public override void OnAdded(Vector3i _blockPos, BlockValue _blockValue)
{
base.OnAdded(_blockPos, _blockValue);
if (_blockValue.Block?.GetBlockName() == SkullBlockName)
_logist = IsLogistBlock(_blockValue.Block);
_logistKnown = true;
string placed = _blockValue.Block?.GetBlockName();
if (placed == SkullBlockName || placed == LogistSkullBlockName)
{
HasSkull = true; // поставили урну, поднятую с черепом
}
@@ -290,7 +322,14 @@ namespace NecromancerTome
/// <summary>Нити к убранным ящикам снимаются (раз в 2 с); вид перестраивается после изменений.</summary>
void SyncThreads(World _world)
{
if (_world != null && ConnectionManager.Instance.IsServer && IsLogist && Time.time >= _nextThreadCheck)
if (_world != null && ConnectionManager.Instance.IsServer && HasThreads && !IsLogist && _logistKnown)
{
Debug.Log("[NecromancerTome] Cinerarium: нити у обычной урны " + ToWorldPos() + " сняты - нити только у урны-логиста");
ThreadFrom.Clear();
HasThreadTo = false;
ThreadsChanged();
}
if (_world != null && ConnectionManager.Instance.IsServer && HasThreads && Time.time >= _nextThreadCheck)
{
_nextThreadCheck = Time.time + 2f;
bool gone = false;
@@ -316,7 +355,7 @@ namespace NecromancerTome
ThreadsChanged();
}
}
if (!_threadsDirty && (_threadView != null || !IsLogist))
if (!_threadsDirty && (_threadView != null || !HasThreads))
{
return;
}
@@ -327,7 +366,7 @@ namespace NecromancerTome
}
_threadsDirty = false;
SyncGlow();
if (!IsLogist)
if (!HasThreads)
{
RemoveThreadView();
return;
@@ -545,8 +584,10 @@ namespace NecromancerTome
void SyncGlow()
{
Transform t = ModelTransform();
int want = HasSkull ? (IsLogist ? 2 : 1) : 0;
Texture logist = want == 2 ? LogistEmission() : null;
// Логист (16.10: «Так сразу будет видно что логистика, а что технолог») - всегда своя текстура свечения (руны и
// глазницы зелёные) и зеленоватая глазурь; светится, как и обычная урна, только с черепом.
int want = (HasSkull ? 1 : 0) + (IsLogist ? 2 : 0);
Texture logist = IsLogist ? LogistEmission() : null;
if (t == null || (_glowState == want && _glowModel == t))
{
return;
@@ -563,20 +604,17 @@ namespace NecromancerTome
if (logist != null)
{
_glowBlock.SetTexture(EmissionMapId, logist);
}
else if (_hadLogistTex)
{
_glowBlock.Clear(); // вернуть текстуру материала
_glowBlock.SetColor(EmissionColorId, HasSkull ? GlowColor : Color.black);
_glowBlock.SetColor(AlbedoColorId, LogistTint);
}
r.SetPropertyBlock(_glowBlock);
}
_glowState = want;
_glowModel = t;
_hadLogistTex = logist != null;
}
bool _hadLogistTex;
/// <summary>Глазурь урны-логиста - множитель цвета материала (чёрная глазурь с зелёным отливом).</summary>
public static Color LogistTint = new Color(0.55f, 1f, 0.75f, 1f);
static readonly int AlbedoColorId = Shader.PropertyToID("_Color");
static readonly int EmissionMapId = Shader.PropertyToID("_EmissionMap");
static Texture2D _logistTex;
static bool _logistTried;
@@ -704,7 +742,7 @@ namespace NecromancerTome
public static bool IsUrn(Block _b)
{
string n = _b?.GetBlockName();
return n == BlockName || n == SkullBlockName;
return n == BlockName || n == SkullBlockName || n == LogistBlockName || n == LogistSkullBlockName;
}
public static int UrnLimit(EntityPlayer _player)
@@ -714,9 +752,9 @@ namespace NecromancerTome
}
/// <summary>Ключ подсказки, почему урну сюда нельзя, или null.</summary>
public static string PlacementRefusal(Vector3i _pos)
public static string PlacementRefusal(Vector3i _pos, Block _placing)
{
if (CinerariumRegistry.Overlaps(_pos))
if (!IsLogistBlock(_placing) && CinerariumRegistry.Overlaps(_pos))
{
return "necroCinerariumOverlap";
}
@@ -833,8 +871,13 @@ namespace NecromancerTome
{
int w = TEFeatureCinerarium.HalfWidth * 2;
int h = TEFeatureCinerarium.Below + TEFeatureCinerarium.Above;
World world = GameManager.Instance?.World;
foreach (Vector3i p in Urns().Keys)
{
if (world != null && TEFeatureCinerarium.IsLogistBlock(world.GetBlock(p).Block))
{
continue; // у логиста зоны нет
}
if (p != _pos && Math.Abs(p.x - _pos.x) <= w && Math.Abs(p.z - _pos.z) <= w && Math.Abs(p.y - _pos.y) <= h)
{
return true;
@@ -886,11 +929,11 @@ namespace NecromancerTome
}
else if (text.EndsWith("bounds_show"))
{
_commands[i].enabled = !urn.ZoneShown;
_commands[i].enabled = !urn.ZoneShown && !urn.IsLogist;
}
else if (text.EndsWith("bounds_hide"))
{
_commands[i].enabled = urn.ZoneShown;
_commands[i].enabled = urn.ZoneShown && !urn.IsLogist;
}
}
}
@@ -968,7 +1011,7 @@ namespace NecromancerTome
{
return;
}
string why = TEFeatureCinerarium.PlacementRefusal(_blockPos);
string why = TEFeatureCinerarium.PlacementRefusal(_blockPos, __instance);
if (why != null)
{
__result = false;
@@ -1029,7 +1072,10 @@ namespace NecromancerTome
GameManager.ShowTooltip(player, Localization.Get("necroCinerariumBagNotEmpty"), string.Empty, "ui_denied");
return false;
}
ItemValue iv = ItemClass.GetItem(urn.HasSkull ? TEFeatureCinerarium.SkullBlockName : TEFeatureCinerarium.BlockName);
bool logist = TEFeatureCinerarium.IsLogistBlock(bv.Block);
ItemValue iv = ItemClass.GetItem(logist
? (urn.HasSkull ? TEFeatureCinerarium.LogistSkullBlockName : TEFeatureCinerarium.LogistBlockName)
: (urn.HasSkull ? TEFeatureCinerarium.SkullBlockName : TEFeatureCinerarium.BlockName));
if (iv == null || iv.type <= 0)
{
return true;