From dc4568e0230e3f605190825560e4e343120360e5 Mon Sep 17 00:00:00 2001 From: claude Date: Mon, 2 Nov 2026 15:58:01 +0100 Subject: [PATCH] Phase 6 done: see-through cores, faint on sacrifice and rebirth; Phase 7 design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- README.md | 2 +- docs/checklists/phase-6.md | 4 ++ docs/design.md | 14 +++- .../machine/MachineBlockEntity.kt | 20 ++++-- .../cobblesurvival/machine/MachineDisplay.kt | 28 ++++++++ .../dev/cobblesurvival/machine/MachineWork.kt | 4 +- .../dev/cobblesurvival/machine/Machines.kt | 4 +- .../cobblesurvival/selftest/MachineTests.kt | 11 ++- .../models/block/altar_core.json | 1 + .../models/block/dynamo_core.json | 1 + .../models/block/heat_core.json | 1 + .../models/block/rebirth_core.json | 1 + .../models/block/synthesis_core.json | 1 + .../textures/block/altar_core_front.png | Bin 270 -> 322 bytes .../textures/block/altar_core_side.png | Bin 215 -> 205 bytes .../textures/block/altar_core_top.png | Bin 260 -> 223 bytes .../textures/block/dynamo_core_front.png | Bin 205 -> 276 bytes .../textures/block/dynamo_core_side.png | Bin 193 -> 200 bytes .../textures/block/dynamo_core_top.png | Bin 249 -> 222 bytes .../textures/block/heat_core_front.png | Bin 242 -> 258 bytes .../textures/block/heat_core_side.png | Bin 217 -> 204 bytes .../textures/block/heat_core_top.png | Bin 264 -> 219 bytes .../textures/block/rebirth_core_front.png | Bin 235 -> 278 bytes .../textures/block/rebirth_core_side.png | Bin 194 -> 201 bytes .../textures/block/rebirth_core_top.png | Bin 254 -> 227 bytes .../textures/block/synthesis_core_front.png | Bin 238 -> 261 bytes .../textures/block/synthesis_core_side.png | Bin 186 -> 205 bytes .../textures/block/synthesis_core_top.png | Bin 247 -> 227 bytes tools/machine_assets.py | 66 ++++++++++-------- 29 files changed, 116 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index c0ceee8..88be512 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A free NeoForge 1.21.1 modpack on Cobblemon 1.8: one large island with soft-gated, Palworld/ARK-style progression, plus a companion mod for the mechanics no addon covers. The plan is in [docs/roadmap.md](docs/roadmap.md); decisions taken so far are in [docs/design.md](docs/design.md). -**Status:** Phases 0–5 approved (2026-10-31); Phase 6 (Pokémon machines) in progress. Checks per phase: `docs/checklists/`. +**Status:** Phases 0–6 approved (2026-11-02); Phase 7 (boss battles with area attacks) in progress. Checks per phase: `docs/checklists/`. ## What's in the pack diff --git a/docs/checklists/phase-6.md b/docs/checklists/phase-6.md index 89e72be..0a2e165 100644 --- a/docs/checklists/phase-6.md +++ b/docs/checklists/phase-6.md @@ -30,6 +30,8 @@ Elsewhere, creative mode gives the cores in the Functional Blocks tab; each has - [ ] Use a bare core (third row): ghost blocks show its shell for ten seconds, and chat lists what's missing. Place a new core anywhere: the same. - [ ] Hover a core in the inventory: the tooltip says what it does and what its shell is made of. +- [ ] Look at a working machine from the front: the core is see-through (frame and tinted glass), the Pokémon shows behind it. +- [ ] Sacrifice a Pokémon, or finish a rebirth: the Pokémon in the chamber faints with its cry pitched down, then disappears (a reborn one comes back in its new form). - [ ] Build a Heat Core shell of bricks with glass in a side window, send out a Fire type and use the core: the Pokémon stands in the chamber, small enough to fit, and doesn't move. **Does it look right?** (the roadmap asks you to confirm this) - [ ] Put a furnace against the outside of that shell, a hopper with raw iron above it and a hopper with a chest below: the iron cooks without fuel, faster with a stronger Pokémon (speed doubled on 2026-11-02: 7.4× at Lv 50), and ends in the chest. @@ -41,5 +43,7 @@ Elsewhere, creative mode gives the cores in the Functional Blocks tab; each has ## Ask Adrian +**Phase 6 approved 2026-11-02** (after the see-through cores and the faint: "After that, you can begin with the next phase"). Numbers, casings and recipes stay placeholders. + - Does the Pokémon in the chamber look right (size, place, facing out through the core's side)? - All numbers, casings and recipes are placeholders (`docs/balance.md`). diff --git a/docs/design.md b/docs/design.md index 08dce70..f9fd921 100644 --- a/docs/design.md +++ b/docs/design.md @@ -245,7 +245,7 @@ Adrian's choices: the Large Furnace has a Fire Pokémon heat real furnaces, and **A Pokémon goes in** when its owner sends it out near the core and uses the core with an empty hand: the nearest of their sent-out Pokémon that fits walks in (it leaves the party and its data is kept in the core). Using the core again takes it out, back to the party or else the PC. Sneak-use shows the machine's state. Breaking the core or any part of the shell sends the Pokémon back to its owner, online or not. The core is the only place the Pokémon exists while inside, so nothing can be duplicated by breaking and inserting at once. -**What's shown** is a real Cobblemon Pokémon entity, a copy, standing in the chamber: scaled to fit, no AI, invulnerable, silent, uncatchable, and it can't be battled or used. It's never kept in the world save: one loaded from disk is dropped, and the core puts a fresh one there. +**What's shown** is a real Cobblemon Pokémon entity, a copy, standing in the chamber: scaled to fit, no AI, invulnerable, silent, uncatchable, and it can't be battled or used. It's never kept in the world save: one loaded from disk is dropped, and the core puts a fresh one there. The cores are see-through (Adrian, 2026-11-02): a solid frame around tinted glass, rendered translucent, so the Pokémon shows through the core as well as through glass windows. When a sacrifice is made or a rebirth ends, the shown Pokémon plays Cobblemon's faint animation and its cry pitched down (Cobblemon has no separate death sounds; the games pitch the cry down when a Pokémon faints), and vanishes two seconds later; a reborn Pokémon then appears in its new form. **Large Furnace.** Furnaces, blast furnaces and smokers (`#cobblesurvival:machine_furnaces`) standing against the outside of the shell burn without fuel while they have something to cook, and cook faster. They stay ordinary vanilla blocks, so their screens, recipes and hoppers work as always (input from above, output from below). How many are heated and how fast depend on the Pokémon's level and Special Attack (numbers in the data file and `docs/balance.md`). @@ -263,6 +263,18 @@ Self-tests (`MachineTests`): in and out, wrong Pokémon refused, broken shell an **Generator.** The Dynamo Core stores Forge Energy and pushes it to whatever touches the core and takes energy (cables, machines from other mods); its output per tick depends on the Pokémon's level and Special Attack, in line with Poke Power's 50 FE per level. +## Phase 7: boss battles with area attacks (Adrian, 2026-11-02) + +Adrian's choices: every zone guardian gets area attacks (more and harder in harder zones); the Basalt Furnace's boss is **Groudon**; **Giratina** replaces the Ender Dragon; dying or leaving the arena loses the fight; area attacks hit only the player (the roadmap's default). + +**Bosses are data**, `datapacks/core/data/cobblesurvival/bosses/.json`, written by `tools/zone_bosses.py` from a tier table per zone band: which fight it is (a zone's guardian, or the End boss), the arena radius, and the attack patterns. A pattern has a shape (circle, ring, line, cone) and its size, where it's aimed (at the player, or around the boss), a telegraph time, damage, an optional effect (fire: set alight; ice: slowed and freezing) and a trigger (every so many seconds, every so many turns, or once when the boss's HP falls below a share). + +**Runtime** (companion mod). A battle between players and a boss Pokémon becomes a boss fight. While it lasts, due patterns telegraph with particles on the ground where they'll land, then hit the battling players standing inside; everything stops when the battle ends. The arena is a circle around where the boss stood when the fight began: a player who leaves it, or dies, loses the fight (the battle ends, the boss stays, nothing drops). Cobblemon's own flee check only applies when a battle starts, which is why the arena is enforced here. + +**Groudon** becomes the Basalt Furnace's guardian like the others (Lv 75, band top + 5), with the zone's placeholder drops. + +**Giratina** replaces the Ender Dragon: the dragon no longer spawns; Giratina (uncatchable, Lv 80) appears on the main End island while a player is there, and beating it does what killing the dragon did (exit portal, gateways, the egg on the first win). + ## Open decisions for Adrian (proceeding with the default, flagged) | Decision | Default used for now | diff --git a/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineBlockEntity.kt b/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineBlockEntity.kt index 219af69..5e507e1 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineBlockEntity.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineBlockEntity.kt @@ -54,6 +54,8 @@ class MachineBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Machine internal var confirmBy: UUID? = null internal var confirmUntil = 0L private var display: PokemonEntity? = null + /** No Pokémon shown before this game time (the old one is still fainting). */ + private var showFrom = 0L private var ticks = 0 val def: MachineDefs.Def? get() = MachineDefs.byCore[BuiltInRegistries.BLOCK.getKey(blockState.block)] @@ -68,7 +70,7 @@ class MachineBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Machine giveBack(level, Component.translatableWithFallback("message.cobblesurvival.machine.shell_broke", "The shell of the %s broke.", machineName)) return } - if (display?.isAlive != true) display = MachineDisplay.spawn(level, pokemon, worldPosition, shell!!.chamber, facing()) + if (display?.isAlive != true && level.gameTime >= showFrom) display = MachineDisplay.spawn(level, pokemon, worldPosition, shell!!.chamber, facing()) } MachineWork.tick(this, level, def) receivedThisTick = 0 @@ -184,8 +186,18 @@ class MachineBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Machine return true } - /** The Pokémon is gone for good (sacrificed). */ - internal fun consume() = clear() + /** The Pokémon is gone for good (sacrificed): the one shown faints. */ + internal fun consume(level: ServerLevel) { + faintDisplay(level) + clear() + } + + /** The shown Pokémon faints and goes; the next one shows once it's gone (a rebirth's new form). */ + internal fun faintDisplay(level: ServerLevel) { + display?.takeIf { it.isAlive }?.let { MachineDisplay.faint(level, it) } + display = null + showFrom = level.gameTime + MachineDisplay.FAINT_TICKS + } private fun clear() { pokemon = null @@ -198,8 +210,6 @@ class MachineBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Machine setChanged() } - internal fun respawnDisplay() = removeDisplay() - private fun removeDisplay() { display?.discard() display = null diff --git a/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineDisplay.kt b/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineDisplay.kt index 40b14a3..49a6894 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineDisplay.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineDisplay.kt @@ -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() fun register() { NeoForge.EVENT_BUS.addListener(::onJoin) @@ -35,6 +46,23 @@ object MachineDisplay { NeoForge.EVENT_BUS.addListener { if (isDisplay(it.target)) it.isCanceled = true } NeoForge.EVENT_BUS.addListener { if (isDisplay(it.target)) it.isCanceled = true } CobblemonEvents.BATTLE_STARTED_PRE.subscribe(Priority.HIGHEST, ::onBattle) + NeoForge.EVENT_BUS.addListener { 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) diff --git a/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineWork.kt b/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineWork.kt index 1ab6685..c958bf2 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineWork.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/machine/MachineWork.kt @@ -163,7 +163,7 @@ object MachineWork { val rules = def.altar!! val rewards = altarRewards(rules, pokemon) if (core.confirmBy == player.uuid && level.gameTime <= core.confirmUntil) { - core.consume() + core.consume(level) rewards.forEach { give(level, core, player, it.copy()) } CobbleSurvival.LOGGER.info("{} sacrificed {} (Lv {}) at {}", player.gameProfile.name, pokemon.species.name, pokemon.level, core.blockPos) MachineBlockEntity.tell(player, Component.translatableWithFallback("message.cobblesurvival.machine.sacrificed", @@ -180,10 +180,10 @@ object MachineWork { // --- Rebirth Chamber ------------------------------------------------------------------------------------------- private fun rebirth(core: MachineBlockEntity, level: ServerLevel, rules: MachineDefs.RebirthRules, pokemon: Pokemon) { + core.faintDisplay(level) rebirth(pokemon, rules.perfectIvs) core.rebirthAt = 0 core.reborn = true - core.respawnDisplay() core.setChanged() core.owner?.let { level.server.playerList.getPlayer(it) }?.let { MachineBlockEntity.tell(it, Component.translatableWithFallback("message.cobblesurvival.machine.reborn", diff --git a/mod/src/main/kotlin/dev/cobblesurvival/machine/Machines.kt b/mod/src/main/kotlin/dev/cobblesurvival/machine/Machines.kt index 8cea5f0..f29c500 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/machine/Machines.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/machine/Machines.kt @@ -39,7 +39,9 @@ object Machines { }) private fun core(name: String, color: MapColor, sound: SoundType): DeferredBlock = BLOCKS.registerBlock( - name, ::MachineBlock, BlockBehaviour.Properties.of().mapColor(color).strength(3.5f).requiresCorrectToolForDrops().sound(sound), + name, ::MachineBlock, BlockBehaviour.Properties.of().mapColor(color).strength(3.5f).requiresCorrectToolForDrops().sound(sound) + // See-through, so the Pokémon in the chamber behind the core shows (Adrian, 2026-11-02). + .noOcclusion().isViewBlocking { _, _, _ -> false }.isSuffocating { _, _, _ -> false }, ) fun register(bus: IEventBus) { diff --git a/mod/src/main/kotlin/dev/cobblesurvival/selftest/MachineTests.kt b/mod/src/main/kotlin/dev/cobblesurvival/selftest/MachineTests.kt index c82fcbf..df388d0 100644 --- a/mod/src/main/kotlin/dev/cobblesurvival/selftest/MachineTests.kt +++ b/mod/src/main/kotlin/dev/cobblesurvival/selftest/MachineTests.kt @@ -58,8 +58,8 @@ object MachineTests { SelfTestCase("heat_core_cooks_with_hoppers", 800, ::heatCore), SelfTestCase("dynamo_core_powers_cables", 100, ::dynamo), SelfTestCase("synthesis_core_rolls_drops", 100, ::synthesis), - SelfTestCase("altar_core_sacrifice_needs_confirmation", 60, ::altar), - SelfTestCase("rebirth_core_makes_a_shiny_first_stage", 100, ::rebirth), + SelfTestCase("altar_core_sacrifice_needs_confirmation", 120, ::altar), + SelfTestCase("rebirth_core_makes_a_shiny_first_stage", 200, ::rebirth), SelfTestCase("machine_preview_shows_the_shell", 300, ::preview), SelfTestCase("machine_showcase_builds_every_machine", 100, ::showcase), ) @@ -348,6 +348,13 @@ object MachineTests { s.helper.startSequence() .thenExecute { core.rebirthAt = level.gameTime } .thenWaitUntil { s.check(core.reborn) { "the rebirth didn't finish" } } + .thenWaitUntil { + // The old form faints first; then the chamber shows the new one. + val shown = displays(level, core.blockPos) + s.check(shown.size == 1 && shown.single().pokemon.species.name == "Charmander") { + "after the rebirth the chamber shows ${shown.map { it.pokemon.species.name }}, not one Charmander" + } + } .thenExecute { core.use(player) val back = Cobblemon.storage.getParty(player).firstOrNull { it.uuid == pokemon.uuid } diff --git a/mod/src/main/resources/assets/cobblesurvival/models/block/altar_core.json b/mod/src/main/resources/assets/cobblesurvival/models/block/altar_core.json index 4477e67..4b96625 100644 --- a/mod/src/main/resources/assets/cobblesurvival/models/block/altar_core.json +++ b/mod/src/main/resources/assets/cobblesurvival/models/block/altar_core.json @@ -1,5 +1,6 @@ { "parent": "minecraft:block/orientable", + "render_type": "minecraft:translucent", "textures": { "top": "cobblesurvival:block/altar_core_top", "front": "cobblesurvival:block/altar_core_front", diff --git a/mod/src/main/resources/assets/cobblesurvival/models/block/dynamo_core.json b/mod/src/main/resources/assets/cobblesurvival/models/block/dynamo_core.json index b11dfe5..1e3a558 100644 --- a/mod/src/main/resources/assets/cobblesurvival/models/block/dynamo_core.json +++ b/mod/src/main/resources/assets/cobblesurvival/models/block/dynamo_core.json @@ -1,5 +1,6 @@ { "parent": "minecraft:block/orientable", + "render_type": "minecraft:translucent", "textures": { "top": "cobblesurvival:block/dynamo_core_top", "front": "cobblesurvival:block/dynamo_core_front", diff --git a/mod/src/main/resources/assets/cobblesurvival/models/block/heat_core.json b/mod/src/main/resources/assets/cobblesurvival/models/block/heat_core.json index bc2c408..964793e 100644 --- a/mod/src/main/resources/assets/cobblesurvival/models/block/heat_core.json +++ b/mod/src/main/resources/assets/cobblesurvival/models/block/heat_core.json @@ -1,5 +1,6 @@ { "parent": "minecraft:block/orientable", + "render_type": "minecraft:translucent", "textures": { "top": "cobblesurvival:block/heat_core_top", "front": "cobblesurvival:block/heat_core_front", diff --git a/mod/src/main/resources/assets/cobblesurvival/models/block/rebirth_core.json b/mod/src/main/resources/assets/cobblesurvival/models/block/rebirth_core.json index 7f8a329..3ba5d62 100644 --- a/mod/src/main/resources/assets/cobblesurvival/models/block/rebirth_core.json +++ b/mod/src/main/resources/assets/cobblesurvival/models/block/rebirth_core.json @@ -1,5 +1,6 @@ { "parent": "minecraft:block/orientable", + "render_type": "minecraft:translucent", "textures": { "top": "cobblesurvival:block/rebirth_core_top", "front": "cobblesurvival:block/rebirth_core_front", diff --git a/mod/src/main/resources/assets/cobblesurvival/models/block/synthesis_core.json b/mod/src/main/resources/assets/cobblesurvival/models/block/synthesis_core.json index 2619560..4f4baaf 100644 --- a/mod/src/main/resources/assets/cobblesurvival/models/block/synthesis_core.json +++ b/mod/src/main/resources/assets/cobblesurvival/models/block/synthesis_core.json @@ -1,5 +1,6 @@ { "parent": "minecraft:block/orientable", + "render_type": "minecraft:translucent", "textures": { "top": "cobblesurvival:block/synthesis_core_top", "front": "cobblesurvival:block/synthesis_core_front", diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/altar_core_front.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/altar_core_front.png index 39b1efea5deefecd036a9676c84dec7b16de5e83..18bfb74f8c20e6ff473ef6e26b90d709f58d6bab 100644 GIT binary patch delta 296 zcmeBUI>aF(dZ?5h6#Kz=JzhW>N=iD$R&~n3gZYBfI*&rXv6DmhIF~IP!AA zjuRJ*@9-IJWE2!u-?`lPU8ZnJV)6XHvmD=L8mqcrY}2^KFyZShar1AB0u^3w)elQu zu+IO=SI(E78@BEK*pj7jif8VE533hy#5Nw)@_iz6PgarZ`; delta 243 zcmX@a)WgJDBsW8=Z8$`@ph z?b9u~*wNV7==inwpJmLf^8XS0CpR`WHge8Z@ligbP{XjAafw9JWrif)gV_v*Z2~3@ z6BG-WIev;xSM)i-b;7WPGnR2BqUUPcp?`$OWwAc!LXsRv2meyg5cqI z@~`hoN=QiLb;p<_B_t#S)Gn^)o9!0fcVKR+{38hvn32$AaQ1{$BHKddl&wBDLzS=KWik#M@e;M8`2SK&!`Oq+qCw!(1%wG!4NQcX5j8H#ugN_8BOXi99*c7NcP x#OoM#TH=x5=lPG$e0gz`c@s}ULP7!qgKlh8Y_Gz4X9fla22WQ%mvv4FO#pzPO4|Sc delta 232 zcmcc5*upeHrT(<1i(^Q|oTb4Vd7BLcYy^TMPHfUJW>94nWe!-9aKL9`lM(Y0muang z*ff=9fBziju=Ca1YWebS!99n!Jme2yNVxvgVaBe;SaFN|g;P%!EnOldb*_NDQM)~v z<$3O%1*Z2W?+{|xV)WwHzX?;7KSa%p2)E4czddKo`}ck#x)UoCTjLE%H(O8GSJ@Bx!FK<+ q!`-GAF$DA+-g0*R@*5>=@)5jMr~hq9U&6q^z~JfX=d#Wzp$P!x)@Z{3 diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/dynamo_core_front.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/dynamo_core_front.png index 6d1e92c5b9ad734f4271f1e73f3bdca98b2d6727..9ff2804fae4751d5bc9d3062f481f679b1d6efcb 100644 GIT binary patch delta 248 zcmX@hIE86~O8sq57srr_IZK1j@-`Vr)Jj<~aKCW8#JG#WeLc@1n{yWz%k5@h_gm(u ztapL2>Oym!@9#v#gVQHCJosDFzkc@dm9-D~MHmX+zSa7)bALSx!-Igw9^VgnvNG)3 z65Cf}yrYFhX5W^%1wowZSNCn){o`)Jg2&Hu&mEk;En4Y3SBUAXLe-+MhRN=_^RI9p zU&8Fi#vqk?r6?%J!-224aoOwX%KkzuD}KgII_NOD`cJG8Q|FPlpH=Q;TwH$V-FwHQ(^$)H$WCQoU|{fc^>bP0 Hl+XkKrOkpH|2hORrSn)7>T=coX+G6sUm$~FD4-XH|yV3~;8a#@J zSX&jpOO%-z8i=&rIc&f&n>$0E%|Lvj4BHJ8!4k*+QmS*z9%&UZdPyj>8!1Y$Nyz)0 i{^`H)QU(dk6z~JfX=d#Wzp$P!nAwmNH diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/dynamo_core_side.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/dynamo_core_side.png index 5d132fcaf90f058d982272c82085186e21324ecb..bbd757c26e5e4803cc5386baf294d32315b1149a 100644 GIT binary patch delta 172 zcmX@ec!F_)N`1eli(^Q|oa7n<-RJhqtPQP=jg1eFEsajeyjS>A?g#@L8yj0{HCwsF z?5}%g2{JP?AI?7*{k$M^U-g7Ub!KK}W??hW8E!Kie=vkGdl<4TXFMX+be$oIM=^sz z(fI+Jz$a}z=NS`3CnR$S?_x^gJ($H{+4jK4`l?KY0KE delta 165 zcmX@Xc#v^|N_~f?i(^Q|oTHOB@-`@lxX2&z)ael|Ht0DVVcFM&Gq6(?0DHlvl!My$}b3)y|zXt&3U!isVB2nOP@&OZ=Q6RA!&YF@|@{6x;#x8 zqP9j_Gn>CN)~Nj!I?03E@>tC*$2ZRmCQk^xt`W?v88)}EV7{DY`tu3DdFs}FWKlU* W`QCQJMOg+01_n=8KbLh*2~7aWU_s{q diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/dynamo_core_top.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/dynamo_core_top.png index a17a1391c0f97c2574dce3bf72860dae1398d94e..39c43e158a2545a9cd39cc9e79f2600d08d3750f 100644 GIT binary patch delta 194 zcmey#c#m;{O8rt#7srr_ImtB!y3g&ISsPj#8yg=UTN<5`d9U!L+z|#gHa52JR%~t7 z?2>mRB_t#w?yr_KG%zsuBetiMZ?>Cgb=zF)*$O#oGZcJ`H@VGle8CW@D{RJkM8fF; zLlW=7YzD(N0h5LaiUrIZKSif2`kdf8Vc5bM%eb*E!L&if*?I$;l3`nd#Db*r8zx6M zPq=#MGm}Z~dza8I+sSurI}$b*yhO8o&(7srr_IctM=^Byt~v2mYw#C6K4;tB>YHxI_i6SRZ_j1J6x92&@_ zJ-dbJ=AIwSE)Q3!-~CyWZo4}5O}I=0TSMpbo)Y;&+@$IG@N6 zaQepj!*jDl_b*~(Naxi{pI+)KSds2Kr&RaW+l{8X=hfIcuKgUQUb3xLT|sa6at^B` z`E=fkx}wVpuI0QE`aRuIs`rMt_4IpQ(^-rHZydQ*Cv%Tk)Nb+{RpBM(-gf_5ZC7jW d+#gcGlr3HKWp`ai0RsaAgQu&X%Q~loCIBk}U$g)K diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/heat_core_front.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/heat_core_front.png index 085a1837401ee61f209465407392cb284f0fda9b..c538bb236a933c83e74ee5d437060bbd566bc4dd 100644 GIT binary patch delta 230 zcmeyw*u*qJrT(O+i(^Q|oa8&AkH5@sWIAx@z<~n=&o63kGLf(T#Fxat%*@=}`Ov;x z;`iZbPUZ0F=|#)W8|NNa`twZXvbj7wJUl)(RAOW2ojk~+^n_~-n}Rfp@sTSZ;u!+j z5`sItjoTP+h#cC}>@Bxmd;?>N^NbB%H*Po>2r^t|Jt0*n(IgXZ-Llu=fNBBrgU)9s z1oyPfu{*!T(c#BYONsfck#}Ey_L27C{m+_U%EF%DDDL35PyU{lgyQYC>kJXM+*sF3 mNXR|pR}*8)Sf?b;z;LC)O=Y+B)O!pJ3=E#GelF{r5}E+l#9gHT delta 214 zcmZo-`ouUvrGBTUi(^Q|oa8&AkH5@sWIAx@z<~oBUfkf_#GBysEaD``!Iwu%Ycy(d zOu87ujGTCm&)bu{VUl3d37=nw4;(na@H~03A)5W_vR&#eS8aNy9IBe@Yc#N&7kYSCU!^NK7UX~61yBO3DByog^`^-Pi zlqh4_bbZ3h&k+GJ2bvzOsl1?ZIE`bH)sq!1(hTBr+HIdc^y=){oiyiPu&(h$W(LFP X;+ZznAKNf6Ffe$!`njxgN@xNAR*zmK diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/heat_core_side.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/heat_core_side.png index e2da16f0c1e165c93a12c5fe3d7f2f17ac269935..29c05c94fb2fe99ad7055618e4f44519200fd22e 100644 GIT binary patch delta 176 zcmcb~c!qI;O8sO{7srr_ImvfKAAgzO$aLV)fddB$o?q17WFlYvi7$zPnVGq{^PzpY z#P7q=oXTu$Y;24V#h({s+7-{sRA**pW)?Q{oZ&XZ@drZ~vxgzea>gT4P1hNccoZ`j z6rCTi34GGlbDl9lbV4$R@Ghn#-h){TmTeDwJdUU+&0(96#Gz@Uf8dgtR>jc+A2{lL hHXAq;i|%A#a6ai1q8w8EjDdlH!PC{xWt~$(698mjKokG~ delta 189 zcmX@Zc$0C0O8o*)7srr_ImvfKAAgzO$aLV)fddB$o?q1F;S{wrY_sl{>k0taAdBQ<26?hG?6^M+#4F tG%zqQI1-sD@HD-ovOaZ5D=&<U35)HmwL)w4=$3=9kmp00i_>zopr0HHiboB#j- delta 236 zcmcc3*ugYGrT)C9i(^Q|oTb5vLd^yOF%l)NE}?9#4|pf;Y5p{&%WmmkR{j&xS^+*H zN{i$dESmhKC}x>KZ0X6kX?LG!9NQQDkzI>n#NPKtSUYLQwV(YA+tcQX8>i2oXdd96C{+A;;vL6xR%dm%c$WDV1+xD+ vJ*EHup@|ol$}or|-(Oq)TV(dP2fUXiR~PKCS@4*Ffq}u()z4*}Q$iB}Jlkw< diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/rebirth_core_front.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/rebirth_core_front.png index 17a4668effaf66781296f79f5693be7667335dee..28e74b1cbc2d6d70a0d0ab2a6577097b5cadeb6d 100644 GIT binary patch delta 250 zcmaFOIE`t7O8s3=7srr_IZG#PO5OMn`s=;0?Mi|6g9Z_y6hsZ(-9fKkl@D!gxS;|NoS8p1ceUH;&$BzCKspgF&Fy z+3fSy73Uc?n8j|I=%^>j$m97$ZB}yx-b2F+VL7sxJGblJ%;|1`(01!m{xG&VLiE;KtYvz|L5p=U1lt%W+pJrmj_ z()gyA`>!}s(pc@J>il`hdW=UpT zCizTuMt4G;n;8q+gGP?P9$VWR608UQ_Pyin3RZf&E;IjMu&(h$W`?3uE8OQLo(o`L PU|{fc^>bP0l+XkK^te?_ diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/rebirth_core_side.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/rebirth_core_side.png index ea2c469da7c49566e1e7ebbd11b5fedf770aa117..43359e26fb7fd373df7eb7df2fc56b77a7e214a3 100644 GIT binary patch delta 173 zcmX@ac#?5~O8o>+7srr_ImvfS-=3{!<6_`uW@bLzsXYC}rJ5&q*g6<_czAfqo?Uup z@J&A~QJIa6jcwcg{PPbl)x3Cec`iuO=Y~q6R3h6$<_KPeqXKi-1hP5em^%zx7BIH( zG)g<1SigfO(J9QKPtoTESBELfYQ`rLO~#6XlRFBe9J&v7n=WoKQ*AhwxxL8xi>O@r4xS-UhQ`>!igXlG^1=VRuueS6&C+eXpJ zs~AEqr@rO6v*YxGx0}~ba)~-&`0O%IlKJnMKi2GUn)&@$KV`V$Ntyrv diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/rebirth_core_top.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/rebirth_core_top.png index d8e6415399e29df8cd3c34a8ccca908ee21b21b9..71611d342f5689d364fde3091f0e9f803389ca05 100644 GIT binary patch delta 199 zcmeyz_?U5mO8qKN7srr_ImvfS-=3{!<6_`uW@bLzsXYC}rJ5&q*g6<_czAfWRzBll z_qQudPe@4kaa1ek$bka~4t)Fl)-p%x?b*~aGk>d-Adv$s!Y!F@*EozhWEEDY9$MBQ z;mjfButu?hSK*jI6Pth$OCE#b{zpbFoRN%{Z3YXq6|M<5tYMpw%yG>@<(L2u^P97& zWkT5;o1!Y>8kW7CyTWUJ($=pRJXgTe~DWM4f D4{%NE delta 226 zcmaFN_>XadO8rq!7srr_IctNp#SR$=%=z-vMl?|L^+ro3b%#U)4j#7F70);}hOA%F z^}@2MU8#aI)9%7PqpZ(qn`WN8EgEa>cY}eG!Iq!%VnFXa_6?>AOS@yHzJA5xy<^G2 zS*IBkc9qS^`|hpz$UlU^!Rv8_g?ePN#k?usw(3My+pXLDIj=ri%UUD#Ut8O&e&%gy z7gd)hXV>TorgXO&dc2H`+4k(N!nR}AQjDMf^LoazP}#h5>CYQB?1yxNXK0rQ?3eje jCw{Fb@5Eo#L)@lciv8>3v-}wt7#KWV{an^LB{Ts5%H?C! diff --git a/mod/src/main/resources/assets/cobblesurvival/textures/block/synthesis_core_front.png b/mod/src/main/resources/assets/cobblesurvival/textures/block/synthesis_core_front.png index 1dd73342babff3306697df9d8b037ff16f6444f3..62b2a309dde644ba0d03ee0724efb221d4bc9b9f 100644 GIT binary patch delta 233 zcmaFI*vd3PrT&bki(^Q|oTZZ#`41Tgw92QXtz6^YFlTn0k>%<2F8%_O9&IxT=FOWl z-(aUtlS1se>t(71tKQ!I{qAYi?$>+kANHSMI3V$Pa>a$e>1H%rP{PuhWM+Wg+M;iv+b$+2jj~J4i9*Lx|-Z)t-xc{;h zyTg7T`>o6m%DGNvIQ1>sYPR#lGf^w{2fTrOi@r{IGk4e7q@%uZ)1zlglsfGG(Ab(( qnovHwt+BDigPHB_aw*rxyzW(@R;`O9f*BYX7(8A5T-G@yGywo8_++90 delta 210 zcmZo=ddE0HrGBfYi(^Q|oVAk=@*Xnaajh3LE@524C+OO9P@<LCEbo0{x%8irKBr*^2-(ouOv?fY+!%6Aq-{Y^k2(&k@T(SKyGhdP1 z43)+-whw%Phr~G-S$ZiOau#jA|I^4t(Svc?O|C%BEt6l@O;qsidLP5`q}F%BG>I7x z4B{8_F*dBrKdQvtbf9Hje(Ob}ZrLY_=O1rfam`wQW&X7lX%d;gZl^~r7TZ?qgzRR@5uYGp^LybNw z4=>G~&xH1CPS`DPxkF~%_74yHWf)zStgP&N$j)HXKHWdPe%BnvW8X`z&-(3d&a~rB zYL?H1xJ?eci*uA$; k;Y&$sLf^qpw{rHmJofn_#%*vTCkzmY?Jq^m{A10jBOPwsS{8;Z% z)7=7zg2T@U0t~<{*R$#!&bAEHG8k*Gd!rPSTK7Ji^lcevJI8< z@)ssWm!wtSVOVhI($v3;nx8#R57kg`T+|@)lhb2@hJdr1LfN%^hmKFonjN#)mv{(x zI5th!a1?WhE^+Jnar2`-Pkwb%gwCuQ#uv{vpO&Z#ef5P+HsY3#*vhR83=9mOu6{1- HoD!MZrO8q`h7srr_IctMY@*Y+YkkRq-5Y$YF=Lno6x^#j@k#613olbi+6kM0E zKNozs#!@Ky+#a#B8_q0PuRGF-9v zXmcljZHN6u#)gf2f1gIBoSj&ad^hOzw5^v5;^xl(yZG&bsXwJdI$k_hsJK_6`LgNx z(rW@YixT;g?`q|J_>(swntR^rnc4MAvzcCM_$FoB@ttq