Cobblemon Survival Pack: phases 0-2 and Phase 3 zones and hazards

Free NeoForge 1.21.1 modpack on Cobblemon 1.8.4 built with packwiz, plus
the Kotlin companion mod (cobblesurvival) and its built-in data pack.

- Phase 0: packwiz pack with Modrinth/CurseForge cross-links, build and
  export tooling, Prism instance sync, test server with smoke checks.
- Phase 1: Poke Ball shaped island (WorldGen Editor preset, seed isle-26),
  Adrian's biome list, no natural ore, structures without ore or vanilla
  mobs, no Nether portals, world template builder and world report.
- Phase 2: permadeath (CobbleHardcoreMon plus the companion mod's gap
  fixes), Revival Charm, expensive Healing Machine, Lootr refreshing
  loot, in-game self-tests on Minecraft's GameTest framework.
- Phase 3 so far: 14 zones with their own spawn pools and level bands
  (tools/zone_layout.py, tools/zone_spawns.py, "zone" spawn condition),
  Cold Sweat heat and cold as gates with boss-drop insulation recipes,
  spawn survey and hazard self-tests in tools/quick_check.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-10-29 19:01:21 +01:00
commit 398301f0be
232 files changed
+72665

No files matched your search

@@ -0,0 +1,103 @@
package dev.cobblesurvival
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.mojang.logging.LogUtils
import dev.cobblesurvival.dev.BiomePreview
import dev.cobblesurvival.permadeath.Permadeath
import dev.cobblesurvival.selftest.SelfTest
import dev.cobblesurvival.world.OnlyPokemon
import dev.cobblesurvival.zone.ZoneCondition
import dev.cobblesurvival.zone.Zones
import net.minecraft.commands.Commands
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.repository.Pack
import net.minecraft.server.packs.repository.PackSource
import net.minecraft.world.level.Level
import net.neoforged.fml.ModList
import net.neoforged.fml.common.Mod
import net.neoforged.neoforge.common.NeoForge
import net.neoforged.neoforge.event.AddPackFindersEvent
import net.neoforged.neoforge.event.RegisterCommandsEvent
import net.neoforged.neoforge.event.level.BlockEvent
import net.neoforged.neoforge.event.server.ServerStartedEvent
import org.slf4j.Logger
import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS
@Mod(CobbleSurvival.MOD_ID)
class CobbleSurvival {
companion object {
const val MOD_ID = "cobblesurvival"
val LOGGER: Logger = LogUtils.getLogger()
val version: String
get() = ModList.get().getModContainerById(MOD_ID)
.map { it.modInfo.version.toString() }
.orElse("unknown")
/** One line the server smoke test reads over RCON to confirm the mod is live. */
fun statusLine() =
"$MOD_ID $version ok; cobblemon ${Cobblemon.VERSION}; species ${PokemonSpecies.count()}"
}
init {
LOGGER.info("Cobblemon Survival companion {} loaded (Cobblemon {})", version, Cobblemon.VERSION)
MOD_BUS.addListener(::onAddPackFinders)
NeoForge.EVENT_BUS.addListener(::onRegisterCommands)
NeoForge.EVENT_BUS.addListener(::onServerStarted)
NeoForge.EVENT_BUS.addListener(::onPortalSpawn)
NeoForge.EVENT_BUS.addListener(OnlyPokemon::onChunkLoad)
NeoForge.EVENT_BUS.addListener(OnlyPokemon::onEntityJoin)
Permadeath.register()
ZoneCondition.register()
NeoForge.EVENT_BUS.addListener(Zones::onAddReloadListeners)
SelfTest.register()
}
/** The island world type has no Nether, so a lit obsidian frame must not turn into a dead portal. */
private fun onPortalSpawn(event: BlockEvent.PortalSpawnEvent) {
val level = event.level as? ServerLevel ?: return
if (level.server.getLevel(Level.NETHER) == null) {
event.isCanceled = true
}
}
/** Ships datapacks/core from the repo as a built-in data pack that is always on, in every world. */
private fun onAddPackFinders(event: AddPackFindersEvent) {
event.addPackFinders(
ResourceLocation.fromNamespaceAndPath(MOD_ID, "datapacks/core"),
PackType.SERVER_DATA,
Component.literal("Cobblemon Survival core data"),
PackSource.BUILT_IN,
true,
Pack.Position.TOP,
)
}
private fun onRegisterCommands(event: RegisterCommandsEvent) {
val root = Commands.literal(MOD_ID)
.requires { it.hasPermission(2) }
.then(Commands.literal("status").executes { context ->
context.source.sendSuccess({ Component.literal(statusLine()) }, false)
1
})
.then(BiomePreview.command())
.then(Commands.literal("zone").executes { context ->
val source = context.source
val zone = Zones.at(source.level, BlockPos.containing(source.position))
val text = zone?.let { "${it.name} (${it.id}), Lv ${it.minLevel}-${it.maxLevel}" } ?: "no zone"
source.sendSuccess({ Component.literal(text) }, false)
1
})
if (SelfTest.enabled) root.then(SelfTest.command())
event.dispatcher.register(root)
}
private fun onServerStarted(event: ServerStartedEvent) {
LOGGER.info("Cobblemon Survival companion ready: {}", statusLine())
}
}