Phase 7: one End boss at a time; more visible attack telegraphs

Adrian (2026-11-02): a second Eternatus appeared on his second visit
to the End; the attacks should be more visible.

- EndBoss waits until the entities around its spot are loaded before
  deciding it's missing, refuses a second copy while the recorded one
  is alive (join check, like the guardians), and removes extra copies
  near the spot (cleans up worlds that already have two).
- Telegraphs: 2.2-size dust redrawn every 2 ticks, the inside filled
  (Area.fill), rising sparks on the outline, a low bell on cast.
- Tests: no second End boss (after the End's middle is loaded), fill
  points inside their areas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-02 16:50:58 +01:00
1 parent c451c58483
commit 4e7b16abae
6 files changed
+89 -25

No files matched your search

@@ -103,6 +103,16 @@ sealed class Area(val origin: Vec3) {
abstract fun containsFlat(x: Double, z: Double): Boolean
/** Points along the outline, for the telegraph's particles. */
abstract fun outline(): List<Vec3>
/** How far from [origin] the area reaches. */
abstract val reach: Double
/** Points spread over the inside, so the whole area shows, not just its edge. */
fun fill(): List<Vec3> {
val steps = (reach / FILL_STEP).toInt()
return (-steps..steps).flatMap { i -> (-steps..steps).map { j -> origin.x + i * FILL_STEP to origin.z + j * FILL_STEP } }
.filter { (x, z) -> containsFlat(x, z) }
.map { (x, z) -> at(x, z) }
}
fun contains(pos: Vec3) = kotlin.math.abs(pos.y - origin.y) <= REACH_Y && containsFlat(pos.x, pos.z)
@@ -114,16 +124,19 @@ sealed class Area(val origin: Vec3) {
class Circle(origin: Vec3, val radius: Double) : Area(origin) {
override fun containsFlat(x: Double, z: Double) = hypot(x - origin.x, z - origin.z) <= radius
override fun outline() = circle(radius) + circle(radius / 2)
override fun outline() = circle(radius)
override val reach get() = radius
}
class Ring(origin: Vec3, val inner: Double, val outer: Double) : Area(origin) {
override fun containsFlat(x: Double, z: Double) = hypot(x - origin.x, z - origin.z).let { it in inner..outer }
override fun outline() = circle(inner) + circle(outer)
override val reach get() = outer
}
/** From [origin] along [heading] (radians, x towards z), [length] long, [halfWidth] to each side. */
class Line(origin: Vec3, val heading: Double, val length: Double, val halfWidth: Double) : Area(origin) {
override val reach get() = length
override fun containsFlat(x: Double, z: Double): Boolean {
val dx = x - origin.x
val dz = z - origin.z
@@ -142,6 +155,7 @@ sealed class Area(val origin: Vec3) {
/** From [origin] along [heading], [length] far, [halfAngle] radians to each side. */
class Cone(origin: Vec3, val heading: Double, val length: Double, val halfAngle: Double) : Area(origin) {
override val reach get() = length
override fun containsFlat(x: Double, z: Double): Boolean {
val distance = hypot(x - origin.x, z - origin.z)
if (distance > length) return false
@@ -167,6 +181,7 @@ sealed class Area(val origin: Vec3) {
companion object {
const val REACH_Y = 3.0
private const val STEP = 0.6
private const val STEP = 0.5
private const val FILL_STEP = 1.25
}
}