Логист - латунное кадило: два вида, дух 1/3 из окон, мигание; команды разработки

- Блоки логиста на модели кадила: по центру на подставке (necroCinerariumLogist) и новый вид у
  стены на кронштейне (necroCinerariumLogistWall/+Skull); выбор вида - R с урной в руке.
- Дух логиста втрое меньше, в покое сидит в кадиле, на перевозку вылетает через окно крышки
  (у стены - не в стену) и возвращается тем же окном.
- Застрял (ящик «куда» полон) - урна мигает (CinerariumBlink) вместо почерневшего духа;
  прежняя зелёная подмена свечения старой модели убрана.
- Команды разработки без мыши: necrospawn, necroexit [quit], necrolook, necroaim, necroplace,
  necrohold, necrobox, necrourn, necroclick.

Проверено в игре (test8): перевозка обоими видами, вылет духа, вид у стены от стены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-18 14:19:50 +03:00
co-authored by Claude Opus 5.5
parent b5bd4e4b2c
commit 6693d96d5e
11 changed files with 630 additions and 71 deletions
+42 -50
View File
@@ -35,11 +35,20 @@ namespace NecromancerTome
/// без зоны - ставится вплотную к любым урнам, только нити и перевозка, глазницы всегда зелёные. Лимит - общий.</summary>
public const string LogistBlockName = "necroCinerariumLogist";
public const string LogistSkullBlockName = "necroCinerariumLogistSkull";
/// <summary>Логист у стены (18.10, кадило на кронштейне): тот же логист, другой вид - выбор в меню R (CinerariumLogistForm).</summary>
public const string LogistWallBlockName = "necroCinerariumLogistWall";
public const string LogistWallSkullBlockName = "necroCinerariumLogistWallSkull";
public static bool IsLogistBlock(Block _b)
{
string n = _b?.GetBlockName();
return n == LogistBlockName || n == LogistSkullBlockName;
return n == LogistBlockName || n == LogistSkullBlockName || n == LogistWallBlockName || n == LogistWallSkullBlockName;
}
public static bool IsLogistWallBlock(Block _b)
{
string n = _b?.GetBlockName();
return n == LogistWallBlockName || n == LogistWallSkullBlockName;
}
/// <summary>Урна рабочего (17.10, BACKLOG «Урна рабочего»): изготавливает предмет из ячейки образца на станциях, к которым
/// от неё тянутся нити; ингредиенты - из ящиков с нитями к урне, готовое - в ящики с нитями от урны. Без зоны и вне лимита,
@@ -143,7 +152,7 @@ namespace NecromancerTome
_worker = IsWorkerBlock(_blockValue.Block);
_logistKnown = true;
string placed = _blockValue.Block?.GetBlockName();
if (placed == SkullBlockName || placed == LogistSkullBlockName || placed == WorkerSkullBlockName)
if (placed == SkullBlockName || placed == LogistSkullBlockName || placed == LogistWallSkullBlockName || placed == WorkerSkullBlockName)
{
HasSkull = true; // поставили урну, поднятую с черепом
}
@@ -543,9 +552,10 @@ namespace NecromancerTome
if (stuck != Stuck)
{
Stuck = stuck;
Debug.Log("[NecromancerTome] Cinerarium: дух урны " + ToWorldPos() + (stuck ? " почернел - готовое класть некуда" : " ожил"));
_glowState = -1; // логист - мигание
Debug.Log("[NecromancerTome] Cinerarium: дух урны " + ToWorldPos() + (stuck ? (IsLogist ? " застрял - ящик «куда» полон, урна мигает" : " почернел - готовое класть некуда") : " ожил"));
}
if (motion != null)
if (motion != null && !IsLogist)
{
motion.SetStuck(Stuck && at == null);
}
@@ -683,7 +693,13 @@ namespace NecromancerTome
SyncGlow();
if (HasSkull && _genie == null)
{
_genie = CinerariumGenie.Spawn(ModelTransform(), IsWorker ? CinerariumGenie.WorkerDrop : 0f);
int logist = 0;
if (IsLogist)
{
World w = GameManager.Instance?.World;
logist = w != null && IsLogistWallBlock(w.GetBlock(ToWorldPos()).Block) ? 2 : 1;
}
_genie = CinerariumGenie.Spawn(ModelTransform(), IsWorker ? CinerariumGenie.WorkerDrop : 0f, logist);
if (_genie != null && _sound)
{
Audio.Manager.BroadcastPlay(World.blockToTransformPos(ToWorldPos()), GenieSound);
@@ -705,14 +721,15 @@ namespace NecromancerTome
void SyncGlow()
{
Transform t = ModelTransform();
// Логист (16.10: «Так сразу будет видно что логистика, а что технолог») - всегда своя текстура свечения (руны и
// глазницы зелёные) и зеленоватая глазурь; светится, как и обычная урна, только с черепом.
int want = (HasSkull ? 1 : 0) + (IsLogist ? 2 : 0);
Texture logist = IsLogist ? LogistEmission() : null;
// Логист (18.10, кадило): своё свечение в материале - внутренность кадила, светятся окна крышки. Застрял (класть
// некуда) - вместо почерневшего духа мигает (18.10: «При застревании должно мигать»): CinerariumBlink на модели.
bool blink = HasSkull && IsLogist && Stuck;
int want = (HasSkull ? 1 : 0) + (blink ? 2 : 0);
if (t == null || (_glowState == want && _glowModel == t))
{
return;
}
List<Renderer> own = new List<Renderer>();
foreach (Renderer r in t.GetComponentsInChildren<Renderer>(true))
{
if (_genie != null && r.transform.IsChildOf(_genie.transform) || _bounds != null && r.transform.IsChildOf(_bounds.transform) ||
@@ -720,53 +737,25 @@ namespace NecromancerTome
{
continue;
}
own.Add(r);
r.GetPropertyBlock(_glowBlock);
_glowBlock.SetColor(EmissionColorId, HasSkull ? GlowColor : Color.black);
if (logist != null)
{
_glowBlock.SetTexture(EmissionMapId, logist);
_glowBlock.SetColor(AlbedoColorId, LogistTint);
}
r.SetPropertyBlock(_glowBlock);
}
CinerariumBlink b = t.GetComponent<CinerariumBlink>();
if (blink && b == null)
{
b = t.gameObject.AddComponent<CinerariumBlink>();
}
if (b != null)
{
b.Renderers = own;
b.enabled = blink;
}
_glowState = want;
_glowModel = t;
}
/// <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;
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;
void RemoveGenie(bool _sound)
@@ -864,7 +853,7 @@ namespace NecromancerTome
{
string n = _b?.GetBlockName();
return n == BlockName || n == SkullBlockName || n == LogistBlockName || n == LogistSkullBlockName ||
n == WorkerBlockName || n == WorkerSkullBlockName;
n == LogistWallBlockName || n == LogistWallSkullBlockName || n == WorkerBlockName || n == WorkerSkullBlockName;
}
public static int UrnLimit(EntityPlayer _player)
@@ -1224,8 +1213,11 @@ namespace NecromancerTome
return false;
}
bool logist = TEFeatureCinerarium.IsLogistBlock(bv.Block);
bool wall = TEFeatureCinerarium.IsLogistWallBlock(bv.Block);
bool worker = TEFeatureCinerarium.IsWorkerBlock(bv.Block);
ItemValue iv = ItemClass.GetItem(logist
ItemValue iv = ItemClass.GetItem(wall
? (urn.HasSkull ? TEFeatureCinerarium.LogistWallSkullBlockName : TEFeatureCinerarium.LogistWallBlockName)
: logist
? (urn.HasSkull ? TEFeatureCinerarium.LogistSkullBlockName : TEFeatureCinerarium.LogistBlockName)
: worker
? (urn.HasSkull ? TEFeatureCinerarium.WorkerSkullBlockName : TEFeatureCinerarium.WorkerBlockName)