From eb0c3fea8981246f43c03bc3b9d1b1f35871bfb8 Mon Sep 17 00:00:00 2001 From: CoderJoe Date: Thu, 12 Mar 2026 20:57:33 -0500 Subject: [PATCH 1/2] Add multi power cable block with hub-style power distribution Pulls power from behind (opposite of facing) and pushes 1 power per tick to each of the 5 other adjacent faces. Max storage 10, 12 visual variants (6 directional x 2 powered states), crafted with 4 copper ingots. --- src/main/kotlin/com/coderjoe/atlas/Atlas.kt | 3 +- .../coderjoe/atlas/power/PowerBlockDialog.kt | 4 + .../atlas/power/block/MultiPowerCable.kt | 91 +++++ .../resources/nexo/items/atlas_blocks.yml | 385 ++++++++++++++++++ .../textures/block/multi_power_cable_back.png | Bin 0 -> 90 bytes .../block/multi_power_cable_back_powered.png | Bin 0 -> 90 bytes .../textures/block/multi_power_cable_cap.png | Bin 0 -> 91 bytes .../block/multi_power_cable_cap_powered.png | Bin 0 -> 91 bytes .../block/multi_power_cable_front.png | Bin 0 -> 90 bytes .../block/multi_power_cable_front_powered.png | Bin 0 -> 90 bytes .../textures/block/multi_power_cable_side.png | Bin 0 -> 91 bytes .../block/multi_power_cable_side_powered.png | Bin 0 -> 90 bytes .../com/coderjoe/atlas/AtlasPluginTest.kt | 2 +- .../kotlin/com/coderjoe/atlas/TestHelper.kt | 4 +- .../atlas/power/MultiPowerCableTest.kt | 207 ++++++++++ .../atlas/power/PowerBlockInitializerTest.kt | 4 +- 16 files changed, 695 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/com/coderjoe/atlas/power/block/MultiPowerCable.kt create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_back.png create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_back_powered.png create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_cap.png create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_cap_powered.png create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_front.png create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_front_powered.png create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_side.png create mode 100644 src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_side_powered.png create mode 100644 src/test/kotlin/com/coderjoe/atlas/power/MultiPowerCableTest.kt diff --git a/src/main/kotlin/com/coderjoe/atlas/Atlas.kt b/src/main/kotlin/com/coderjoe/atlas/Atlas.kt index 44f1d83..50507be 100644 --- a/src/main/kotlin/com/coderjoe/atlas/Atlas.kt +++ b/src/main/kotlin/com/coderjoe/atlas/Atlas.kt @@ -180,7 +180,8 @@ class Atlas : JavaPlugin() { com.coderjoe.atlas.power.block.SmallBattery.descriptor, com.coderjoe.atlas.power.block.PowerCable.descriptor, com.coderjoe.atlas.power.block.LavaGenerator.descriptor, - com.coderjoe.atlas.power.block.AutoSmelter.descriptor + com.coderjoe.atlas.power.block.AutoSmelter.descriptor, + com.coderjoe.atlas.power.block.MultiPowerCable.descriptor ).associateBy { it.baseBlockId } } diff --git a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt b/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt index bb62079..5459e8b 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt @@ -7,6 +7,7 @@ import com.coderjoe.atlas.power.block.LavaGenerator import com.coderjoe.atlas.power.block.PowerCable import com.coderjoe.atlas.power.block.SmallBattery import com.coderjoe.atlas.power.block.SmallDrill +import com.coderjoe.atlas.power.block.MultiPowerCable import com.coderjoe.atlas.power.block.SmallSolarPanel import io.papermc.paper.dialog.Dialog import io.papermc.paper.registry.data.dialog.ActionButton @@ -127,6 +128,7 @@ object PowerBlockDialog { is PowerCable -> "Power Cable (${powerBlock.facing.name.lowercase().replaceFirstChar { it.uppercase() }})" is LavaGenerator -> "Lava Generator" is AutoSmelter -> "Auto Smelter (${powerBlock.facing.name.lowercase().replaceFirstChar { it.uppercase() }})" + is MultiPowerCable -> "Multi Power Cable (${powerBlock.facing.name.lowercase().replaceFirstChar { it.uppercase() }})" else -> "Power Block" } @@ -173,6 +175,8 @@ object PowerBlockDialog { .color(NamedTextColor.GRAY) is AutoSmelter -> Component.text("Machine - smelts items passing through, consumes ${AutoSmelter.POWER_PER_SMELT} power/item") .color(NamedTextColor.GRAY) + is MultiPowerCable -> Component.text("Cable - distributes power to all adjacent faces") + .color(NamedTextColor.GRAY) else -> Component.text("Power block") .color(NamedTextColor.GRAY) } diff --git a/src/main/kotlin/com/coderjoe/atlas/power/block/MultiPowerCable.kt b/src/main/kotlin/com/coderjoe/atlas/power/block/MultiPowerCable.kt new file mode 100644 index 0000000..db4cc5f --- /dev/null +++ b/src/main/kotlin/com/coderjoe/atlas/power/block/MultiPowerCable.kt @@ -0,0 +1,91 @@ +package com.coderjoe.atlas.power.block + +import com.coderjoe.atlas.atlasInfo +import com.coderjoe.atlas.core.BlockDescriptor +import com.coderjoe.atlas.core.PlacementType +import com.coderjoe.atlas.power.PowerBlock +import com.coderjoe.atlas.power.PowerBlockRegistry +import org.bukkit.Location +import org.bukkit.block.BlockFace + +class MultiPowerCable(location: Location, override val facing: BlockFace) : PowerBlock(location, maxStorage = 10) { + + companion object { + const val BLOCK_ID = "multi_power_cable" + + val DIRECTIONAL_IDS = mapOf( + BlockFace.NORTH to "multi_power_cable_north", + BlockFace.SOUTH to "multi_power_cable_south", + BlockFace.EAST to "multi_power_cable_east", + BlockFace.WEST to "multi_power_cable_west", + BlockFace.UP to "multi_power_cable_up", + BlockFace.DOWN to "multi_power_cable_down" + ) + + val POWERED_IDS = mapOf( + BlockFace.NORTH to "multi_power_cable_north_powered", + BlockFace.SOUTH to "multi_power_cable_south_powered", + BlockFace.EAST to "multi_power_cable_east_powered", + BlockFace.WEST to "multi_power_cable_west_powered", + BlockFace.UP to "multi_power_cable_up_powered", + BlockFace.DOWN to "multi_power_cable_down_powered" + ) + + val ID_TO_FACING = (DIRECTIONAL_IDS.entries.associate { (face, id) -> id to face } + + POWERED_IDS.entries.associate { (face, id) -> id to face }) + + fun facingFromBlockId(blockId: String): BlockFace? = ID_TO_FACING[blockId] + + val descriptor = BlockDescriptor( + baseBlockId = BLOCK_ID, + displayName = "Multi Power Cable", + description = "Cable - distributes power to all adjacent faces", + placementType = PlacementType.DIRECTIONAL, + directionalVariants = DIRECTIONAL_IDS, + allRegistrableIds = DIRECTIONAL_IDS.values.toList() + POWERED_IDS.values.toList(), + constructor = { loc, facing -> MultiPowerCable(loc, facing) } + ) + } + + override val baseBlockId: String = BLOCK_ID + + override val updateIntervalTicks: Long = 20L // 1 second + + override fun getVisualStateBlockId(): String = + if (currentPower > 0) POWERED_IDS[facing]!! + else DIRECTIONAL_IDS[facing]!! + + override fun powerUpdate() { + val registry = PowerBlockRegistry.instance ?: return + + // Pull power from behind (opposite of facing direction) + val source = registry.getAdjacentPowerBlock(location, facing.oppositeFace) + + if (source != null && canAcceptPower() && source.hasPower()) { + val remaining = maxStorage - currentPower + val pulled = source.removePower(minOf(remaining, source.currentPower)) + if (pulled > 0) { + addPower(pulled) + plugin.logger.atlasInfo("MultiPowerCable at ${location.blockX},${location.blockY},${location.blockZ} pulled $pulled power (now $currentPower/$maxStorage)") + } + } + + // Push 1 power to each adjacent power block on the other 5 faces + if (!hasPower()) return + + val outputFaces = listOf(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN) + .filter { it != facing.oppositeFace } + + for (face in outputFaces) { + if (!hasPower()) break + val target = registry.getAdjacentPowerBlock(location, face) ?: continue + if (target.canAcceptPower()) { + val pushed = removePower(1) + if (pushed > 0) { + target.addPower(pushed) + plugin.logger.atlasInfo("MultiPowerCable at ${location.blockX},${location.blockY},${location.blockZ} pushed $pushed power to ${target::class.simpleName} at ${face.name}") + } + } + } + } +} diff --git a/src/main/resources/nexo/items/atlas_blocks.yml b/src/main/resources/nexo/items/atlas_blocks.yml index 92e7257..78cd2bf 100644 --- a/src/main/resources/nexo/items/atlas_blocks.yml +++ b/src/main/resources/nexo/items/atlas_blocks.yml @@ -3130,3 +3130,388 @@ auto_smelter_west_on: loots: - nexo_item: auto_smelter probability: 1.0 + +multi_power_cable: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube_all + textures: + all: atlas:block/multi_power_cable_side + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 154 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_north: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_front + south: atlas:block/multi_power_cable_back + east: atlas:block/multi_power_cable_side + west: atlas:block/multi_power_cable_side + up: atlas:block/multi_power_cable_cap + down: atlas:block/multi_power_cable_cap + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 155 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_south: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_back + south: atlas:block/multi_power_cable_front + east: atlas:block/multi_power_cable_side + west: atlas:block/multi_power_cable_side + up: atlas:block/multi_power_cable_cap + down: atlas:block/multi_power_cable_cap + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 156 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_east: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side + south: atlas:block/multi_power_cable_side + east: atlas:block/multi_power_cable_front + west: atlas:block/multi_power_cable_back + up: atlas:block/multi_power_cable_cap + down: atlas:block/multi_power_cable_cap + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 157 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_west: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side + south: atlas:block/multi_power_cable_side + east: atlas:block/multi_power_cable_back + west: atlas:block/multi_power_cable_front + up: atlas:block/multi_power_cable_cap + down: atlas:block/multi_power_cable_cap + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 158 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_up: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side + south: atlas:block/multi_power_cable_side + east: atlas:block/multi_power_cable_side + west: atlas:block/multi_power_cable_side + up: atlas:block/multi_power_cable_front + down: atlas:block/multi_power_cable_back + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 159 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_down: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side + south: atlas:block/multi_power_cable_side + east: atlas:block/multi_power_cable_side + west: atlas:block/multi_power_cable_side + up: atlas:block/multi_power_cable_back + down: atlas:block/multi_power_cable_front + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 160 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_north_powered: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_front_powered + south: atlas:block/multi_power_cable_back_powered + east: atlas:block/multi_power_cable_side_powered + west: atlas:block/multi_power_cable_side_powered + up: atlas:block/multi_power_cable_cap_powered + down: atlas:block/multi_power_cable_cap_powered + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 161 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_south_powered: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_back_powered + south: atlas:block/multi_power_cable_front_powered + east: atlas:block/multi_power_cable_side_powered + west: atlas:block/multi_power_cable_side_powered + up: atlas:block/multi_power_cable_cap_powered + down: atlas:block/multi_power_cable_cap_powered + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 162 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_east_powered: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side_powered + south: atlas:block/multi_power_cable_side_powered + east: atlas:block/multi_power_cable_front_powered + west: atlas:block/multi_power_cable_back_powered + up: atlas:block/multi_power_cable_cap_powered + down: atlas:block/multi_power_cable_cap_powered + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 163 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_west_powered: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side_powered + south: atlas:block/multi_power_cable_side_powered + east: atlas:block/multi_power_cable_back_powered + west: atlas:block/multi_power_cable_front_powered + up: atlas:block/multi_power_cable_cap_powered + down: atlas:block/multi_power_cable_cap_powered + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 164 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_up_powered: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side_powered + south: atlas:block/multi_power_cable_side_powered + east: atlas:block/multi_power_cable_side_powered + west: atlas:block/multi_power_cable_side_powered + up: atlas:block/multi_power_cable_front_powered + down: atlas:block/multi_power_cable_back_powered + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 165 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.hit + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 + +multi_power_cable_down_powered: + itemname: "Multi Power Cable" + material: paper + Pack: + generate_model: true + parent_model: block/cube + textures: + north: atlas:block/multi_power_cable_side_powered + south: atlas:block/multi_power_cable_side_powered + east: atlas:block/multi_power_cable_side_powered + west: atlas:block/multi_power_cable_side_powered + up: atlas:block/multi_power_cable_back_powered + down: atlas:block/multi_power_cable_front_powered + Mechanics: + custom_block: + type: NOTEBLOCK + custom_variation: 166 + hardness: 2 + block_sounds: + break_sound: block.metal.break + place_sound: block.metal.place + hit_sound: block.metal.hit + step_sound: block.metal.step + fall_sound: block.metal.fall + drop: + silktouch: false + loots: + - nexo_item: multi_power_cable + probability: 1.0 diff --git a/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_back.png b/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8ee99659cb826b7826ab2ef44bf1ab0ff233c41c GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|ik>cxAr*6y|J*<9Ae5lwYk7DD n+adgTe~DWM4fiGLTd literal 0 HcmV?d00001 diff --git a/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_back_powered.png b/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_back_powered.png new file mode 100644 index 0000000000000000000000000000000000000000..2a455b8ee58d68fbd4197bad6b0679691dd6d3ee GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|ik>cxAr*6y|LmXAsB*w%R#96Z nuM4BvqbZ69R8oRng)2x2^vR%=%TRLtP%>gTe~DWM4fo{|^x literal 0 HcmV?d00001 diff --git a/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_cap.png b/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_cap.png new file mode 100644 index 0000000000000000000000000000000000000000..6036b663068385f95939e817479537a45d0bddd3 GIT binary patch literal 91 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|N}eu`Ar*6y?_4}~)`2TQ$i_@% literal 0 HcmV?d00001 diff --git a/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_cap_powered.png b/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_cap_powered.png new file mode 100644 index 0000000000000000000000000000000000000000..006e3d8559fccf3d91cad390d4b5ea87bbf9e4ad GIT binary patch literal 91 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|N}eu`Ar*6y_iPPmY!o@*GAXMq okoO3qn$p#S2UoBqF`SfQ&^XSxKVMPn08ll9r>mdKI;Vst0J*Oivj6}9 literal 0 HcmV?d00001 diff --git a/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_front.png b/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_front.png new file mode 100644 index 0000000000000000000000000000000000000000..cdfc96f5e2c322e2c02384173d0cd2970d3a6508 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|ik>cxAr*6y|J2WIR5{=>tEerI n*M(8-(GcxAr*6y|NNidsB*w%R#96Z nuM4BvqbZ69R8oRnhl_GOZ@Ny~`=DrWF>^>bP0l+XkKpG+5h literal 0 HcmV?d00001 diff --git a/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_side.png b/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_side.png new file mode 100644 index 0000000000000000000000000000000000000000..006e3d8559fccf3d91cad390d4b5ea87bbf9e4ad GIT binary patch literal 91 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|N}eu`Ar*6y_iPPmY!o@*GAXMq okoO3qn$p#S2UoBqF`SfQ&^XSxKVMPn08ll9r>mdKI;Vst0J*Oivj6}9 literal 0 HcmV?d00001 diff --git a/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_side_powered.png b/src/main/resources/nexo/pack/assets/atlas/textures/block/multi_power_cable_side_powered.png new file mode 100644 index 0000000000000000000000000000000000000000..cdfc96f5e2c322e2c02384173d0cd2970d3a6508 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|ik>cxAr*6y|J2WIR5{=>tEerI n*M(8-(G 0, "Cable should have pulled power") + assertTrue(battery.currentPower < 5, "Battery should have less power") + } + + @Test + fun `distributes power to multiple outputs`() { + val registry = PowerBlockRegistry(TestHelper.mockPlugin) + val cableLoc = TestHelper.createLocation(0.0, 64.0, 0.0) + val cable = MultiPowerCable(cableLoc, BlockFace.NORTH) + cable.currentPower = 5 + TestHelper.addToRegistry(registry, cable, "multi_power_cable_north") + + // Place batteries on output faces (not behind/south) + val eastBatteryLoc = TestHelper.createLocation(1.0, 64.0, 0.0) + val eastBattery = SmallBattery(eastBatteryLoc, BlockFace.WEST) + TestHelper.addToRegistry(registry, eastBattery, "small_battery") + + val westBatteryLoc = TestHelper.createLocation(-1.0, 64.0, 0.0) + val westBattery = SmallBattery(westBatteryLoc, BlockFace.EAST) + TestHelper.addToRegistry(registry, westBattery, "small_battery_east") + + val northBatteryLoc = TestHelper.createLocation(0.0, 64.0, -1.0) + val northBattery = SmallBattery(northBatteryLoc, BlockFace.SOUTH) + TestHelper.addToRegistry(registry, northBattery, "small_battery_south") + + cable.callPowerUpdate() + + assertTrue(eastBattery.currentPower > 0, "East battery should have received power") + assertTrue(westBattery.currentPower > 0, "West battery should have received power") + assertTrue(northBattery.currentPower > 0, "North battery should have received power") + assertEquals(2, cable.currentPower, "Cable should have distributed 3 power (1 to each)") + } + + @Test + fun `does not exceed max storage when pulling power`() { + val registry = PowerBlockRegistry(TestHelper.mockPlugin) + val cableLoc = TestHelper.createLocation(0.0, 64.0, 0.0) + val cable = MultiPowerCable(cableLoc, BlockFace.NORTH) + cable.currentPower = 10 // Already full + TestHelper.addToRegistry(registry, cable, "multi_power_cable_north") + + val batteryLoc = TestHelper.createLocation(0.0, 64.0, 1.0) + val battery = SmallBattery(batteryLoc, BlockFace.NORTH) + battery.currentPower = 5 + TestHelper.addToRegistry(registry, battery, "small_battery") + + cable.callPowerUpdate() + + assertEquals(10, cable.maxStorage) + assertEquals(5, battery.currentPower, "Battery should not lose power when cable is full") + } + + @Test + fun `does not crash with no adjacent blocks`() { + PowerBlockRegistry(TestHelper.mockPlugin) + val cable = MultiPowerCable(TestHelper.createLocation(), BlockFace.NORTH) + + assertDoesNotThrow { + cable.callPowerUpdate() + } + } +} diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt index 981aa33..e2f5b7c 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt @@ -26,8 +26,8 @@ class PowerBlockRegistrationTest { TestHelper.initPowerFactory() val ids = PowerBlockFactory.getRegisteredBlockIds() - // 1 solar + 6 drill + 4 battery + 6 cable + 2 lava generator + 8 auto smelter = 27 - assertEquals(27, ids.size) + // 1 solar + 6 drill + 4 battery + 6 cable + 2 lava generator + 8 auto smelter + 12 multi power cable = 39 + assertEquals(39, ids.size) } @Test From 332483d2976f50b25ff1dde2cb7fa4bebf4451ab Mon Sep 17 00:00:00 2001 From: CoderJoe Date: Thu, 12 Mar 2026 20:57:38 -0500 Subject: [PATCH 2/2] Fix shapeless recipes to use individual ingredient entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove invalid 'amount' field from ingredients — shapeless recipes require each item listed as a separate lettered entry (A, B, C, etc.). --- .../nexo/recipes/shapeless/atlas_recipes.yml | 125 +++++++++++++----- 1 file changed, 93 insertions(+), 32 deletions(-) diff --git a/src/main/resources/nexo/recipes/shapeless/atlas_recipes.yml b/src/main/resources/nexo/recipes/shapeless/atlas_recipes.yml index 2123a03..c4c2763 100644 --- a/src/main/resources/nexo/recipes/shapeless/atlas_recipes.yml +++ b/src/main/resources/nexo/recipes/shapeless/atlas_recipes.yml @@ -6,13 +6,22 @@ small_solar_panel_recipe: amount: 1 ingredients: A: - amount: 3 minecraft_type: IRON_INGOT B: - amount: 3 - minecraft_type: LAPIS_LAZULI + minecraft_type: IRON_INGOT C: - amount: 3 + minecraft_type: IRON_INGOT + D: + minecraft_type: LAPIS_LAZULI + E: + minecraft_type: LAPIS_LAZULI + F: + minecraft_type: LAPIS_LAZULI + G: + minecraft_type: REDSTONE + H: + minecraft_type: REDSTONE + I: minecraft_type: REDSTONE power_cable_recipe: @@ -21,7 +30,6 @@ power_cable_recipe: amount: 1 ingredients: A: - amount: 1 minecraft_type: COPPER_INGOT small_drill_recipe: @@ -30,13 +38,18 @@ small_drill_recipe: amount: 1 ingredients: A: - amount: 3 minecraft_type: IRON_INGOT B: - amount: 3 - minecraft_type: REDSTONE + minecraft_type: IRON_INGOT C: - amount: 1 + minecraft_type: IRON_INGOT + D: + minecraft_type: REDSTONE + E: + minecraft_type: REDSTONE + F: + minecraft_type: REDSTONE + G: minecraft_type: DIAMOND small_battery_recipe: @@ -45,13 +58,18 @@ small_battery_recipe: amount: 1 ingredients: A: - amount: 3 minecraft_type: IRON_INGOT B: - amount: 3 - minecraft_type: REDSTONE + minecraft_type: IRON_INGOT C: - amount: 1 + minecraft_type: IRON_INGOT + D: + minecraft_type: REDSTONE + E: + minecraft_type: REDSTONE + F: + minecraft_type: REDSTONE + G: minecraft_type: COPPER_INGOT fluid_pump_recipe: @@ -60,13 +78,18 @@ fluid_pump_recipe: amount: 1 ingredients: A: - amount: 3 minecraft_type: IRON_INGOT B: - amount: 3 - minecraft_type: REDSTONE + minecraft_type: IRON_INGOT C: - amount: 1 + minecraft_type: IRON_INGOT + D: + minecraft_type: REDSTONE + E: + minecraft_type: REDSTONE + F: + minecraft_type: REDSTONE + G: minecraft_type: BUCKET fluid_pipe_recipe: @@ -75,7 +98,6 @@ fluid_pipe_recipe: amount: 1 ingredients: A: - amount: 1 minecraft_type: IRON_INGOT fluid_container_recipe: @@ -84,13 +106,22 @@ fluid_container_recipe: amount: 1 ingredients: A: - amount: 4 minecraft_type: IRON_INGOT B: - amount: 4 - minecraft_type: GLASS + minecraft_type: IRON_INGOT C: - amount: 1 + minecraft_type: IRON_INGOT + D: + minecraft_type: IRON_INGOT + E: + minecraft_type: GLASS + F: + minecraft_type: GLASS + G: + minecraft_type: GLASS + H: + minecraft_type: GLASS + I: minecraft_type: BUCKET lava_generator_recipe: @@ -99,13 +130,18 @@ lava_generator_recipe: amount: 1 ingredients: A: - amount: 3 minecraft_type: IRON_INGOT B: - amount: 3 - minecraft_type: REDSTONE + minecraft_type: IRON_INGOT C: - amount: 1 + minecraft_type: IRON_INGOT + D: + minecraft_type: REDSTONE + E: + minecraft_type: REDSTONE + F: + minecraft_type: REDSTONE + G: minecraft_type: MAGMA_BLOCK conveyor_belt_recipe: @@ -114,23 +150,48 @@ conveyor_belt_recipe: amount: 4 ingredients: A: - amount: 3 minecraft_type: IRON_INGOT B: - amount: 3 + minecraft_type: IRON_INGOT + C: + minecraft_type: IRON_INGOT + D: + minecraft_type: REDSTONE + E: + minecraft_type: REDSTONE + F: minecraft_type: REDSTONE +multi_power_cable_recipe: + result: + nexo_item: multi_power_cable + amount: 1 + ingredients: + A: + minecraft_type: COPPER_INGOT + B: + minecraft_type: COPPER_INGOT + C: + minecraft_type: COPPER_INGOT + D: + minecraft_type: COPPER_INGOT + auto_smelter_recipe: result: nexo_item: auto_smelter amount: 1 ingredients: A: - amount: 3 minecraft_type: IRON_INGOT B: - amount: 3 - minecraft_type: REDSTONE + minecraft_type: IRON_INGOT C: - amount: 1 + minecraft_type: IRON_INGOT + D: + minecraft_type: REDSTONE + E: + minecraft_type: REDSTONE + F: + minecraft_type: REDSTONE + G: minecraft_type: FURNACE