Phase 6 done: see-through cores, faint on sacrifice and rebirth; Phase 7 design

Adrian (2026-11-02): the cores should be transparent so the Pokémon
shows; the Pokémon's death sound when a sacrifice is made or a rebirth
ends; then start the next phase (Phase 6 approved).

- Cores drawn as a solid frame around tinted glass, rendered
  translucent, no occlusion (tools/machine_assets.py).
- The shown Pokémon plays Cobblemon's faint animation and its cry
  pitched down (Cobblemon has no death sounds), vanishes after 2 s; a
  reborn Pokémon then shows in its new form. Tests check the new form.
- Phase 7 decisions (every guardian gets area attacks, Groudon for the
  Basalt Furnace, Giratina replaces the Ender Dragon, dying or leaving
  the arena loses) and design in docs/design.md; README status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-02 15:58:01 +01:00
1 parent c2a87f178e
commit dc4568e023
29 files changed
+116 -42

No files matched your search

@@ -8,6 +8,10 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.Holder
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.Entity
@@ -15,6 +19,7 @@ import net.neoforged.neoforge.common.NeoForge
import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent
import net.neoforged.neoforge.event.entity.player.AttackEntityEvent
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent
import net.neoforged.neoforge.event.tick.ServerTickEvent
import java.util.UUID
/**
@@ -28,6 +33,12 @@ object MachineDisplay {
const val TAG = "cobblesurvival_machine"
/** The chamber is one block; leave a little room. */
private const val FIT = 0.9f
/** A fainting Pokémon's cry is played lower, as in the games. */
private const val FAINT_PITCH = 0.6f
const val FAINT_TICKS = 40L
private class Fading(val entity: PokemonEntity, val until: Long)
private val fading = ArrayList<Fading>()
fun register() {
NeoForge.EVENT_BUS.addListener(::onJoin)
@@ -35,6 +46,23 @@ object MachineDisplay {
NeoForge.EVENT_BUS.addListener<PlayerInteractEvent.EntityInteractSpecific> { if (isDisplay(it.target)) it.isCanceled = true }
NeoForge.EVENT_BUS.addListener<AttackEntityEvent> { if (isDisplay(it.target)) it.isCanceled = true }
CobblemonEvents.BATTLE_STARTED_PRE.subscribe(Priority.HIGHEST, ::onBattle)
NeoForge.EVENT_BUS.addListener<ServerTickEvent.Post> { event ->
if (fading.isEmpty()) return@addListener
val now = event.server.overworld().gameTime
fading.removeIf { f -> (f.until <= now || f.entity.isRemoved).also { if (it) f.entity.discard() } }
}
}
/**
* The shown Pokémon dies (a sacrifice, or the old form at the end of a rebirth; Adrian, 2026-11-02): its cry,
* pitched down as for a fainting Pokémon, and Cobblemon's faint animation; then it's gone after [FAINT_TICKS].
*/
fun faint(level: ServerLevel, entity: PokemonEntity) {
val species = entity.pokemon.species.resourceIdentifier
val cry = SoundEvent.createVariableRangeEvent(ResourceLocation.fromNamespaceAndPath(species.namespace, "pokemon.${species.path}.cry"))
level.playSeededSound(null, entity.x, entity.y, entity.z, Holder.direct(cry), SoundSource.NEUTRAL, 1f, FAINT_PITCH, level.random.nextLong())
entity.playAnimation("faint")
fading += Fading(entity, level.gameTime + FAINT_TICKS)
}
fun isDisplay(entity: Entity?): Boolean = entity is PokemonEntity && entity.pokemon.persistentData.contains(TAG)