Phase 3: Alpha guardians, one per zone, back 5 hours after a defeat
CI / build-and-smoke-test (push) Failing after 7m6s
CI / build-and-smoke-test (push) Failing after 7m6s
- Companion mod (zone/Guardians.kt): each zone's guardian stands at the zone's sample point, appears when a player is within 96 blocks, stays within 24 blocks, is uncatchable and half again as big. Beating it in a battle gives every player in the battle its drops; it returns after 5 hours of game time (Adrian: fixed spots, 5 hours). State lives in the world save; /cobblesurvival guardians lists it. - tools/zone_spawns.py writes data/cobblesurvival/guardians/<zone>.json: the strongest walking (or swimming) Pokemon of the zone at band top + 5, with the drops from world/zones.json. Boss drops follow Adrian's rule: Icy Rocks before Sunscorch, Heat Rocks before Frostpeak. - Goat Fur and Hoglin Hide recipes make 8 per rock, so one guardian's 3 rocks pay for one full insulating set. - Self-test guardian_appears_and_returns; hazard tests cleaned up; the zone generator reads Cobblemon's own entries from the field guide's hidden_spawns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
398301f0be
commit
73fe71918f
25 files changed
+607
-23
No files matched your search
@@ -0,0 +1,75 @@
|
||||
package dev.cobblesurvival.selftest
|
||||
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.MoveActionResponse
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.isInBattle
|
||||
import dev.cobblesurvival.zone.Guardians
|
||||
import dev.cobblesurvival.zone.Zones
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.gametest.framework.GameTestAssertException
|
||||
import net.minecraft.world.level.levelgen.Heightmap
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* Roadmap Phase 3, Alpha guardians (Adrian: fixed spots, back 5 hours after being beaten). On Button Isle's spot:
|
||||
* a player arriving makes the guardian appear, big and uncatchable at its level; beating it in a battle gives the
|
||||
* player its drops; it stays away, and returns once its time is up. Runs in a batch of its own, since the guardian
|
||||
* state is shared.
|
||||
*/
|
||||
object GuardianTests {
|
||||
private const val ZONE = "button_isle"
|
||||
|
||||
fun all() = listOf(SelfTestCase("guardian_appears_and_returns", 2400, ::guardian, batch = "guardian"))
|
||||
|
||||
private fun guardian(s: Scenario) {
|
||||
val definition = Guardians.definitions[ZONE] ?: throw GameTestAssertException("no guardian defined for $ZONE")
|
||||
val sample = Zones.samples[ZONE] ?: throw GameTestAssertException("no sample point for $ZONE")
|
||||
val level = s.level
|
||||
Guardians.resetForTest(level, ZONE)
|
||||
s.afterTest { Guardians.resetForTest(level, ZONE) }
|
||||
|
||||
val player = s.player()
|
||||
s.give(player, s.pokemon("mewtwo", 100, "earthquake")) // a spread move needs no target
|
||||
s.answerBattles(player) { _, _ -> MoveActionResponse("earthquake") }
|
||||
lateinit var guardian: PokemonEntity
|
||||
val expectedLevel = Regex("level=(\\d+)").find(definition.pokemon)?.groupValues?.get(1)?.toInt()
|
||||
|
||||
s.helper.startSequence()
|
||||
.thenExecute {
|
||||
level.getChunk(sample.x shr 4, sample.z shr 4)
|
||||
val y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, sample.x, sample.z)
|
||||
s.moveTo(player, sample.x + 6.5, y.toDouble(), sample.z + 0.5)
|
||||
}
|
||||
.thenWaitUntil {
|
||||
guardian = Guardians.guardianOf(level, ZONE) ?: throw GameTestAssertException("no guardian appeared near the player")
|
||||
}
|
||||
.thenExecute {
|
||||
val pokemon = guardian.pokemon
|
||||
s.check(pokemon.isUncatchable()) { "the guardian can be caught" }
|
||||
s.check(abs(pokemon.scaleModifier - definition.scale) < 0.01f) { "the guardian is size ${pokemon.scaleModifier}, not ${definition.scale}" }
|
||||
s.check(expectedLevel == null || pokemon.level == expectedLevel) { "the guardian is level ${pokemon.level}, not $expectedLevel" }
|
||||
s.moveTo(player, guardian.x + 3, guardian.y, guardian.z)
|
||||
}
|
||||
.thenWaitUntil { s.check(guardian.pokemon.entity === guardian) { "the guardian has not ticked yet" } }
|
||||
.thenExecute { s.started(BattleBuilder.pve(player, guardian)) }
|
||||
.thenWaitUntil {
|
||||
s.check(!player.isInBattle() && Guardians.defeatedAt(level, ZONE) != null) { "the guardian isn't beaten yet: ${s.battleState(player)}" }
|
||||
}
|
||||
.thenExecute {
|
||||
for ((item, count) in definition.drops) {
|
||||
val have = player.inventory.countItem(BuiltInRegistries.ITEM.get(item))
|
||||
s.check(have >= count) { "the player got $have of $item from the guardian, not $count" }
|
||||
}
|
||||
}
|
||||
.thenIdle(100)
|
||||
.thenExecute {
|
||||
s.check(Guardians.guardianOf(level, ZONE) == null) { "the guardian came back right away" }
|
||||
Guardians.expireDefeatForTest(level, ZONE)
|
||||
}
|
||||
.thenWaitUntil {
|
||||
s.check(Guardians.guardianOf(level, ZONE) != null) { "the guardian didn't come back after its time" }
|
||||
}
|
||||
.thenSucceed()
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ object SelfTest {
|
||||
private const val FORCED_CHUNKS = 9
|
||||
|
||||
private val cases: List<SelfTestCase> get() =
|
||||
PermadeathTests.all() + SurvivalRulesTests.all() + ZoneTests.all() + HazardTests.all()
|
||||
PermadeathTests.all() + SurvivalRulesTests.all() + ZoneTests.all() + HazardTests.all() + GuardianTests.all()
|
||||
|
||||
private var running: List<Scenario> = emptyList()
|
||||
private var forced: List<ChunkPos> = emptyList()
|
||||
|
||||
Reference in new issue
Block a user