Пирамида без огня, банка крови как в ванили, руны ножа на другой плоскости
- пирамида: огонь убран; включённая модель светится ярко (кристалл, глазницы), выключенная тускло; граница зоны - светящееся фиолетовое кольцо по земле (LineRenderer, точки опущены на рельеф) вместо огоньков; старые огни снимаются - банка крови: снято зеркалирование - стоит как ванильная, в руке - нож: руны перенесены на противоположную плоскость клинка Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
20ee89323d
commit
6ded684d2c
@@ -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<Renderer>(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;
|
||||
}
|
||||
|
||||
/// <summary>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).</summary>
|
||||
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<LineRenderer>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Модель блока появляется позже, чем срабатывает OnAdded/загрузка, - поэтому
|
||||
/// свечение и кольцо досоздаются из UpdateTick, когда модель уже есть.</summary>
|
||||
void EnsureVisuals()
|
||||
{
|
||||
if (glowPending && ApplyModelGlow(EffectOn)) glowPending = false;
|
||||
if (ZoneShown && ring == null) SpawnZoneRing();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>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--)
|
||||
{
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user