Phase 5: field moves from Cobblemon HMs

Adrian (2026-10-31): use Cobblemon HMs; mining ore is the one hard
requirement, furnaces get lit by a Pokemon inside a Phase 6 machine.

- Cobblemon HMs 4.1.0 in the pack (Modrinth), default config.
- Self-test rock_smash_mines_veins: HMs' Rock Smash breaks a
  regenerating vein, the ore drops and the vein becomes a regenerator.
- HMs' Trainer Ditto (a wild Ditto underneath) is exempt from the
  hostility scan and the stray despawn (world/SpecialPokemon.kt).
- Docs: licenses, addon status, design, balance, roadmap note,
  Phase 5 checklist with the open questions for Adrian.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-10-31 03:00:07 +01:00
1 parent ea21bb725a
commit 56ffdd9f2e
14 files changed
+145 -6

No files matched your search

@@ -0,0 +1,62 @@
package dev.cobblesurvival.selftest
import dev.cobblesurvival.dev.OreVeins
import net.minecraft.commands.arguments.EntityAnchorArgument
import net.minecraft.core.BlockPos
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.RandomSource
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.item.Items
import net.minecraft.world.level.GameType
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
/**
* Phase 5 field moves come from Cobblemon HMs (Adrian, 2026-10-31); mining ore is the one hard requirement. Rock Smash
* must mine a regenerating vein the normal way: the ore drops and Regenerating Ore Veins turns the block into a
* regenerator, so the vein grows back. The test fires HMs' own Rock Smash handler (by reflection) at a one-block
* coal vein, away from spawn, since HMs respects spawn protection.
*/
object FieldMoveTests {
private const val VEIN = "selftest"
private const val OFFSET = 64
private val REGENERATOR = ResourceLocation.parse("regenerating_ore_veins:regenerator_block")
fun all() = listOf(SelfTestCase("rock_smash_mines_veins", 100, ::rockSmashMinesVeins))
private fun rockSmashMinesVeins(s: Scenario) {
val level = s.level
val pos = level.sharedSpawnPos.let { BlockPos(it.x + OFFSET, SelfTest.PLATFORM_Y, it.z) }
val player = s.player()
val start = player.position()
s.helper.startSequence()
.thenExecute {
s.moveTo(player, pos.x + 0.5, pos.y - 1.0, pos.z + 3.5) // forces the chunks around the vein
s.check(OreVeins.place(level, pos, VEIN, RandomSource.create(1L)) > 0) { "the test vein was not placed" }
s.check(level.getBlockState(pos).`is`(Blocks.COAL_ORE)) { "the test vein is ${level.getBlockState(pos)}" }
player.lookAt(EntityAnchorArgument.Anchor.EYES, Vec3.atCenterOf(pos))
s.check(rockSmash(player)) { "Rock Smash refused to break the ore" }
player.teleportTo(start.x, start.y, start.z)
}
.thenWaitUntil {
val block = BuiltInRegistries.BLOCK.getKey(level.getBlockState(pos).block)
s.check(block == REGENERATOR) { "the mined vein block is $block, not a regenerator: it won't grow back" }
val drops = level.getEntitiesOfClass(ItemEntity::class.java, AABB(pos).inflate(4.0)) { it.item.`is`(Items.COAL) }
s.check(drops.isNotEmpty()) { "Rock Smash dropped no coal" }
}
.thenExecute {
level.getEntitiesOfClass(ItemEntity::class.java, AABB(pos).inflate(4.0)).forEach { it.discard() }
player.setGameMode(GameType.CREATIVE) // breaking a regenerator in creative removes the vein for good
player.gameMode.destroyBlock(pos)
}
.thenSucceed()
}
private fun rockSmash(player: ServerPlayer): Boolean {
val handler = Class.forName("com.shier.cobblemonhms.ability.handlers.RockSmashHandler")
return handler.getMethod("execute", ServerPlayer::class.java).invoke(handler.getField("INSTANCE").get(null), player) as Boolean
}
}
@@ -54,7 +54,7 @@ object SelfTest {
private val cases: List<SelfTestCase> get() =
PermadeathTests.all() + SurvivalRulesTests.all() + ZoneTests.all() + HazardTests.all() + GuardianTests.all() +
VeinTests.all() + OreNodeTests.all() + DropTests.all() + ProgressionTests.all() +
CatchingTests.all() + WorldRulesTests.all()
CatchingTests.all() + WorldRulesTests.all() + FieldMoveTests.all()
private var running: List<Scenario> = emptyList()
private var forced: List<ChunkPos> = emptyList()