diff --git a/HarmonySrc/PyramidWardPatch.cs b/HarmonySrc/PyramidWardPatch.cs index b041868..4e8f2f4 100644 --- a/HarmonySrc/PyramidWardPatch.cs +++ b/HarmonySrc/PyramidWardPatch.cs @@ -269,97 +269,108 @@ namespace NecromancerTome // `ToWorldPos()` (this feature's own position) instead of a dictionary-passed key. // ------------------------------------------------------------------ + // 01.10.2026: огня больше нет. Своя модель (бандл necroward) светится сама - кристалл и + // глазницы в карте эмиссии; включённая пирамида светится ярко, выключенная тускло + // (MaterialPropertyBlock на рендерерах модели блока, материал общий не трогается). + static readonly MaterialPropertyBlock glowBlock = new MaterialPropertyBlock(); + + Transform ModelTransform() + { + World world = GameManager.Instance?.World; + if (world == null) return null; + Vector3i pos = ToWorldPos(); + Chunk chunk = world.GetChunkFromWorldPos(pos) as Chunk; + BlockEntityData be = chunk?.GetBlockEntity(pos); + return be != null && be.bHasTransform ? be.transform : null; + } + + bool ApplyModelGlow(bool on) + { + Transform t = ModelTransform(); + if (t == null) return false; + Color c = on ? new Color(2.2f, 2.2f, 2.2f, 1f) : new Color(0.25f, 0.25f, 0.25f, 1f); + foreach (Renderer r in t.GetComponentsInChildren(true)) + { + r.GetPropertyBlock(glowBlock); + glowBlock.SetColor("_EmissionColor", c); + r.SetPropertyBlock(glowBlock); + } + return true; + } + public void SpawnGlow() { - if (GameManager.Instance == null) - { - return; - } - Vector3i pos = ToWorldPos(); - if (GameManager.Instance.HasBlockParticleEffect(pos)) - { - return; - } - // FIXED 2026-09-02 (user report: "горение пирамидки почему-то смещено на куб вверх и - // вбок"): World.blockToTransformPos(Vector3i) ALREADY returns (x+0.5, y, z+0.5) - // (decompiled to confirm - horizontally centered, y left raw/un-centered) - adding - // another +0.5 on x/z on top of that (the original bug) double-centered it, landing a - // full extra block over on both horizontal axes. Only the vertical lift (how far above - // the block the flame sits) is actually ours to add. Height LOWERED again same day - // ("сделай ниже не 2/3 куба" - after the sideways offset was gone, still sat too high): - // 1.2 (0.2 above the full block top) -> 0.6, under 2/3 (0.667) of a block as asked. - // Height lowered twice same day: 1.2 -> 0.6 ("сделай ниже не 2/3 куба"), then -> 0.4 - // (direct follow-up: "Снизь высоту пламени до +0.4"). - Vector3 worldPos = World.blockToTransformPos(pos) + new Vector3(0f, 0.4f, 0f); - // WardTint (not Color.white) as the ParticleEffect's own _color: ParticleEffect. - // SpawnParticleEffect applies this directly to any non-ParticleSystem Renderer on the - // prefab (decompiled to confirm) - covers a sub-emitter/glow sprite ApplyGlowTint's own - // ParticleSystem-only loop wouldn't reach, belt-and-suspenders alongside it. - GameManager.Instance.SpawnBlockParticleEffect(pos, new ParticleEffect(GlowParticleName, worldPos, Quaternion.identity, 0f, WardTint)); - pendingTint.Add(pos); + if (GameManager.Instance != null && !ApplyModelGlow(true)) glowPending = true; } public void RemoveGlow() { - if (GameManager.Instance == null) - { - return; - } + if (GameManager.Instance == null) return; + // пирамиды, поставленные до 01.10, могли оставить огонь - снимаем, если есть Vector3i pos = ToWorldPos(); - if (GameManager.Instance.HasBlockParticleEffect(pos)) - { - GameManager.Instance.RemoveBlockParticleEffect(pos); - } + if (GameManager.Instance.HasBlockParticleEffect(pos)) GameManager.Instance.RemoveBlockParticleEffect(pos); pendingTint.Remove(pos); + if (!ApplyModelGlow(false)) glowPending = true; } - /// Ring of glow points marking EffectRadius, keyed by y = -1000-i (real block - /// y-coordinates never go negative that far, so these keys can never collide with an - /// actual placed block's own glow key). + bool glowPending = true; + + // Граница зоны (01.10: «вместо огня другие частицы, как круг у квеста на копание»): + // светящееся фиолетовое кольцо по земле - LineRenderer, точки опущены на рельеф лучом. + // Дочерний объект модели блока - поэтому сдвиги Origin и выгрузка чанка его не теряют. + GameObject ring; + public void SpawnZoneRing() { - if (GameManager.Instance == null) + if (ring != null) return; + Transform t = ModelTransform(); + if (t == null) return; + ring = new GameObject("NecroWardRing"); + ring.transform.SetParent(t, false); + var lr = ring.AddComponent(); + lr.useWorldSpace = false; lr.loop = true; + const int n = 96; + lr.positionCount = n; + lr.widthMultiplier = 0.14f; + var mat = new Material(Shader.Find("Sprites/Default")); + lr.material = mat; + Color col = new Color(0.75f, 0.25f, 1f, 0.85f); + lr.startColor = col; lr.endColor = col; + lr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; lr.receiveShadows = false; + Vector3 c = t.position; + for (int i = 0; i < n; i++) { - return; - } - Vector3i basePos = ToWorldPos(); - // Same double-centering bug as SpawnGlow's own fix above - blockToTransformPos already - // centers x/z, only the vertical offset (0.5, mid-block height) is ours to add. - Vector3 center = World.blockToTransformPos(basePos) + new Vector3(0f, 0.5f, 0f); - for (int i = 0; i < ZoneRingPointCount; i++) - { - float angle = i * (360f / ZoneRingPointCount) * Mathf.Deg2Rad; - Vector3 point = center + new Vector3(Mathf.Cos(angle) * EffectRadius, 0f, Mathf.Sin(angle) * EffectRadius); - Vector3i key = new Vector3i(basePos.x, -1000 - i, basePos.z); - if (GameManager.Instance.HasBlockParticleEffect(key)) - { - continue; - } - // ZoneRingParticleName (not GlowParticleName) - see that constant's own comment: - // user asked for the boundary to read as "some particles", not literal fire. - GameManager.Instance.SpawnBlockParticleEffect(key, new ParticleEffect(ZoneRingParticleName, point, Quaternion.identity, 0f, WardTint)); - pendingTint.Add(key); + float ang = i * Mathf.PI * 2f / n; + Vector3 p = c + new Vector3(Mathf.Cos(ang) * EffectRadius, 0f, Mathf.Sin(ang) * EffectRadius); + if (Physics.Raycast(p + Vector3.up * 12f, Vector3.down, out RaycastHit hit, 30f, 65536)) + p.y = hit.point.y + 0.08f; + lr.SetPosition(i, t.InverseTransformPoint(p)); } } public void RemoveZoneRing() { - if (GameManager.Instance == null) - { - return; - } + if (ring != null) { UnityEngine.Object.Destroy(ring); ring = null; } + if (GameManager.Instance == null) return; + // кольца из огоньков от версий до 01.10 Vector3i basePos = ToWorldPos(); for (int i = 0; i < ZoneRingPointCount; i++) { Vector3i key = new Vector3i(basePos.x, -1000 - i, basePos.z); - if (GameManager.Instance.HasBlockParticleEffect(key)) - { - GameManager.Instance.RemoveBlockParticleEffect(key); - } + if (GameManager.Instance.HasBlockParticleEffect(key)) GameManager.Instance.RemoveBlockParticleEffect(key); pendingTint.Remove(key); } } + /// Модель блока появляется позже, чем срабатывает OnAdded/загрузка, - поэтому + /// свечение и кольцо досоздаются из UpdateTick, когда модель уже есть. + void EnsureVisuals() + { + if (glowPending && ApplyModelGlow(EffectOn)) glowPending = false; + if (ZoneShown && ring == null) SpawnZoneRing(); + } + + /// Mirrors ParticlePatch.cs's own tint technique (same ParticleSystem.MainModule /// fields, same reasoning: the particle prefab always instantiates at its authored color, /// nothing in XML/the block-particle API can override that) - kept as its own copy here @@ -463,6 +474,7 @@ namespace NecromancerTome { return; } + EnsureVisuals(); for (int i = pendingTint.Count - 1; i >= 0; i--) { diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 34e6d05..abb33f1 100644 Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb index de9c977..6016616 100644 Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ diff --git a/Resources/necroblood b/Resources/necroblood index 8e813f7..5e294d4 100644 Binary files a/Resources/necroblood and b/Resources/necroblood differ diff --git a/Resources/necroknife b/Resources/necroknife index dc329af..10b5741 100644 Binary files a/Resources/necroknife and b/Resources/necroknife differ