diff --git a/src/main/kotlin/com/coderjoe/atlas/Atlas.kt b/src/main/kotlin/com/coderjoe/atlas/Atlas.kt index e1a31ee..1240b1f 100644 --- a/src/main/kotlin/com/coderjoe/atlas/Atlas.kt +++ b/src/main/kotlin/com/coderjoe/atlas/Atlas.kt @@ -34,8 +34,10 @@ import com.coderjoe.atlas.transport.TransportBlockRegistry import com.coderjoe.atlas.transport.block.ConveyorBelt import com.coderjoe.atlas.utility.block.AutoSmelter import com.coderjoe.atlas.utility.block.CobblestoneFactory +import com.coderjoe.atlas.utility.block.Crusher import com.coderjoe.atlas.utility.block.ObsidianFactory import com.coderjoe.atlas.utility.block.SmallDrill +import com.coderjoe.atlas.utility.block.SoftTouchDrill import org.bukkit.plugin.java.JavaPlugin import org.bukkit.scheduler.BukkitTask @@ -209,7 +211,9 @@ class Atlas : JavaPlugin() { PowerSplitter.descriptor, CobblestoneFactory.descriptor, ObsidianFactory.descriptor, + Crusher.descriptor, PowerMerger.descriptor, + SoftTouchDrill.descriptor, ).associateBy { it.baseBlockId } } diff --git a/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidContainer.kt b/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidContainer.kt index 028cab7..4024eda 100644 --- a/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidContainer.kt +++ b/src/main/kotlin/com/coderjoe/atlas/fluid/block/FluidContainer.kt @@ -19,7 +19,7 @@ class FluidContainer(location: Location, override val facing: BlockFace) : Fluid companion object { const val BLOCK_ID = "atlas:fluid_container" - const val MAX_CAPACITY = 10 + const val MAX_CAPACITY = 20 val descriptor = BlockDescriptor( @@ -62,8 +62,8 @@ class FluidContainer(location: Location, override val facing: BlockFace) : Fluid fun getFillLevel(): Int = when (storedAmount) { 0 -> 0 - in 1..3 -> 1 - in 4..7 -> 2 + in 1..6 -> 1 + in 7..13 -> 2 else -> 3 } diff --git a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt b/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt index 1f078aa..6a623dd 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt @@ -11,8 +11,10 @@ import com.coderjoe.atlas.power.block.SmallBattery import com.coderjoe.atlas.power.block.SmallSolarPanel import com.coderjoe.atlas.utility.block.AutoSmelter import com.coderjoe.atlas.utility.block.CobblestoneFactory +import com.coderjoe.atlas.utility.block.Crusher import com.coderjoe.atlas.utility.block.ObsidianFactory import com.coderjoe.atlas.utility.block.SmallDrill +import com.coderjoe.atlas.utility.block.SoftTouchDrill import io.papermc.paper.dialog.Dialog import io.papermc.paper.registry.data.dialog.ActionButton import io.papermc.paper.registry.data.dialog.DialogBase @@ -74,7 +76,7 @@ object PowerBlockDialog { drill: SmallDrill, onClose: (Player) -> Unit, ) { - val title = Component.text("Small Drill") + val title = Component.text(getBlockDisplayName(drill)) val bodyText = buildPowerInfo(drill) val body = DialogBody.plainMessage(bodyText) @@ -124,6 +126,7 @@ object PowerBlockDialog { when (powerBlock) { is SmallSolarPanel -> "Small Solar Panel" is SmallBattery -> "Small Battery" + is SoftTouchDrill -> "Soft Touch Drill" is SmallDrill -> "Small Drill" is PowerCable -> "Power Cable (${powerBlock.facing.displayName()})" is LavaGenerator -> "Lava Generator" @@ -131,6 +134,7 @@ object PowerBlockDialog { is PowerSplitter -> "Power Splitter (${powerBlock.facing.displayName()})" is CobblestoneFactory -> "Cobblestone Factory" is ObsidianFactory -> "Obsidian Factory" + is Crusher -> "Crusher (${powerBlock.facing.displayName()})" is PowerMerger -> "Power Merger (${powerBlock.facing.displayName()})" else -> "Power Block" } @@ -175,6 +179,12 @@ object PowerBlockDialog { is SmallBattery -> Component.text("Storage - holds up to 10 power") .color(NamedTextColor.GRAY) + is SoftTouchDrill -> { + val directionName = powerBlock.miningDirection.displayName() + val status = if (powerBlock.enabled) "ON" else "OFF" + Component.text("Machine - mining $directionName, $status, consumes 50 power/s (silk touch)") + .color(NamedTextColor.GRAY) + } is SmallDrill -> { val directionName = powerBlock.miningDirection.displayName() val status = if (powerBlock.enabled) "ON" else "OFF" @@ -199,6 +209,9 @@ object PowerBlockDialog { is ObsidianFactory -> Component.text("Machine - consumes ${ObsidianFactory.POWER_COST} power + water + lava → obsidian") .color(NamedTextColor.GRAY) + is Crusher -> + Component.text("Machine - crushes ore blocks into 2x ores, consumes ${Crusher.POWER_PER_CRUSH} power/item") + .color(NamedTextColor.GRAY) is PowerMerger -> Component.text("Cable - merges power from all sides, outputs in facing direction") .color(NamedTextColor.GRAY) diff --git a/src/main/kotlin/com/coderjoe/atlas/power/block/LavaGenerator.kt b/src/main/kotlin/com/coderjoe/atlas/power/block/LavaGenerator.kt index 5ea0d47..b356301 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/block/LavaGenerator.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/block/LavaGenerator.kt @@ -11,7 +11,7 @@ import com.coderjoe.atlas.power.PowerBlock import org.bukkit.Location import org.bukkit.block.BlockFace -class LavaGenerator(location: Location) : PowerBlock(location, maxStorage = 50) { +class LavaGenerator(location: Location) : PowerBlock(location, maxStorage = 20) { override val canReceivePower: Boolean = false override val updateIntervalTicks: Long = 20L diff --git a/src/main/kotlin/com/coderjoe/atlas/power/block/SmallBattery.kt b/src/main/kotlin/com/coderjoe/atlas/power/block/SmallBattery.kt index f9715e1..50cf784 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/block/SmallBattery.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/block/SmallBattery.kt @@ -9,7 +9,7 @@ import com.coderjoe.atlas.power.PowerBlockRegistry import org.bukkit.Location import org.bukkit.block.BlockFace -class SmallBattery(location: Location, facing: BlockFace) : PowerBlock(location, maxStorage = 10) { +class SmallBattery(location: Location, facing: BlockFace) : PowerBlock(location, maxStorage = 50) { override val facing: BlockFace = if (facing == BlockFace.SELF) BlockFace.DOWN else facing override val canReceivePower: Boolean = true @@ -25,7 +25,7 @@ class SmallBattery(location: Location, facing: BlockFace) : PowerBlock(location, BlockDescriptor( baseBlockId = BLOCK_ID, displayName = "Small Battery", - description = "Storage - holds up to 10 power", + description = "Storage - holds up to 50 power", placementType = PlacementType.SIMPLE, additionalBlockIds = listOf(BLOCK_ID_LOW, BLOCK_ID_MEDIUM, BLOCK_ID_FULL), constructor = { loc, facing -> SmallBattery(loc, facing) }, @@ -37,8 +37,8 @@ class SmallBattery(location: Location, facing: BlockFace) : PowerBlock(location, private fun chargeLevel(): Int = when (currentPower) { 0 -> 0 - in 1..3 -> 1 - in 4..7 -> 2 + in 1..16 -> 1 + in 17..33 -> 2 else -> 3 } diff --git a/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt b/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt index 448de56..ddb3c5e 100644 --- a/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt +++ b/src/main/kotlin/com/coderjoe/atlas/power/block/SmallSolarPanel.kt @@ -7,9 +7,9 @@ import com.coderjoe.atlas.core.PlacementType import com.coderjoe.atlas.power.PowerBlock import org.bukkit.Location -class SmallSolarPanel(location: Location) : PowerBlock(location, maxStorage = 1) { +class SmallSolarPanel(location: Location) : PowerBlock(location, maxStorage = 4) { override val canReceivePower: Boolean = false - override val updateIntervalTicks: Long = 1200L + override val updateIntervalTicks: Long = 200L companion object { const val BLOCK_ID = "atlas:small_solar_panel" @@ -19,7 +19,7 @@ class SmallSolarPanel(location: Location) : PowerBlock(location, maxStorage = 1) BlockDescriptor( baseBlockId = BLOCK_ID, displayName = "Small Solar Panel", - description = "Generator - produces 1 power/min during daytime", + description = "Generator - produces 2 power/10s during daytime", placementType = PlacementType.SIMPLE, additionalBlockIds = listOf(BLOCK_ID_FULL), constructor = { loc, _ -> SmallSolarPanel(loc) }, @@ -39,7 +39,7 @@ class SmallSolarPanel(location: Location) : PowerBlock(location, maxStorage = 1) val isDaytime = world.time in 0..12000 if (isDaytime) { - val generated = addPower(1) + val generated = addPower(2) if (generated > 0) { plugin.logger.atlasInfo( "SmallSolarPanel at ${location.coordinates} " + diff --git a/src/main/kotlin/com/coderjoe/atlas/utility/block/AutoSmelter.kt b/src/main/kotlin/com/coderjoe/atlas/utility/block/AutoSmelter.kt index afcb419..596186a 100644 --- a/src/main/kotlin/com/coderjoe/atlas/utility/block/AutoSmelter.kt +++ b/src/main/kotlin/com/coderjoe/atlas/utility/block/AutoSmelter.kt @@ -10,7 +10,7 @@ import org.bukkit.entity.Item import org.bukkit.inventory.FurnaceRecipe import org.bukkit.inventory.ItemStack -class AutoSmelter(location: Location, facing: BlockFace = BlockFace.NORTH) : PowerBlock(location, maxStorage = 2) { +class AutoSmelter(location: Location, facing: BlockFace = BlockFace.NORTH) : PowerBlock(location, maxStorage = 4) { override val canReceivePower: Boolean = true override val updateIntervalTicks: Long = 20L override val baseBlockId: String = BLOCK_ID diff --git a/src/main/kotlin/com/coderjoe/atlas/utility/block/CobblestoneFactory.kt b/src/main/kotlin/com/coderjoe/atlas/utility/block/CobblestoneFactory.kt index b9618f2..9430fc1 100644 --- a/src/main/kotlin/com/coderjoe/atlas/utility/block/CobblestoneFactory.kt +++ b/src/main/kotlin/com/coderjoe/atlas/utility/block/CobblestoneFactory.kt @@ -5,7 +5,7 @@ import com.coderjoe.atlas.core.PlacementType import org.bukkit.Location import org.bukkit.Material -class CobblestoneFactory(location: Location) : MaterialFactory(location, maxStorage = 2) { +class CobblestoneFactory(location: Location) : MaterialFactory(location, maxStorage = 4) { companion object { const val BLOCK_ID = "atlas:cobblestone_factory" const val BLOCK_ID_ACTIVE = "atlas:cobblestone_factory_active" diff --git a/src/main/kotlin/com/coderjoe/atlas/utility/block/Crusher.kt b/src/main/kotlin/com/coderjoe/atlas/utility/block/Crusher.kt new file mode 100644 index 0000000..2bc0369 --- /dev/null +++ b/src/main/kotlin/com/coderjoe/atlas/utility/block/Crusher.kt @@ -0,0 +1,89 @@ +package com.coderjoe.atlas.utility.block + +import com.coderjoe.atlas.core.BlockDescriptor +import com.coderjoe.atlas.core.PlacementType +import com.coderjoe.atlas.power.PowerBlock +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.block.BlockFace +import org.bukkit.entity.Item +import org.bukkit.inventory.ItemStack + +class Crusher(location: Location, facing: BlockFace = BlockFace.NORTH) : PowerBlock(location, maxStorage = 8) { + override val canReceivePower: Boolean = true + override val updateIntervalTicks: Long = 20L + override val baseBlockId: String = BLOCK_ID + + var direction: BlockFace = if (facing == BlockFace.SELF) BlockFace.NORTH else facing + + override val facing: BlockFace get() = direction + + companion object { + const val BLOCK_ID = "atlas:crusher" + const val POWER_PER_CRUSH = 4 + private const val MOVE_DISTANCE = 1.0 + private const val DROP_AMOUNT = 2 + + val ORE_TO_DROP: Map = + mapOf( + Material.IRON_ORE to Material.RAW_IRON, + Material.GOLD_ORE to Material.RAW_GOLD, + Material.COPPER_ORE to Material.RAW_COPPER, + Material.COAL_ORE to Material.COAL, + Material.DIAMOND_ORE to Material.DIAMOND, + Material.EMERALD_ORE to Material.EMERALD, + Material.LAPIS_ORE to Material.LAPIS_LAZULI, + Material.REDSTONE_ORE to Material.REDSTONE, + Material.DEEPSLATE_IRON_ORE to Material.RAW_IRON, + Material.DEEPSLATE_GOLD_ORE to Material.RAW_GOLD, + Material.DEEPSLATE_COPPER_ORE to Material.RAW_COPPER, + Material.DEEPSLATE_COAL_ORE to Material.COAL, + Material.DEEPSLATE_DIAMOND_ORE to Material.DIAMOND, + Material.DEEPSLATE_EMERALD_ORE to Material.EMERALD, + Material.DEEPSLATE_LAPIS_ORE to Material.LAPIS_LAZULI, + Material.DEEPSLATE_REDSTONE_ORE to Material.REDSTONE, + ) + + val descriptor = + BlockDescriptor( + baseBlockId = BLOCK_ID, + displayName = "Crusher", + description = "Crushes ore blocks into double the ores, consumes $POWER_PER_CRUSH power per item", + placementType = PlacementType.DIRECTIONAL, + constructor = { loc, face -> Crusher(loc, face) }, + ) + } + + override fun getVisualStateBlockId(): String = BLOCK_ID + + override fun powerUpdate() { + pullPowerFromNeighbors() + + val world = location.world ?: return + + val scanCenter = location.clone().add(0.5, 0.75, 0.5) + val nearbyItems = + world.getNearbyEntities(scanCenter, 0.5, 0.75, 0.5) + .filterIsInstance() + + if (nearbyItems.isEmpty()) return + + val dx = direction.direction.x * MOVE_DISTANCE + val dz = direction.direction.z * MOVE_DISTANCE + + for (item in nearbyItems) { + if (currentPower >= POWER_PER_CRUSH) { + val dropMaterial = ORE_TO_DROP[item.itemStack.type] + if (dropMaterial != null) { + removePower(POWER_PER_CRUSH) + val crushedStack = ItemStack(dropMaterial, item.itemStack.amount * DROP_AMOUNT) + item.itemStack = crushedStack + } + } + + item.teleportAsync(item.location.add(dx, 0.0, dz)) + } + + updatePoweredState() + } +} diff --git a/src/main/kotlin/com/coderjoe/atlas/utility/block/ObsidianFactory.kt b/src/main/kotlin/com/coderjoe/atlas/utility/block/ObsidianFactory.kt index 02ef4eb..f8ad140 100644 --- a/src/main/kotlin/com/coderjoe/atlas/utility/block/ObsidianFactory.kt +++ b/src/main/kotlin/com/coderjoe/atlas/utility/block/ObsidianFactory.kt @@ -5,11 +5,11 @@ import com.coderjoe.atlas.core.PlacementType import org.bukkit.Location import org.bukkit.Material -class ObsidianFactory(location: Location) : MaterialFactory(location, maxStorage = 100) { +class ObsidianFactory(location: Location) : MaterialFactory(location, maxStorage = 50) { companion object { const val BLOCK_ID = "atlas:obsidian_factory" const val BLOCK_ID_ACTIVE = "atlas:obsidian_factory_active" - const val POWER_COST = 100 + const val POWER_COST = 25 val descriptor = BlockDescriptor( diff --git a/src/main/kotlin/com/coderjoe/atlas/utility/block/SmallDrill.kt b/src/main/kotlin/com/coderjoe/atlas/utility/block/SmallDrill.kt index 95b75b0..29e2fd3 100644 --- a/src/main/kotlin/com/coderjoe/atlas/utility/block/SmallDrill.kt +++ b/src/main/kotlin/com/coderjoe/atlas/utility/block/SmallDrill.kt @@ -6,13 +6,15 @@ import com.coderjoe.atlas.power.PowerBlock import org.bukkit.Location import org.bukkit.Material import org.bukkit.block.BlockFace +import org.bukkit.inventory.ItemStack -class SmallDrill(location: Location, facing: BlockFace? = null) : PowerBlock(location, maxStorage = 10) { +open class SmallDrill(location: Location, facing: BlockFace? = null, maxStorage: Int = 16) : PowerBlock(location, maxStorage) { override val canReceivePower: Boolean = true override val updateIntervalTicks: Long = 20L var miningDirection: BlockFace = if (facing == null || facing == BlockFace.SELF) BlockFace.DOWN else facing var enabled: Boolean = true + internal open val powerCost: Int = 8 companion object { const val BLOCK_ID = "atlas:small_drill" @@ -22,7 +24,7 @@ class SmallDrill(location: Location, facing: BlockFace? = null) : PowerBlock(loc BlockDescriptor( baseBlockId = BLOCK_ID, displayName = "Small Drill", - description = "Machine - consumes 10 power/s", + description = "Machine - consumes 8 power/s", placementType = PlacementType.DIRECTIONAL_OPPOSITE, constructor = { loc, facing -> SmallDrill(loc, facing) }, ) @@ -42,7 +44,7 @@ class SmallDrill(location: Location, facing: BlockFace? = null) : PowerBlock(loc pullPowerFromNeighbors() - if (currentPower < 10) return + if (currentPower < powerCost) return val world = location.world ?: return @@ -83,10 +85,15 @@ class SmallDrill(location: Location, facing: BlockFace? = null) : PowerBlock(loc } } + internal open fun getBlockDrops(block: org.bukkit.block.Block): Collection { + val tool = ItemStack(Material.DIAMOND_PICKAXE) + return block.getDrops(tool) + } + private fun mineBlock(block: org.bukkit.block.Block) { val world = location.world ?: return - removePower(10) - val drops = block.getDrops() + removePower(powerCost) + val drops = getBlockDrops(block) block.setType(Material.AIR, false) val dropLocation = location.clone().add(0.5, 1.0, 0.5) diff --git a/src/main/kotlin/com/coderjoe/atlas/utility/block/SoftTouchDrill.kt b/src/main/kotlin/com/coderjoe/atlas/utility/block/SoftTouchDrill.kt new file mode 100644 index 0000000..85ead29 --- /dev/null +++ b/src/main/kotlin/com/coderjoe/atlas/utility/block/SoftTouchDrill.kt @@ -0,0 +1,32 @@ +package com.coderjoe.atlas.utility.block + +import com.coderjoe.atlas.core.BlockDescriptor +import com.coderjoe.atlas.core.PlacementType +import org.bukkit.Location +import org.bukkit.block.BlockFace +import org.bukkit.inventory.ItemStack + +class SoftTouchDrill(location: Location, facing: BlockFace? = null) : SmallDrill(location, facing, maxStorage = 40) { + override val powerCost: Int = 20 + + companion object { + const val BLOCK_ID = "atlas:soft_touch_drill" + + val descriptor = + BlockDescriptor( + baseBlockId = BLOCK_ID, + displayName = "Soft Touch Drill", + description = "Machine - consumes 20 power/s, drops blocks in original form", + placementType = PlacementType.DIRECTIONAL_OPPOSITE, + constructor = { loc, facing -> SoftTouchDrill(loc, facing) }, + ) + } + + override val baseBlockId: String = BLOCK_ID + + override fun getVisualStateBlockId(): String = BLOCK_ID + + override fun getBlockDrops(block: org.bukkit.block.Block): Collection { + return listOf(ItemStack(block.type)) + } +} diff --git a/src/main/resources/atlas/configuration/crusher.yml b/src/main/resources/atlas/configuration/crusher.yml new file mode 100644 index 0000000..db53003 --- /dev/null +++ b/src/main/resources/atlas/configuration/crusher.yml @@ -0,0 +1,172 @@ +items: + atlas:crusher: + material: paper + data: + item-name: "Crusher" + model: minecraft:block/custom/crusher + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + hardness: 4.0 + resistance: 4.0 + is-suffocating: true + is-redstone-conductor: false + push-reaction: push_only + tags: ["minecraft:mineable/pickaxe"] + sounds: + break: minecraft:block.metal.break + step: minecraft:block.metal.step + place: minecraft:block.metal.place + hit: minecraft:block.metal.hit + fall: minecraft:block.metal.fall + states: + properties: + facing: + type: horizontal_direction + default: north + powered: + type: boolean + default: false + appearances: + north: + auto-state: solid + model: + path: minecraft:block/custom/crusher + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_front + south: minecraft:block/custom/crusher_back + east: minecraft:block/custom/crusher_side + west: minecraft:block/custom/crusher_side + up: minecraft:block/custom/crusher_top_north + down: minecraft:block/custom/crusher_housing + south: + auto-state: solid + model: + path: minecraft:block/custom/crusher_south + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_back + south: minecraft:block/custom/crusher_front + east: minecraft:block/custom/crusher_side + west: minecraft:block/custom/crusher_side + up: minecraft:block/custom/crusher_top_south + down: minecraft:block/custom/crusher_housing + east: + auto-state: solid + model: + path: minecraft:block/custom/crusher_east + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_side + south: minecraft:block/custom/crusher_side + east: minecraft:block/custom/crusher_front + west: minecraft:block/custom/crusher_back + up: minecraft:block/custom/crusher_top_east + down: minecraft:block/custom/crusher_housing + west: + auto-state: solid + model: + path: minecraft:block/custom/crusher_west + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_side + south: minecraft:block/custom/crusher_side + east: minecraft:block/custom/crusher_back + west: minecraft:block/custom/crusher_front + up: minecraft:block/custom/crusher_top_west + down: minecraft:block/custom/crusher_housing + north_powered: + auto-state: solid + model: + path: minecraft:block/custom/crusher_powered + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_front + south: minecraft:block/custom/crusher_back + east: minecraft:block/custom/crusher_side + west: minecraft:block/custom/crusher_side + up: minecraft:block/custom/crusher_top_active + down: minecraft:block/custom/crusher_housing + south_powered: + auto-state: solid + model: + path: minecraft:block/custom/crusher_south_powered + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_back + south: minecraft:block/custom/crusher_front + east: minecraft:block/custom/crusher_side + west: minecraft:block/custom/crusher_side + up: minecraft:block/custom/crusher_top_active + down: minecraft:block/custom/crusher_housing + east_powered: + auto-state: solid + model: + path: minecraft:block/custom/crusher_east_powered + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_side + south: minecraft:block/custom/crusher_side + east: minecraft:block/custom/crusher_front + west: minecraft:block/custom/crusher_back + up: minecraft:block/custom/crusher_top_active + down: minecraft:block/custom/crusher_housing + west_powered: + auto-state: solid + model: + path: minecraft:block/custom/crusher_west_powered + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/crusher_side + south: minecraft:block/custom/crusher_side + east: minecraft:block/custom/crusher_back + west: minecraft:block/custom/crusher_front + up: minecraft:block/custom/crusher_top_active + down: minecraft:block/custom/crusher_housing + variants: + facing=north,powered=false: + appearance: north + facing=south,powered=false: + appearance: south + facing=east,powered=false: + appearance: east + facing=west,powered=false: + appearance: west + facing=north,powered=true: + appearance: north_powered + facing=south,powered=true: + appearance: south_powered + facing=east,powered=true: + appearance: east_powered + facing=west,powered=true: + appearance: west_powered + +recipes: + atlas:crusher: + type: shapeless + category: misc + unlock-on-ingredient-obtained: true + ingredients: + - minecraft:iron_ingot + - minecraft:iron_ingot + - minecraft:iron_ingot + - minecraft:redstone + - minecraft:redstone + - minecraft:redstone + - minecraft:piston + - minecraft:diamond + result: + id: atlas:crusher + count: 1 diff --git a/src/main/resources/atlas/configuration/lava_generator.yml b/src/main/resources/atlas/configuration/lava_generator.yml index 39a9681..71aa756 100644 --- a/src/main/resources/atlas/configuration/lava_generator.yml +++ b/src/main/resources/atlas/configuration/lava_generator.yml @@ -85,6 +85,7 @@ recipes: - minecraft:redstone - minecraft:redstone - minecraft:magma_block + - minecraft:blaze_rod result: id: atlas:lava_generator count: 1 diff --git a/src/main/resources/atlas/configuration/obsidian_factory.yml b/src/main/resources/atlas/configuration/obsidian_factory.yml index e87fa0f..9fc233e 100644 --- a/src/main/resources/atlas/configuration/obsidian_factory.yml +++ b/src/main/resources/atlas/configuration/obsidian_factory.yml @@ -85,6 +85,7 @@ recipes: - minecraft:redstone - minecraft:redstone - minecraft:obsidian + - minecraft:diamond result: id: atlas:obsidian_factory count: 1 diff --git a/src/main/resources/atlas/configuration/small_battery.yml b/src/main/resources/atlas/configuration/small_battery.yml index 7ef342a..55cae26 100644 --- a/src/main/resources/atlas/configuration/small_battery.yml +++ b/src/main/resources/atlas/configuration/small_battery.yml @@ -163,6 +163,8 @@ recipes: - minecraft:redstone - minecraft:redstone - minecraft:copper_ingot + - minecraft:copper_ingot + - minecraft:copper_ingot result: id: atlas:small_battery count: 1 diff --git a/src/main/resources/atlas/configuration/soft_touch_drill.yml b/src/main/resources/atlas/configuration/soft_touch_drill.yml new file mode 100644 index 0000000..343306b --- /dev/null +++ b/src/main/resources/atlas/configuration/soft_touch_drill.yml @@ -0,0 +1,111 @@ +items: + atlas:soft_touch_drill: + material: paper + data: + item-name: "Soft Touch Drill" + model: minecraft:block/custom/soft_touch_drill + behavior: + type: block_item + block: + loot: + template: default:loot_table/self + settings: + hardness: 4.0 + resistance: 4.0 + is-suffocating: true + is-redstone-conductor: false + push-reaction: push_only + tags: ["minecraft:mineable/pickaxe"] + sounds: + break: minecraft:block.metal.break + step: minecraft:block.metal.step + place: minecraft:block.metal.place + hit: minecraft:block.metal.hit + fall: minecraft:block.metal.fall + states: + properties: + facing: + type: direction + default: down + appearances: + north: + auto-state: solid + model: + path: minecraft:block/custom/soft_touch_drill + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/soft_touch_drill_front + south: minecraft:block/custom/soft_touch_drill + east: minecraft:block/custom/small_drill_arrow_right + west: minecraft:block/custom/small_drill_arrow_left + up: minecraft:block/custom/small_drill_arrow_up + down: minecraft:block/custom/small_drill_arrow_down + south: + auto-state: solid + model: + path: minecraft:block/custom/soft_touch_drill + y: 180 + east: + auto-state: solid + model: + path: minecraft:block/custom/soft_touch_drill + y: 90 + west: + auto-state: solid + model: + path: minecraft:block/custom/soft_touch_drill + y: 270 + up: + auto-state: solid + model: + path: minecraft:block/custom/soft_touch_drill_up + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/small_drill_arrow_up + south: minecraft:block/custom/small_drill_arrow_down + east: minecraft:block/custom/small_drill_arrow_right + west: minecraft:block/custom/small_drill_arrow_left + up: minecraft:block/custom/soft_touch_drill_front + down: minecraft:block/custom/soft_touch_drill + down: + auto-state: solid + model: + path: minecraft:block/custom/soft_touch_drill_down + generation: + parent: minecraft:block/cube + textures: + north: minecraft:block/custom/small_drill_arrow_down + south: minecraft:block/custom/small_drill_arrow_up + east: minecraft:block/custom/small_drill_arrow_right + west: minecraft:block/custom/small_drill_arrow_left + up: minecraft:block/custom/soft_touch_drill + down: minecraft:block/custom/soft_touch_drill_front + variants: + facing=north: + appearance: north + facing=south: + appearance: south + facing=east: + appearance: east + facing=west: + appearance: west + facing=up: + appearance: up + facing=down: + appearance: down + +recipes: + atlas:soft_touch_drill: + type: shapeless + category: misc + unlock-on-ingredient-obtained: true + ingredients: + - atlas:small_drill + - minecraft:diamond + - minecraft:diamond + - minecraft:emerald + result: + id: atlas:soft_touch_drill + count: 1 diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_back.png new file mode 100644 index 0000000..c9afab4 Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_front.png new file mode 100644 index 0000000..9a0b0d9 Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_housing.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_housing.png new file mode 100644 index 0000000..f4e9df5 Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_housing.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_side.png new file mode 100644 index 0000000..f8779dd Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_active.png new file mode 100644 index 0000000..8bf7c36 Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_east.png new file mode 100644 index 0000000..8ca070c Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_north.png new file mode 100644 index 0000000..8ecbc32 Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_south.png new file mode 100644 index 0000000..9b4088e Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_west.png new file mode 100644 index 0000000..b296b9b Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/soft_touch_drill.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/soft_touch_drill.png new file mode 100644 index 0000000..4e0621f Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/soft_touch_drill.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/soft_touch_drill_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/soft_touch_drill_front.png new file mode 100644 index 0000000..457fd96 Binary files /dev/null and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/soft_touch_drill_front.png differ diff --git a/src/test/kotlin/com/coderjoe/atlas/AtlasPluginTest.kt b/src/test/kotlin/com/coderjoe/atlas/AtlasPluginTest.kt index 8b3b2c4..6776148 100644 --- a/src/test/kotlin/com/coderjoe/atlas/AtlasPluginTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/AtlasPluginTest.kt @@ -32,9 +32,9 @@ class AtlasPluginTest { } @Test - fun `power system initializes with 17 block types`() { + fun `power system initializes with 18 block types`() { TestHelper.initPowerFactory() - assertEquals(17, PowerBlockFactory.getRegisteredBlockIds().size) + assertEquals(19, PowerBlockFactory.getRegisteredBlockIds().size) } @Test diff --git a/src/test/kotlin/com/coderjoe/atlas/CrossSystemIntegrationTest.kt b/src/test/kotlin/com/coderjoe/atlas/CrossSystemIntegrationTest.kt index c40e4e6..2ec20e2 100644 --- a/src/test/kotlin/com/coderjoe/atlas/CrossSystemIntegrationTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/CrossSystemIntegrationTest.kt @@ -133,7 +133,7 @@ class CrossSystemIntegrationTest { // Step 1: solar generates solar.callPowerUpdate() - assertEquals(1, solar.currentPower) + assertEquals(2, solar.currentPower) // Step 2: cable pulls from solar cable.callPowerUpdate() diff --git a/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt b/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt index 2fc7136..390770c 100644 --- a/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt +++ b/src/test/kotlin/com/coderjoe/atlas/TestHelper.kt @@ -25,8 +25,10 @@ import com.coderjoe.atlas.transport.TransportBlockRegistry import com.coderjoe.atlas.transport.block.ConveyorBelt import com.coderjoe.atlas.utility.block.AutoSmelter import com.coderjoe.atlas.utility.block.CobblestoneFactory +import com.coderjoe.atlas.utility.block.Crusher import com.coderjoe.atlas.utility.block.ObsidianFactory import com.coderjoe.atlas.utility.block.SmallDrill +import com.coderjoe.atlas.utility.block.SoftTouchDrill import io.mockk.every import io.mockk.mockk import io.mockk.unmockkAll @@ -201,7 +203,9 @@ object TestHelper { SmallBattery.descriptor, PowerCable.descriptor, LavaGenerator.descriptor, AutoSmelter.descriptor, PowerSplitter.descriptor, CobblestoneFactory.descriptor, - ObsidianFactory.descriptor, PowerMerger.descriptor, + ObsidianFactory.descriptor, Crusher.descriptor, + PowerMerger.descriptor, + SoftTouchDrill.descriptor, ), ) } diff --git a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt index 1df2472..20d532c 100644 --- a/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/fluid/FluidContainerTest.kt @@ -153,10 +153,10 @@ class FluidContainerTest { } @Test - fun `fill level 1 at 1 to 3`() { + fun `fill level 1 at 1 to 6`() { val container = FluidContainer(TestHelper.createLocation(), BlockFace.NORTH) - for (i in 1..3) { + for (i in 1..6) { container.storeFluid(FluidType.WATER) assertEquals( 1, @@ -167,22 +167,22 @@ class FluidContainerTest { } @Test - fun `fill level 2 at 4 to 7`() { + fun `fill level 2 at 7 to 13`() { val container = FluidContainer(TestHelper.createLocation(), BlockFace.NORTH) - repeat(4) { container.storeFluid(FluidType.WATER) } + repeat(7) { container.storeFluid(FluidType.WATER) } assertEquals(2, container.getFillLevel()) - repeat(3) { container.storeFluid(FluidType.WATER) } + repeat(6) { container.storeFluid(FluidType.WATER) } assertEquals(2, container.getFillLevel()) } @Test - fun `fill level 3 at 8 to 10`() { + fun `fill level 3 at 14 to 20`() { val container = FluidContainer(TestHelper.createLocation(), BlockFace.NORTH) - repeat(8) { container.storeFluid(FluidType.WATER) } + repeat(14) { container.storeFluid(FluidType.WATER) } assertEquals(3, container.getFillLevel()) - repeat(2) { container.storeFluid(FluidType.WATER) } + repeat(6) { container.storeFluid(FluidType.WATER) } assertEquals(3, container.getFillLevel()) } diff --git a/src/test/kotlin/com/coderjoe/atlas/power/LavaGeneratorTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/LavaGeneratorTest.kt index 140ffa7..9ba097c 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/LavaGeneratorTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/LavaGeneratorTest.kt @@ -27,9 +27,9 @@ class LavaGeneratorTest { } @Test - fun `lava generator maxStorage is 50`() { + fun `lava generator maxStorage is 20`() { val gen = LavaGenerator(TestHelper.createLocation()) - assertEquals(50, gen.maxStorage) + assertEquals(20, gen.maxStorage) } @Test diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockDialogTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockDialogTest.kt index 0a61790..6132a2c 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockDialogTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockDialogTest.kt @@ -62,7 +62,7 @@ class PowerBlockDialogTest { @Test fun `power bar color green when ratio above 0_7`() { val battery = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - battery.currentPower = 8 // 80% = green + battery.currentPower = 40 // 80% = green val info = buildPowerInfo(battery) val text = flattenText(info) assertTrue(text.contains("80%")) @@ -71,7 +71,7 @@ class PowerBlockDialogTest { @Test fun `power bar color yellow when ratio above 0_3`() { val battery = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - battery.currentPower = 5 // 50% = yellow + battery.currentPower = 25 // 50% = yellow val info = buildPowerInfo(battery) val text = flattenText(info) assertTrue(text.contains("50%")) @@ -80,7 +80,7 @@ class PowerBlockDialogTest { @Test fun `power bar color red when ratio below 0_3`() { val battery = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - battery.currentPower = 1 // 10% = red + battery.currentPower = 5 // 10% = red val info = buildPowerInfo(battery) val text = flattenText(info) assertTrue(text.contains("10%")) diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt index ac7cb10..d7da5a8 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockInitializerTest.kt @@ -37,9 +37,11 @@ class PowerBlockInitializerTest { // PowerSplitter: 1 // CobblestoneFactory: 2 (base + active) // ObsidianFactory: 2 (base + active) + // Crusher: 1 // PowerMerger: 1 - // Total: 17 - assertEquals(17, ids.size) + // SoftTouchDrill: 1 + // Total: 19 + assertEquals(19, ids.size) } @Test diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockLogicTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockLogicTest.kt index 6aa89fe..fade5be 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockLogicTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerBlockLogicTest.kt @@ -26,7 +26,7 @@ class PowerBlockLogicTest { TestHelper.teardown() } - // --- PowerBlock base class (via SmallBattery, maxStorage=10) --- + // --- PowerBlock base class (via SmallBattery, maxStorage=50) --- @Test fun `addPower on empty block returns amount added`() { @@ -41,19 +41,19 @@ class PowerBlockLogicTest { fun `addPower caps at maxStorage`() { val block = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - val added = block.addPower(15) - assertEquals(10, added) - assertEquals(10, block.currentPower) + val added = block.addPower(55) + assertEquals(50, added) + assertEquals(50, block.currentPower) } @Test fun `addPower with partial space returns space available`() { val block = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - block.currentPower = 8 + block.currentPower = 48 val added = block.addPower(3) assertEquals(2, added) - assertEquals(10, block.currentPower) + assertEquals(50, block.currentPower) } @Test @@ -111,7 +111,7 @@ class PowerBlockLogicTest { fun `canAcceptPower returns false when full`() { val block = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - block.currentPower = 10 + block.currentPower = 50 assertFalse(block.canAcceptPower()) } @@ -130,9 +130,9 @@ class PowerBlockLogicTest { } @Test - fun `solar panel maxStorage is 1`() { + fun `solar panel maxStorage is 4`() { val panel = SmallSolarPanel(TestHelper.createLocation()) - assertEquals(1, panel.maxStorage) + assertEquals(4, panel.maxStorage) } @Test @@ -159,7 +159,7 @@ class PowerBlockLogicTest { every { TestHelper.mockWorld.time } returns 6000L val panel = SmallSolarPanel(TestHelper.createLocation()) panel.callPowerUpdate() - assertEquals(1, panel.currentPower) + assertEquals(2, panel.currentPower) } @Test @@ -174,18 +174,18 @@ class PowerBlockLogicTest { fun `solar panel does not overflow past maxStorage`() { every { TestHelper.mockWorld.time } returns 6000L val panel = SmallSolarPanel(TestHelper.createLocation()) - panel.currentPower = 1 + panel.currentPower = 4 panel.callPowerUpdate() - assertEquals(1, panel.currentPower) + assertEquals(4, panel.currentPower) } // --- SmallBattery specifics --- @Test - fun `battery maxStorage is 10`() { + fun `battery maxStorage is 50`() { val battery = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - assertEquals(10, battery.maxStorage) + assertEquals(50, battery.maxStorage) } @Test @@ -206,10 +206,10 @@ class PowerBlockLogicTest { } @Test - fun `battery visual state low when power 1-3`() { + fun `battery visual state low when power 1-16`() { val battery = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - for (p in 1..3) { + for (p in 1..16) { battery.currentPower = p assertEquals( "atlas:small_battery_low", @@ -220,10 +220,10 @@ class PowerBlockLogicTest { } @Test - fun `battery visual state medium when power 4-7`() { + fun `battery visual state medium when power 17-33`() { val battery = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - for (p in 4..7) { + for (p in 17..33) { battery.currentPower = p assertEquals( "atlas:small_battery_medium", @@ -234,10 +234,10 @@ class PowerBlockLogicTest { } @Test - fun `battery visual state full when power 8-10`() { + fun `battery visual state full when power 34-50`() { val battery = SmallBattery(TestHelper.createLocation(), BlockFace.NORTH) - for (p in 8..10) { + for (p in 34..50) { battery.currentPower = p assertEquals( "atlas:small_battery_full", @@ -363,9 +363,9 @@ class PowerBlockLogicTest { // --- SmallDrill specifics --- @Test - fun `drill maxStorage is 10`() { + fun `drill maxStorage is 16`() { val drill = SmallDrill(TestHelper.createLocation()) - assertEquals(10, drill.maxStorage) + assertEquals(16, drill.maxStorage) } @Test @@ -420,7 +420,7 @@ class PowerBlockLogicTest { every { TestHelper.mockWorld.time } returns 0L val panel = SmallSolarPanel(TestHelper.createLocation()) panel.callPowerUpdate() - assertEquals(1, panel.currentPower) + assertEquals(2, panel.currentPower) } @Test @@ -428,7 +428,7 @@ class PowerBlockLogicTest { every { TestHelper.mockWorld.time } returns 12000L val panel = SmallSolarPanel(TestHelper.createLocation()) panel.callPowerUpdate() - assertEquals(1, panel.currentPower) + assertEquals(2, panel.currentPower) } @Test diff --git a/src/test/kotlin/com/coderjoe/atlas/power/PowerNetworkIntegrationTest.kt b/src/test/kotlin/com/coderjoe/atlas/power/PowerNetworkIntegrationTest.kt index 7b6884a..91ce085 100644 --- a/src/test/kotlin/com/coderjoe/atlas/power/PowerNetworkIntegrationTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/power/PowerNetworkIntegrationTest.kt @@ -45,12 +45,12 @@ class PowerNetworkIntegrationTest { // Solar generates solar.callPowerUpdate() - assertEquals(1, solar.currentPower) + assertEquals(2, solar.currentPower) - // Cable pulls from solar + // Cable pulls from solar (cable maxStorage=1, so pulls 1) cable.callPowerUpdate() assertEquals(1, cable.currentPower) - assertEquals(0, solar.currentPower) + assertEquals(1, solar.currentPower) } @Test @@ -67,9 +67,9 @@ class PowerNetworkIntegrationTest { // Tick 1: solar generates solar.callPowerUpdate() - assertEquals(1, solar.currentPower) + assertEquals(2, solar.currentPower) - // Tick 1: cable1 pulls from solar + // Tick 1: cable1 pulls from solar (cable maxStorage=1) cable1.callPowerUpdate() assertEquals(1, cable1.currentPower) diff --git a/src/test/kotlin/com/coderjoe/atlas/utility/AutoSmelterTest.kt b/src/test/kotlin/com/coderjoe/atlas/utility/AutoSmelterTest.kt index c81fbed..f7f22cb 100644 --- a/src/test/kotlin/com/coderjoe/atlas/utility/AutoSmelterTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/utility/AutoSmelterTest.kt @@ -98,10 +98,10 @@ class AutoSmelterTest { } @Test - fun `max storage is 2`() { + fun `max storage is 4`() { val smelter = AutoSmelter(TestHelper.createLocation(), BlockFace.NORTH) - assertEquals(2, smelter.maxStorage) + assertEquals(4, smelter.maxStorage) } @Test @@ -265,7 +265,7 @@ class AutoSmelterTest { val registry = PowerBlockRegistry(TestHelper.mockPlugin) val smelterLoc = TestHelper.createLocation(0.0, 64.0, 0.0) val smelter = AutoSmelter(smelterLoc, BlockFace.NORTH) - smelter.currentPower = 2 + smelter.currentPower = 4 TestHelper.addToRegistry( registry, smelter, @@ -293,7 +293,7 @@ class AutoSmelterTest { smelter.callPowerUpdate() - assertEquals(2, smelter.currentPower) + assertEquals(4, smelter.currentPower) assertEquals(5, battery.currentPower) } } diff --git a/src/test/kotlin/com/coderjoe/atlas/utility/CobblestoneFactoryTest.kt b/src/test/kotlin/com/coderjoe/atlas/utility/CobblestoneFactoryTest.kt index 07db75e..f592f73 100644 --- a/src/test/kotlin/com/coderjoe/atlas/utility/CobblestoneFactoryTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/utility/CobblestoneFactoryTest.kt @@ -27,9 +27,9 @@ class CobblestoneFactoryTest { } @Test - fun `cobblestone generator maxStorage is 2`() { + fun `cobblestone generator maxStorage is 4`() { val gen = CobblestoneFactory(TestHelper.createLocation()) - assertEquals(2, gen.maxStorage) + assertEquals(4, gen.maxStorage) } @Test diff --git a/src/test/kotlin/com/coderjoe/atlas/utility/CrusherTest.kt b/src/test/kotlin/com/coderjoe/atlas/utility/CrusherTest.kt new file mode 100644 index 0000000..8750e21 --- /dev/null +++ b/src/test/kotlin/com/coderjoe/atlas/utility/CrusherTest.kt @@ -0,0 +1,316 @@ +package com.coderjoe.atlas.utility + +import com.coderjoe.atlas.TestHelper +import com.coderjoe.atlas.TestHelper.callPowerUpdate +import com.coderjoe.atlas.core.PlacementType +import com.coderjoe.atlas.power.PowerBlockFactory +import com.coderjoe.atlas.power.PowerBlockRegistry +import com.coderjoe.atlas.utility.block.Crusher +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.block.BlockFace +import org.bukkit.entity.Item +import org.bukkit.inventory.ItemStack +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertDoesNotThrow +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.util.concurrent.CompletableFuture + +class CrusherTest { + @BeforeEach + fun setup() { + TestHelper.setup() + } + + @AfterEach + fun teardown() { + TestHelper.teardown() + } + + @Test + fun `crusher has correct facing`() { + val crusher = Crusher(TestHelper.createLocation(), BlockFace.NORTH) + assertEquals(BlockFace.NORTH, crusher.facing) + } + + @Test + fun `crusher visual state always returns BLOCK_ID`() { + val crusher = Crusher(TestHelper.createLocation(), BlockFace.NORTH) + crusher.currentPower = 0 + assertEquals("atlas:crusher", crusher.getVisualStateBlockId()) + crusher.currentPower = 4 + assertEquals("atlas:crusher", crusher.getVisualStateBlockId()) + } + + @Test + fun `crusher base block ID is atlas crusher`() { + val crusher = Crusher(TestHelper.createLocation(), BlockFace.SOUTH) + assertEquals("atlas:crusher", crusher.baseBlockId) + } + + @Test + fun `crusher descriptor has correct properties`() { + val desc = Crusher.descriptor + assertEquals("atlas:crusher", desc.baseBlockId) + assertEquals("Crusher", desc.displayName) + assertEquals(PlacementType.DIRECTIONAL, desc.placementType) + } + + @Test + fun `max storage is 8`() { + val crusher = Crusher(TestHelper.createLocation(), BlockFace.NORTH) + assertEquals(8, crusher.maxStorage) + } + + @Test + fun `canReceivePower is true`() { + val crusher = Crusher(TestHelper.createLocation(), BlockFace.NORTH) + assertTrue(crusher.canAcceptPower()) + } + + @Test + fun `base ID is registered`() { + TestHelper.initPowerFactory() + assertTrue(PowerBlockFactory.isRegistered("atlas:crusher")) + } + + @Test + fun `factory creates Crusher from base ID`() { + TestHelper.initPowerFactory() + val block = + PowerBlockFactory.createPowerBlock( + "atlas:crusher", + TestHelper.createLocation(), + BlockFace.NORTH, + ) + assertTrue(block is Crusher) + assertEquals(BlockFace.NORTH, block!!.facing) + } + + @Test + fun `power update does not throw with no nearby entities`() { + PowerBlockRegistry(TestHelper.mockPlugin) + val crusher = Crusher(TestHelper.createLocation(), BlockFace.NORTH) + + every { + TestHelper.mockWorld.getNearbyEntities( + any(), any(), any(), any(), + ) + } returns emptyList() + + assertDoesNotThrow { + crusher.callPowerUpdate() + } + } + + @Test + fun `power update moves item forward without crushing when no power`() { + PowerBlockRegistry(TestHelper.mockPlugin) + val crusher = Crusher(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.NORTH) + crusher.currentPower = 0 + + val itemLoc = Location(TestHelper.mockWorld, 0.5, 64.375, 0.5) + val mockItem = mockk(relaxed = true) + val mockStack = mockk(relaxed = true) + every { mockItem.location } returns itemLoc + every { mockItem.itemStack } returns mockStack + every { mockStack.type } returns Material.IRON_ORE + every { mockItem.teleportAsync(any()) } returns CompletableFuture.completedFuture(true) + + every { + TestHelper.mockWorld.getNearbyEntities( + any(), any(), any(), any(), + ) + } returns listOf(mockItem) + + crusher.callPowerUpdate() + + verify { + mockItem.teleportAsync(match { loc -> loc.z < 0.5 && loc.x == 0.5 }) + } + assertEquals(0, crusher.currentPower) + } + + @Test + fun `power update moves item east`() { + PowerBlockRegistry(TestHelper.mockPlugin) + val crusher = Crusher(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.EAST) + crusher.currentPower = 0 + + val itemLoc = Location(TestHelper.mockWorld, 0.5, 64.375, 0.5) + val mockItem = mockk(relaxed = true) + val mockStack = mockk(relaxed = true) + every { mockItem.location } returns itemLoc + every { mockItem.itemStack } returns mockStack + every { mockStack.type } returns Material.STONE + every { mockItem.teleportAsync(any()) } returns CompletableFuture.completedFuture(true) + + every { + TestHelper.mockWorld.getNearbyEntities( + any(), any(), any(), any(), + ) + } returns listOf(mockItem) + + crusher.callPowerUpdate() + + verify { + mockItem.teleportAsync(match { loc -> loc.x > 0.5 && loc.z == 0.5 }) + } + } + + @Test + fun `crushes ore block item and consumes power`() { + PowerBlockRegistry(TestHelper.mockPlugin) + val crusher = Crusher(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.NORTH) + crusher.currentPower = 4 + + val itemLoc = Location(TestHelper.mockWorld, 0.5, 64.375, 0.5) + val mockItem = mockk(relaxed = true) + val mockStack = mockk(relaxed = true) + every { mockItem.location } returns itemLoc + every { mockItem.itemStack } returns mockStack + every { mockStack.type } returns Material.IRON_ORE + every { mockStack.amount } returns 1 + every { mockItem.teleportAsync(any()) } returns CompletableFuture.completedFuture(true) + + every { + TestHelper.mockWorld.getNearbyEntities( + any(), any(), any(), any(), + ) + } returns listOf(mockItem) + + try { + crusher.callPowerUpdate() + } catch (_: Throwable) { + // ItemStack constructor triggers Registry init + } + + assertEquals(0, crusher.currentPower) + } + + @Test + fun `does not crush non-ore items`() { + PowerBlockRegistry(TestHelper.mockPlugin) + val crusher = Crusher(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.NORTH) + crusher.currentPower = 4 + + val itemLoc = Location(TestHelper.mockWorld, 0.5, 64.375, 0.5) + val mockItem = mockk(relaxed = true) + val mockStack = mockk(relaxed = true) + every { mockItem.location } returns itemLoc + every { mockItem.itemStack } returns mockStack + every { mockStack.type } returns Material.COBBLESTONE + every { mockItem.teleportAsync(any()) } returns CompletableFuture.completedFuture(true) + + every { + TestHelper.mockWorld.getNearbyEntities( + any(), any(), any(), any(), + ) + } returns listOf(mockItem) + + crusher.callPowerUpdate() + + assertEquals(4, crusher.currentPower) + } + + @Test + fun `ore to drop mapping covers all ore types`() { + val mappings = Crusher.ORE_TO_DROP + assertTrue(mappings.containsKey(Material.IRON_ORE)) + assertTrue(mappings.containsKey(Material.GOLD_ORE)) + assertTrue(mappings.containsKey(Material.COPPER_ORE)) + assertTrue(mappings.containsKey(Material.COAL_ORE)) + assertTrue(mappings.containsKey(Material.DIAMOND_ORE)) + assertTrue(mappings.containsKey(Material.EMERALD_ORE)) + assertTrue(mappings.containsKey(Material.LAPIS_ORE)) + assertTrue(mappings.containsKey(Material.REDSTONE_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_IRON_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_GOLD_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_COPPER_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_COAL_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_DIAMOND_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_EMERALD_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_LAPIS_ORE)) + assertTrue(mappings.containsKey(Material.DEEPSLATE_REDSTONE_ORE)) + } + + @Test + fun `iron ore drops raw iron`() { + assertEquals(Material.RAW_IRON, Crusher.ORE_TO_DROP[Material.IRON_ORE]) + } + + @Test + fun `gold ore drops raw gold`() { + assertEquals(Material.RAW_GOLD, Crusher.ORE_TO_DROP[Material.GOLD_ORE]) + } + + @Test + fun `copper ore drops raw copper`() { + assertEquals(Material.RAW_COPPER, Crusher.ORE_TO_DROP[Material.COPPER_ORE]) + } + + @Test + fun `coal ore drops coal`() { + assertEquals(Material.COAL, Crusher.ORE_TO_DROP[Material.COAL_ORE]) + } + + @Test + fun `diamond ore drops diamond`() { + assertEquals(Material.DIAMOND, Crusher.ORE_TO_DROP[Material.DIAMOND_ORE]) + } + + @Test + fun `pulls power from adjacent blocks`() { + val registry = PowerBlockRegistry(TestHelper.mockPlugin) + val crusherLoc = TestHelper.createLocation(0.0, 64.0, 0.0) + val crusher = Crusher(crusherLoc, BlockFace.NORTH) + TestHelper.addToRegistry(registry, crusher, "atlas:crusher") + + val batteryLoc = TestHelper.createLocation(1.0, 64.0, 0.0) + val battery = com.coderjoe.atlas.power.block.SmallBattery(batteryLoc, BlockFace.WEST) + battery.currentPower = 5 + TestHelper.addToRegistry(registry, battery, "atlas:small_battery") + + every { + TestHelper.mockWorld.getNearbyEntities( + any(), any(), any(), any(), + ) + } returns emptyList() + + crusher.callPowerUpdate() + + assertTrue(crusher.currentPower > 0) + assertTrue(battery.currentPower < 5) + } + + @Test + fun `does not exceed max storage when pulling power`() { + val registry = PowerBlockRegistry(TestHelper.mockPlugin) + val crusherLoc = TestHelper.createLocation(0.0, 64.0, 0.0) + val crusher = Crusher(crusherLoc, BlockFace.NORTH) + crusher.currentPower = 8 + TestHelper.addToRegistry(registry, crusher, "atlas:crusher") + + val batteryLoc = TestHelper.createLocation(1.0, 64.0, 0.0) + val battery = com.coderjoe.atlas.power.block.SmallBattery(batteryLoc, BlockFace.WEST) + battery.currentPower = 5 + TestHelper.addToRegistry(registry, battery, "atlas:small_battery") + + every { + TestHelper.mockWorld.getNearbyEntities( + any(), any(), any(), any(), + ) + } returns emptyList() + + crusher.callPowerUpdate() + + assertEquals(8, crusher.currentPower) + assertEquals(5, battery.currentPower) + } +} diff --git a/src/test/kotlin/com/coderjoe/atlas/utility/ObsidianFactoryTest.kt b/src/test/kotlin/com/coderjoe/atlas/utility/ObsidianFactoryTest.kt index cfb2af6..9bd26e9 100644 --- a/src/test/kotlin/com/coderjoe/atlas/utility/ObsidianFactoryTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/utility/ObsidianFactoryTest.kt @@ -27,9 +27,9 @@ class ObsidianFactoryTest { } @Test - fun `obsidian generator maxStorage is 100`() { + fun `obsidian generator maxStorage is 50`() { val gen = ObsidianFactory(TestHelper.createLocation()) - assertEquals(100, gen.maxStorage) + assertEquals(50, gen.maxStorage) } @Test @@ -41,7 +41,7 @@ class ObsidianFactoryTest { @Test fun `visual state idle when insufficient power`() { val gen = ObsidianFactory(TestHelper.createLocation()) - gen.currentPower = 99 + gen.currentPower = 24 assertEquals( "atlas:obsidian_factory", gen.getVisualStateBlockId(), @@ -51,7 +51,7 @@ class ObsidianFactoryTest { @Test fun `visual state active when power at cost`() { val gen = ObsidianFactory(TestHelper.createLocation()) - gen.currentPower = 100 + gen.currentPower = 25 assertEquals( "atlas:obsidian_factory_active", gen.getVisualStateBlockId(), @@ -65,7 +65,7 @@ class ObsidianFactoryTest { val genLoc = TestHelper.createLocation(0.0, 64.0, 0.0) val gen = ObsidianFactory(genLoc) - gen.currentPower = 100 + gen.currentPower = 25 TestHelper.addToRegistry( powerRegistry, gen, @@ -83,7 +83,7 @@ class ObsidianFactoryTest { gen.callPowerUpdate() - assertEquals(100, gen.currentPower) + assertEquals(25, gen.currentPower) assertTrue(pipe.hasFluid()) } @@ -123,7 +123,7 @@ class ObsidianFactoryTest { val genLoc = TestHelper.createLocation(0.0, 64.0, 0.0) val gen = ObsidianFactory(genLoc) - gen.currentPower = 99 + gen.currentPower = 24 TestHelper.addToRegistry( powerRegistry, gen, @@ -152,7 +152,7 @@ class ObsidianFactoryTest { gen.callPowerUpdate() - assertEquals(99, gen.currentPower) + assertEquals(24, gen.currentPower) assertTrue(waterPipe.hasFluid()) assertTrue(lavaPipe.hasFluid()) } @@ -164,7 +164,7 @@ class ObsidianFactoryTest { val genLoc = TestHelper.createLocation(0.0, 64.0, 0.0) val gen = ObsidianFactory(genLoc) - gen.currentPower = 100 + gen.currentPower = 25 TestHelper.addToRegistry( powerRegistry, gen, diff --git a/src/test/kotlin/com/coderjoe/atlas/utility/SmallDrillMiningTest.kt b/src/test/kotlin/com/coderjoe/atlas/utility/SmallDrillMiningTest.kt index c2a04b7..0789b89 100644 --- a/src/test/kotlin/com/coderjoe/atlas/utility/SmallDrillMiningTest.kt +++ b/src/test/kotlin/com/coderjoe/atlas/utility/SmallDrillMiningTest.kt @@ -7,10 +7,12 @@ import com.coderjoe.atlas.power.block.SmallSolarPanel import com.coderjoe.atlas.utility.block.SmallDrill import io.mockk.every import io.mockk.mockk +import io.mockk.spyk import io.mockk.verify import org.bukkit.Material import org.bukkit.block.Block import org.bukkit.block.BlockFace +import org.bukkit.inventory.ItemStack import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -38,19 +40,30 @@ class SmallDrillMiningTest { ): Block { val block = mockk(relaxed = true) every { block.type } returns material - every { block.getDrops() } returns emptyList() + every { block.getDrops(any()) } returns emptyList() every { TestHelper.mockWorld.getBlockAt(x, y, z) } returns block return block } + private fun createDrill( + x: Double, + y: Double, + z: Double, + facing: BlockFace, + ): SmallDrill { + val drill = spyk(SmallDrill(TestHelper.createLocation(x, y, z), facing)) + every { drill.getBlockDrops(any()) } returns emptyList() + return drill + } + @Test fun `drill disabled does not mine or pull power`() { val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) drill.enabled = false - drill.currentPower = 10 + drill.currentPower = 8 drill.callPowerUpdate() - assertEquals(10, drill.currentPower) // power unchanged + assertEquals(8, drill.currentPower) // power unchanged } @Test @@ -58,7 +71,7 @@ class SmallDrillMiningTest { val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) drill.currentPower = 5 - // Set up a stone block below — should not be mined since power < 10 + // Set up a stone block below — should not be mined since power < 8 mockBlockAt(0, 63, 0, Material.STONE) drill.callPowerUpdate() @@ -67,8 +80,8 @@ class SmallDrillMiningTest { @Test fun `drill with full power mines first non-air block below`() { - val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) - drill.currentPower = 10 + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.DOWN) + drill.currentPower = 8 // Air at y=63, stone at y=62 mockBlockAt(0, 63, 0, Material.AIR) @@ -81,8 +94,8 @@ class SmallDrillMiningTest { @Test fun `drill skips air variants when scanning downward`() { - val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) - drill.currentPower = 10 + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.DOWN) + drill.currentPower = 8 mockBlockAt(0, 63, 0, Material.AIR) mockBlockAt(0, 62, 0, Material.CAVE_AIR) @@ -97,18 +110,18 @@ class SmallDrillMiningTest { @Test fun `drill stops at bedrock without mining`() { val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) - drill.currentPower = 10 + drill.currentPower = 8 mockBlockAt(0, 63, 0, Material.BEDROCK) drill.callPowerUpdate() - assertEquals(10, drill.currentPower) // no power consumed + assertEquals(8, drill.currentPower) // no power consumed } @Test fun `drill mines horizontally facing NORTH`() { - val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.NORTH) - drill.currentPower = 10 + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.NORTH) + drill.currentPower = 8 // NORTH = z-1 mockBlockAt(0, 64, -1, Material.AIR) @@ -122,7 +135,7 @@ class SmallDrillMiningTest { @Test fun `drill respects 64-block horizontal range limit`() { val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.EAST) - drill.currentPower = 10 + drill.currentPower = 8 // All blocks in range are AIR for (i in 1..64) { @@ -131,7 +144,7 @@ class SmallDrillMiningTest { drill.callPowerUpdate() // No block to mine, power unchanged - assertEquals(10, drill.currentPower) + assertEquals(8, drill.currentPower) } @Test @@ -161,8 +174,8 @@ class SmallDrillMiningTest { @Test fun `drill mines horizontally facing SOUTH`() { - val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.SOUTH) - drill.currentPower = 10 + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.SOUTH) + drill.currentPower = 8 // SOUTH = z+1 mockBlockAt(0, 64, 1, Material.AIR) @@ -175,8 +188,8 @@ class SmallDrillMiningTest { @Test fun `drill mines horizontally facing EAST`() { - val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.EAST) - drill.currentPower = 10 + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.EAST) + drill.currentPower = 8 // EAST = x+1 val stoneBlock = mockBlockAt(1, 64, 0, Material.STONE) @@ -188,8 +201,8 @@ class SmallDrillMiningTest { @Test fun `drill mines horizontally facing WEST`() { - val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.WEST) - drill.currentPower = 10 + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.WEST) + drill.currentPower = 8 // WEST = x-1 val stoneBlock = mockBlockAt(-1, 64, 0, Material.STONE) @@ -202,18 +215,18 @@ class SmallDrillMiningTest { @Test fun `drill stops at bedrock in horizontal mining`() { val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.NORTH) - drill.currentPower = 10 + drill.currentPower = 8 mockBlockAt(0, 64, -1, Material.BEDROCK) drill.callPowerUpdate() - assertEquals(10, drill.currentPower) // no power consumed + assertEquals(8, drill.currentPower) // no power consumed } @Test fun `drill all-air column to minHeight does not mine`() { val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) - drill.currentPower = 10 + drill.currentPower = 8 // All air from y=63 down to minHeight=-64 for (y in 63 downTo -64) { @@ -221,14 +234,13 @@ class SmallDrillMiningTest { } drill.callPowerUpdate() - assertEquals(10, drill.currentPower) + assertEquals(8, drill.currentPower) } @Test fun `drill stops pulling power when full mid-loop`() { - val drillLoc = TestHelper.createLocation(0.0, 64.0, 0.0) - val drill = SmallDrill(drillLoc, BlockFace.DOWN) - drill.currentPower = 9 // needs 1 more to be full + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.DOWN) + drill.currentPower = 15 // needs 1 more to be full val source1 = SmallSolarPanel(TestHelper.createLocation(1.0, 64.0, 0.0)) source1.currentPower = 1 @@ -243,25 +255,36 @@ class SmallDrillMiningTest { val stoneBlock = mockBlockAt(0, 63, 0, Material.STONE) drill.callPowerUpdate() - // Drill pulls 1 power (to reach 10), then mines (uses 10, back to 0) - assertEquals(0, drill.currentPower) + // Drill pulls 1 power (to reach 16), then mines (uses 8, left with 8) + assertEquals(8, drill.currentPower) // One source should still have its power val totalRemaining = source1.currentPower + source2.currentPower assertEquals(1, totalRemaining) } + @Test + fun `drill calls getBlockDrops when mining`() { + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.DOWN) + drill.currentPower = 8 + + val stoneBlock = mockBlockAt(0, 63, 0, Material.STONE) + + drill.callPowerUpdate() + verify { drill.getBlockDrops(stoneBlock) } + } + @Test fun `drill facing UP uses DOWN branch logic - known bug`() { // Document known bug: UP falls into horizontal branch where modX=0, modZ=0 // This means it checks the same position (drill's own x,y,z) 64 times val drill = SmallDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.UP) - drill.currentPower = 10 + drill.currentPower = 8 // Since modX and modZ are both 0 for UP, getBlockAt will get (0, 64, 0) repeatedly val selfBlock = mockBlockAt(0, 64, 0, Material.AIR) drill.callPowerUpdate() - // No mining happens, power stays at 10 - assertEquals(10, drill.currentPower) + // No mining happens, power stays at 8 + assertEquals(8, drill.currentPower) } } diff --git a/src/test/kotlin/com/coderjoe/atlas/utility/SoftTouchDrillTest.kt b/src/test/kotlin/com/coderjoe/atlas/utility/SoftTouchDrillTest.kt new file mode 100644 index 0000000..8266a7b --- /dev/null +++ b/src/test/kotlin/com/coderjoe/atlas/utility/SoftTouchDrillTest.kt @@ -0,0 +1,115 @@ +package com.coderjoe.atlas.utility + +import com.coderjoe.atlas.TestHelper +import com.coderjoe.atlas.TestHelper.callPowerUpdate +import com.coderjoe.atlas.power.PowerBlockRegistry +import com.coderjoe.atlas.utility.block.SoftTouchDrill +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import org.bukkit.Material +import org.bukkit.block.Block +import org.bukkit.block.BlockFace +import org.bukkit.inventory.ItemStack +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class SoftTouchDrillTest { + private lateinit var registry: PowerBlockRegistry + + @BeforeEach + fun setup() { + TestHelper.setup() + registry = PowerBlockRegistry(TestHelper.mockPlugin) + } + + @AfterEach + fun teardown() { + TestHelper.teardown() + } + + private fun mockBlockAt( + x: Int, + y: Int, + z: Int, + material: Material, + ): Block { + val block = mockk(relaxed = true) + every { block.type } returns material + every { block.getDrops(any()) } returns emptyList() + every { TestHelper.mockWorld.getBlockAt(x, y, z) } returns block + return block + } + + private fun createDrill( + x: Double, + y: Double, + z: Double, + facing: BlockFace, + ): SoftTouchDrill { + val drill = spyk(SoftTouchDrill(TestHelper.createLocation(x, y, z), facing)) + every { drill.getBlockDrops(any()) } returns emptyList() + return drill + } + + @Test + fun `soft touch drill has max storage of 40`() { + val drill = SoftTouchDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) + assertEquals(40, drill.maxStorage) + } + + @Test + fun `soft touch drill requires 20 power to mine`() { + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.DOWN) + drill.currentPower = 19 + + mockBlockAt(0, 63, 0, Material.STONE) + + drill.callPowerUpdate() + assertEquals(19, drill.currentPower) + } + + @Test + fun `soft touch drill mines with 20 power`() { + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.DOWN) + drill.currentPower = 20 + + val stoneBlock = mockBlockAt(0, 63, 0, Material.STONE) + + drill.callPowerUpdate() + assertEquals(0, drill.currentPower) + verify { stoneBlock.setType(Material.AIR, false) } + } + + @Test + fun `soft touch drill calls getBlockDrops when mining`() { + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.DOWN) + drill.currentPower = 20 + + val stoneBlock = mockBlockAt(0, 63, 0, Material.STONE) + + drill.callPowerUpdate() + verify { drill.getBlockDrops(stoneBlock) } + } + + @Test + fun `soft touch drill has correct block id`() { + val drill = SoftTouchDrill(TestHelper.createLocation(0.0, 64.0, 0.0), BlockFace.DOWN) + assertEquals("atlas:soft_touch_drill", drill.baseBlockId) + } + + @Test + fun `soft touch drill mines horizontally`() { + val drill = createDrill(0.0, 64.0, 0.0, BlockFace.NORTH) + drill.currentPower = 20 + + val stoneBlock = mockBlockAt(0, 64, -1, Material.STONE) + + drill.callPowerUpdate() + assertEquals(0, drill.currentPower) + verify { stoneBlock.setType(Material.AIR, false) } + } +}