Урна-логист - отдельный блок: без зоны (ставится вплотную), нити только к ней, зелёные руны и глазницы, своя иконка; рецепт с набором для шитья, лимит общий
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
adc9371a09
commit
f4ae9cbcb8
File diff suppressed because one or more lines are too long
@@ -560,5 +560,21 @@
|
||||
<property name="CreativeMode" value="None"/>
|
||||
<property name="DescriptionKey" value="necroCinerariumDesc"/>
|
||||
</block>
|
||||
<!-- Урна-логист (16.10.2026): «Я не могу ставить урны рядом. Может нам для логиста вообще сделать отдельные урны?»
|
||||
Без зоны (ставится вплотную к любым урнам), только нити и перевозка (CinerariumThreads.cs), лимит урн общий.
|
||||
Модель - та же; отличие - код (TEFeatureCinerarium.SyncGlow): зелёные руны и глазницы
|
||||
(Resources/necroCinerarium_e_logist.png), зеленоватая глазурь. Иконка - своя (зелёные руны, нити из горлышка).
|
||||
Своя модель логиста - позже, в Blender. -->
|
||||
<block name="necroCinerariumLogist">
|
||||
<property name="Extends" value="necroCinerarium"/>
|
||||
<property name="CreativeMode" value="Player"/>
|
||||
<property name="DescriptionKey" value="necroCinerariumLogistDesc"/>
|
||||
<property name="CustomIcon" value="necroCinerariumLogist"/>
|
||||
</block>
|
||||
<block name="necroCinerariumLogistSkull">
|
||||
<property name="Extends" value="necroCinerariumLogist"/>
|
||||
<property name="CreativeMode" value="None"/>
|
||||
<property name="DescriptionKey" value="necroCinerariumLogistDesc"/>
|
||||
</block>
|
||||
</append>
|
||||
</config>
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
пользуется, вода сидит на necroNecromancyLvl20. -->
|
||||
<unlock_entry item="necroModKnifeScavengersFeast,bookSummonRatSpirit" unlock_tier="3"/>
|
||||
<unlock_entry item="necroModKnifeGravesRepose" unlock_tier="4"/>
|
||||
<unlock_entry item="necroModKnifeDarkSense,necroCinerarium" unlock_tier="5"/>
|
||||
<unlock_entry item="necroModKnifeDarkSense,necroCinerarium,necroCinerariumLogist" unlock_tier="5"/>
|
||||
</display_entry>
|
||||
<!-- Ступенчатая разблокировка внутри группы. ПЕРЕРАСПРЕДЕЛЕНО 2026-09-09: четыре из
|
||||
шести модов ножа (вода/еда/покой/чутьё) уехали отсюда в группу 1 - см. большой
|
||||
|
||||
@@ -621,5 +621,20 @@
|
||||
<ingredient name="resourceBone" count="10"/>
|
||||
<ingredient name="resourceNecroBlackRoseJuice" count="1"/>
|
||||
</recipe>
|
||||
<!-- Урна-логист (16.10: «Рецепт с набором для шитья») - рецепт урны и набор для шитья; копия на соке чёрной розы. -->
|
||||
<recipe name="necroCinerariumLogist" count="1" tags="learnable,packMuleCrafting,necroNecromancyLvl300">
|
||||
<ingredient name="resourceClayLump" count="30"/>
|
||||
<ingredient name="resourceZombieAsh" count="100"/>
|
||||
<ingredient name="resourceBone" count="10"/>
|
||||
<ingredient name="resourceNecromancerBlood" count="1"/>
|
||||
<ingredient name="resourceSewingKit" count="1"/>
|
||||
</recipe>
|
||||
<recipe name="necroCinerariumLogist" count="1" tags="learnable,packMuleCrafting,necroNecromancyLvl300">
|
||||
<ingredient name="resourceClayLump" count="30"/>
|
||||
<ingredient name="resourceZombieAsh" count="100"/>
|
||||
<ingredient name="resourceBone" count="10"/>
|
||||
<ingredient name="resourceNecroBlackRoseJuice" count="1"/>
|
||||
<ingredient name="resourceSewingKit" count="1"/>
|
||||
</recipe>
|
||||
</append>
|
||||
</config>
|
||||
|
||||
+67
-21
@@ -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;
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace NecromancerTome
|
||||
/// ящика. Дух в цинерарии будет забирать всё что лежит в ящике из которого в него тянется провод, и тащить в ящик, в
|
||||
/// который тянется провод.» Решения: у урны несколько нитей «откуда» и одна «куда»; везётся по образцу (только то, что уже
|
||||
/// лежит в ящике «куда»; пустой - всё подряд); «куда» полон - дух ждёт; нить призрачная; длина - как у провода (15 м);
|
||||
/// ящики - любые. Урна с нитями - «логист»: её дух только перевозит, глазницы черепов светятся зелёным.
|
||||
/// ящики - любые. Нити - только у отдельного блока «урна-логист» (necroCinerariumLogist, 16.10): без зоны, дух только перевозит,
|
||||
/// глазницы всегда зелёные.
|
||||
///
|
||||
/// Инструмент - нож некроманта «хватом для нитей»: отдельный предмет necroWpnBladeNecroKnifeThread (класс ножа в сейве
|
||||
/// менять нельзя), переключение - пункт в меню ножа по R (то же меню, что «человек/змея/чертоги», SnakeMode.cs; 16.10:
|
||||
@@ -134,6 +135,11 @@ namespace NecromancerTome
|
||||
}
|
||||
if (!_start.HasValue)
|
||||
{
|
||||
if (urn != null && !urn.IsLogist)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadNeedLogist"));
|
||||
return;
|
||||
}
|
||||
_start = owner;
|
||||
_startUrn = urn != null;
|
||||
_preview = new GameObject("NecroThreadPreview");
|
||||
@@ -155,6 +161,12 @@ namespace NecromancerTome
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadNeedPair"));
|
||||
return;
|
||||
}
|
||||
TEFeatureCinerarium end = sUrn ? UrnAt(w, s) : urn;
|
||||
if (end == null || !end.IsLogist)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadNeedLogist"));
|
||||
return;
|
||||
}
|
||||
if (Vector3.Distance(s.ToVector3(), owner.ToVector3()) > MaxLength)
|
||||
{
|
||||
GameManager.ShowTooltip(_pl, Localization.Get("necroThreadTooFar"));
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Reference in New Issue
Block a user