Phase 9: safe start island, better quest book, every useful item obtainable

- Button Isle is peaceful: wild Pokémon only fight a player who hit them first
  (tuning/server.json peaceful_zones); its apricorns and coal make Poké Balls
  (4 apricorns + coal or charcoal), and the world report checks it has enough.
- Quest book: no boilerplate; each zone page says what is particular to it, and
  every zone that needs gear or a field move to be safe has its own quest. New
  page on the bigger mods (Cold Sweat, Fight or Flight, Cobblebase, breeding,
  veins, shared loot, maps and info).
- The Mire's poison stops 10 blocks below the surface.
- Player battles never cost a Pokémon (CobbleHardcoreMon config, mixins, and the
  companion mod's own permadeath).
- tools/item_audit.py: every item with a use has a source on the island; new
  sources for the gaps it found (species additions, three recipes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-10 15:04:09 +01:00
1 parent 6ace26e012
commit d29e177f9d
65 files changed
+1920 -364

No files matched your search

@@ -1,5 +1,7 @@
package dev.cobblesurvival.selftest
import net.minecraft.world.level.block.Blocks
import net.minecraft.core.BlockPos
import dev.cobblesurvival.CobbleSurvival
import dev.cobblesurvival.zone.ZoneSample
import dev.cobblesurvival.zone.Zones
@@ -133,6 +135,7 @@ object HazardTests {
val bare = s.player()
val wading = s.player().also { it.setItemSlot(EquipmentSlot.FEET, ItemStack(waders)) }
val outside = s.player()
val below = s.player()
fun surface(sample: ZoneSample): Double {
level.getChunk(sample.x shr 4, sample.z shr 4)
return level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, sample.x, sample.z).toDouble()
@@ -141,6 +144,10 @@ object HazardTests {
.thenExecute {
for (player in listOf(bare, wading)) s.moveTo(player, mire.x + 0.5, surface(mire), mire.z + 0.5)
s.moveTo(outside, elsewhere.x + 0.5, surface(elsewhere), elsewhere.z + 0.5)
// Deep in the rock under the Mire (a pocket cut for it), where the swamp's poison doesn't reach.
val deep = BlockPos(mire.x + 2, surface(mire).toInt() - 30, mire.z)
for (dy in 0..1) level.setBlockAndUpdate(deep.above(dy), Blocks.AIR.defaultBlockState())
s.moveTo(below, deep.x + 0.5, deep.y.toDouble(), deep.z + 0.5)
}
.thenIdle(220)
.thenExecute {
@@ -148,6 +155,7 @@ object HazardTests {
s.check(bare.hasEffect(poison)) { "a player without waders in the Mire isn't poisoned" }
s.check(!wading.hasEffect(poison)) { "a player in Mire Waders got poisoned" }
s.check(!outside.hasEffect(poison)) { "a player on Button Isle got poisoned" }
s.check(!below.hasEffect(poison)) { "a player deep under the Mire got poisoned" }
}
.thenSucceed()
}