Phase 6: shell preview, core tooltips, machine test world, faster furnace
Adrian (2026-11-02): double the furnace speed; there must be a way in game to see how a machine is built; a test world with the machines. - Large Furnace speed doubled (2 + 0.08 x level + 0.02 x SpA). - Ghost preview: placing a core, using it with an incomplete shell, or sneak-using it empty shows every wrong spot as a small glowing block display for 10 s (never saved), in the core's orientation, with a chat line counting what's missing. - Core tooltips: what the machine does, what its shell is made of. - /cobblesurvival machines showcase builds every machine three times (working with a Showcase-owned Pokemon, empty, bare core) with signs, hoppers, chests and cables; tools/make_machine_world.py makes a superflat creative world from it (level.dat via tools/nbtfile.py) and --prism installs it as "Machine Test (Phase 6)". - Self-tests machine_preview_shows_the_shell and machine_showcase_builds_every_machine; docs and checklist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0877a6a179
commit
c2a87f178e
17 files changed
+558
-27
No files matched your search
@@ -9,6 +9,8 @@ import dev.cobblesurvival.machine.MachineBlock
|
||||
import dev.cobblesurvival.machine.MachineBlockEntity
|
||||
import dev.cobblesurvival.machine.MachineDefs
|
||||
import dev.cobblesurvival.machine.MachineDisplay
|
||||
import dev.cobblesurvival.machine.MachineGhosts
|
||||
import dev.cobblesurvival.machine.MachineShowcase
|
||||
import dev.cobblesurvival.machine.MachineWork
|
||||
import dev.cobblesurvival.machine.Shell
|
||||
import net.minecraft.core.BlockPos
|
||||
@@ -42,6 +44,8 @@ object MachineTests {
|
||||
/** Blocks south (unload) and north (restart) of spawn: far beyond view distance, clear of the vein tests (east, west). */
|
||||
private const val UNLOAD_OFFSET = 352
|
||||
private const val RESTART_OFFSET = -352
|
||||
/** The showcase test: south of the unload spot. */
|
||||
private const val SHOWCASE_OFFSET = 448
|
||||
|
||||
fun all() = listOf(
|
||||
SelfTestCase("machine_pokemon_goes_in_and_out", 60, ::inAndOut),
|
||||
@@ -56,18 +60,13 @@ object MachineTests {
|
||||
SelfTestCase("synthesis_core_rolls_drops", 100, ::synthesis),
|
||||
SelfTestCase("altar_core_sacrifice_needs_confirmation", 60, ::altar),
|
||||
SelfTestCase("rebirth_core_makes_a_shiny_first_stage", 100, ::rebirth),
|
||||
SelfTestCase("machine_preview_shows_the_shell", 300, ::preview),
|
||||
SelfTestCase("machine_showcase_builds_every_machine", 100, ::showcase),
|
||||
)
|
||||
|
||||
private fun id(name: String) = ResourceLocation.fromNamespaceAndPath(CobbleSurvival.MOD_ID, name)
|
||||
private fun id(name: String) = MachineShowcase.id(name)
|
||||
|
||||
fun build(level: ServerLevel, core: BlockPos, name: String, casing: Block): MachineBlockEntity {
|
||||
val def = MachineDefs.byCore[id(name)] ?: throw GameTestAssertException("no machine data for ${id(name)}")
|
||||
for ((offset, text) in def.shell.layout()) {
|
||||
level.setBlockAndUpdate(core.offset(offset), if (text == Shell.AIR) Blocks.AIR.defaultBlockState() else casing.defaultBlockState())
|
||||
}
|
||||
level.setBlockAndUpdate(core, BuiltInRegistries.BLOCK.get(id(name)).defaultBlockState().setValue(MachineBlock.FACING, Direction.SOUTH))
|
||||
return level.getBlockEntity(core) as? MachineBlockEntity ?: throw GameTestAssertException("no machine at $core")
|
||||
}
|
||||
fun build(level: ServerLevel, core: BlockPos, name: String, casing: Block): MachineBlockEntity = MachineShowcase.build(level, core, name, casing)
|
||||
|
||||
private fun build(s: Scenario, name: String, casing: Block) = build(s.level, s.helper.absolutePos(CORE), name, casing)
|
||||
|
||||
@@ -357,4 +356,52 @@ object MachineTests {
|
||||
}
|
||||
.thenSucceed()
|
||||
}
|
||||
|
||||
private fun ghosts(level: ServerLevel, core: BlockPos) =
|
||||
level.getEntitiesOfClass(net.minecraft.world.entity.Display.BlockDisplay::class.java, AABB(core).inflate(3.0)) { it.tags.contains(MachineGhosts.TAG) }
|
||||
|
||||
/** Using a core with a broken shell shows the wrong spots as ghost blocks; they go away again. */
|
||||
private fun preview(s: Scenario) {
|
||||
val level = s.level
|
||||
val player = s.player()
|
||||
val core = build(s, "heat_core", Blocks.BRICKS)
|
||||
val pos = core.blockPos
|
||||
level.setBlockAndUpdate(pos.offset(-1, 1, -2), Blocks.AIR.defaultBlockState())
|
||||
level.setBlockAndUpdate(pos.offset(1, -1, -1), Blocks.DIRT.defaultBlockState())
|
||||
level.setBlockAndUpdate(pos.offset(0, 0, -1), Blocks.STONE.defaultBlockState())
|
||||
core.use(player)
|
||||
s.check(ghosts(level, pos).size == 3) { "${ghosts(level, pos).size} ghost blocks for 3 wrong spots" }
|
||||
s.helper.startSequence()
|
||||
.thenWaitUntil { s.check(ghosts(level, pos).isEmpty()) { "the ghost blocks didn't go away" } }
|
||||
.thenSucceed()
|
||||
}
|
||||
|
||||
/** The test world's showcase: every machine works (Pokémon inside, complete shell), every bare core is there. */
|
||||
private fun showcase(s: Scenario) {
|
||||
val level = s.level
|
||||
val origin = spot(level, SHOWCASE_OFFSET)
|
||||
val area = BlockPos.betweenClosed(origin.offset(-3, -3, -3), origin.offset(5 * 11 + 3, 4, 2 * 12 + 6))
|
||||
val chunkSet = area.map { ChunkPos(it) }.distinct()
|
||||
chunkSet.forEach { level.setChunkForced(it.x, it.z, true) }
|
||||
s.afterTest {
|
||||
BlockPos.betweenClosed(origin.offset(-3, -3, -3), origin.offset(5 * 11 + 3, 4, 2 * 12 + 6)).forEach {
|
||||
level.setBlockAndUpdate(it, Blocks.AIR.defaultBlockState())
|
||||
}
|
||||
chunkSet.forEach { level.setChunkForced(it.x, it.z, false) }
|
||||
}
|
||||
val lines = MachineShowcase.showcase(level, origin)
|
||||
s.check(lines.size == MachineDefs.byCore.size) { "the showcase built ${lines.size} machines, not ${MachineDefs.byCore.size}" }
|
||||
val cores = BlockPos.betweenClosed(origin, origin.offset(5 * 11, 2, 2 * 12 + 3))
|
||||
.mapNotNull { level.getBlockEntity(it) as? MachineBlockEntity }
|
||||
s.check(cores.size == 3 * lines.size) { "${cores.size} cores in the showcase, not ${3 * lines.size}" }
|
||||
s.helper.startSequence()
|
||||
.thenWaitUntil {
|
||||
val working = cores.filter { it.pokemon != null }
|
||||
s.check(working.size == lines.size && working.all { it.def!!.shell.match(level, it.blockPos) != null }) {
|
||||
"${working.size} working machines with complete shells, not ${lines.size}"
|
||||
}
|
||||
s.check(working.sumOf { displays(level, it.blockPos).size } == lines.size) { "not every working machine shows its Pokémon" }
|
||||
}
|
||||
.thenSucceed()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user