Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/kotlin/com/coderjoe/atlas/Atlas.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ 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.ExperienceExtractor
import com.coderjoe.atlas.utility.block.ObsidianFactory
import com.coderjoe.atlas.utility.block.SmallDrill
import com.coderjoe.atlas.utility.block.SoftTouchDrill
Expand Down Expand Up @@ -214,6 +215,7 @@ class Atlas : JavaPlugin() {
Crusher.descriptor,
PowerMerger.descriptor,
SoftTouchDrill.descriptor,
ExperienceExtractor.descriptor,
).associateBy { it.baseBlockId }
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/kotlin/com/coderjoe/atlas/fluid/FluidBlockDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,15 @@ object FluidBlockDialog {
when (fluidBlock.storedFluid) {
FluidType.WATER -> "Water (${fluidBlock.storedAmount}/${FluidContainer.MAX_CAPACITY})"
FluidType.LAVA -> "Lava (${fluidBlock.storedAmount}/${FluidContainer.MAX_CAPACITY})"
FluidType.EXPERIENCE ->
"Liquid Experience (${fluidBlock.storedAmount}/${FluidContainer.MAX_CAPACITY})"
FluidType.NONE -> "Empty"
}
} else {
when (fluidBlock.storedFluid) {
FluidType.WATER -> "Water"
FluidType.LAVA -> "Lava"
FluidType.EXPERIENCE -> "Liquid Experience"
FluidType.NONE -> "Empty"
}
}
Expand All @@ -74,6 +77,7 @@ object FluidBlockDialog {
when (fluidBlock.storedFluid) {
FluidType.WATER -> NamedTextColor.AQUA
FluidType.LAVA -> NamedTextColor.GOLD
FluidType.EXPERIENCE -> NamedTextColor.GREEN
FluidType.NONE -> NamedTextColor.GRAY
}

Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/com/coderjoe/atlas/fluid/FluidType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ package com.coderjoe.atlas.fluid
enum class FluidType {
WATER,
LAVA,
EXPERIENCE,
NONE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class FluidPump(location: Location) : FluidBlock(location) {
when (storedFluid) {
FluidType.WATER -> BLOCK_ID_ACTIVE
FluidType.LAVA -> BLOCK_ID_ACTIVE_LAVA
FluidType.EXPERIENCE -> BLOCK_ID_ACTIVE
FluidType.NONE -> BLOCK_ID
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/kotlin/com/coderjoe/atlas/power/PowerBlockDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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.ExperienceExtractor
import com.coderjoe.atlas.utility.block.ObsidianFactory
import com.coderjoe.atlas.utility.block.SmallDrill
import com.coderjoe.atlas.utility.block.SoftTouchDrill
Expand Down Expand Up @@ -136,6 +137,7 @@ object PowerBlockDialog {
is ObsidianFactory -> "Obsidian Factory"
is Crusher -> "Crusher (${powerBlock.facing.displayName()})"
is PowerMerger -> "Power Merger (${powerBlock.facing.displayName()})"
is ExperienceExtractor -> "Experience Extractor (${powerBlock.facing.displayName()})"
else -> "Power Block"
}

Expand Down Expand Up @@ -215,6 +217,12 @@ object PowerBlockDialog {
is PowerMerger ->
Component.text("Cable - merges power from all sides, outputs in facing direction")
.color(NamedTextColor.GRAY)
is ExperienceExtractor ->
Component.text(
"Machine - extracts XP from items via hopper, outputs Liquid Experience, " +
"consumes ${ExperienceExtractor.POWER_PER_EXTRACT} power/item",
)
.color(NamedTextColor.GRAY)
else ->
Component.text("Power block")
.color(NamedTextColor.GRAY)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.coderjoe.atlas.power

import com.coderjoe.atlas.core.BlockPersistence
import com.coderjoe.atlas.utility.block.ExperienceExtractor
import com.coderjoe.atlas.utility.block.SmallDrill
import org.bukkit.plugin.java.JavaPlugin

Expand All @@ -19,6 +20,9 @@ class PowerBlockPersistence(plugin: JavaPlugin) {
if (block is SmallDrill) {
map["enabled"] = block.enabled
}
if (block is ExperienceExtractor) {
map["storedXp"] = block.storedXp
}
map
},
restore = { block, data ->
Expand All @@ -29,6 +33,9 @@ class PowerBlockPersistence(plugin: JavaPlugin) {
block.enabled = enabled
}
}
if (block is ExperienceExtractor) {
block.storedXp = (data["storedXp"] as? Number)?.toDouble() ?: 0.0
}
},
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.coderjoe.atlas.utility.block

import com.coderjoe.atlas.atlasInfo
import com.coderjoe.atlas.coordinates
import com.coderjoe.atlas.core.BlockDescriptor
import com.coderjoe.atlas.core.PlacementType
import com.coderjoe.atlas.fluid.FluidBlockRegistry
import com.coderjoe.atlas.fluid.FluidType
import com.coderjoe.atlas.power.PowerBlock
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.block.BlockFace
import org.bukkit.block.Hopper

class ExperienceExtractor(
location: Location,
facing: BlockFace = BlockFace.NORTH,
) : PowerBlock(location, maxStorage = 12) {
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

var storedXp: Double = 0.0

companion object {
const val BLOCK_ID = "atlas:experience_extractor"
const val BLOCK_ID_ACTIVE = "atlas:experience_extractor_active"
const val POWER_PER_EXTRACT = 3
const val MAX_XP_BUFFER = 10.0
const val DEFAULT_XP = 0.01

val XP_VALUES: Map<Material, Double> =
mapOf(
// Ores - 1 XP
Material.COAL_ORE to 1.0,
Material.DEEPSLATE_COAL_ORE to 1.0,
Material.IRON_ORE to 1.0,
Material.DEEPSLATE_IRON_ORE to 1.0,
Material.COPPER_ORE to 1.0,
Material.DEEPSLATE_COPPER_ORE to 1.0,
Material.GOLD_ORE to 1.0,
Material.DEEPSLATE_GOLD_ORE to 1.0,
Material.NETHER_GOLD_ORE to 1.0,
Material.REDSTONE_ORE to 1.0,
Material.DEEPSLATE_REDSTONE_ORE to 1.0,
Material.LAPIS_ORE to 1.0,
Material.DEEPSLATE_LAPIS_ORE to 1.0,
Material.NETHER_QUARTZ_ORE to 1.0,
// Raw ores & minerals - 1 XP
Material.RAW_IRON to 1.0,
Material.RAW_GOLD to 1.0,
Material.RAW_COPPER to 1.0,
Material.COAL to 1.0,
Material.CHARCOAL to 1.0,
Material.LAPIS_LAZULI to 1.0,
Material.REDSTONE to 1.0,
Material.QUARTZ to 1.0,
// Cooked food - 2 XP
Material.COOKED_BEEF to 2.0,
Material.COOKED_PORKCHOP to 2.0,
Material.COOKED_CHICKEN to 2.0,
Material.COOKED_MUTTON to 2.0,
Material.COOKED_RABBIT to 2.0,
Material.COOKED_SALMON to 2.0,
Material.COOKED_COD to 2.0,
Material.BAKED_POTATO to 2.0,
// Monster drops - 3 XP
Material.BONE to 3.0,
Material.GUNPOWDER to 3.0,
Material.SPIDER_EYE to 3.0,
Material.ROTTEN_FLESH to 3.0,
Material.SLIME_BALL to 3.0,
Material.MAGMA_CREAM to 3.0,
Material.PHANTOM_MEMBRANE to 3.0,
Material.GHAST_TEAR to 3.0,
// Valuable ores & drops - 5 XP
Material.DIAMOND_ORE to 5.0,
Material.DEEPSLATE_DIAMOND_ORE to 5.0,
Material.EMERALD_ORE to 5.0,
Material.DEEPSLATE_EMERALD_ORE to 5.0,
Material.BLAZE_ROD to 5.0,
Material.ENDER_PEARL to 5.0,
Material.DIAMOND to 5.0,
Material.EMERALD to 5.0,
Material.SHULKER_SHELL to 5.0,
// Very valuable - 8 XP
Material.NETHER_STAR to 8.0,
Material.ENCHANTED_BOOK to 8.0,
)

val descriptor =
BlockDescriptor(
baseBlockId = BLOCK_ID,
displayName = "Experience Extractor",
description = "Extracts XP from items via hopper, outputs Liquid Experience fluid",
placementType = PlacementType.DIRECTIONAL,
additionalBlockIds = listOf(BLOCK_ID_ACTIVE),
constructor = { loc, face -> ExperienceExtractor(loc, face) },
)
}

override fun getVisualStateBlockId(): String =
when {
storedXp > 0.0 || currentPower > 0 -> BLOCK_ID_ACTIVE
else -> BLOCK_ID
}

override fun powerUpdate() {
pullPowerFromNeighbors()

pushXpFluid()
pullFromHoppers()

updatePoweredState()
}

private fun pushXpFluid() {
if (storedXp < 1.0) return

val fluidRegistry = FluidBlockRegistry.instance ?: return
val target = fluidRegistry.getAdjacentFluidBlock(location, facing) ?: return

if (target.storeFluid(FluidType.EXPERIENCE)) {
storedXp -= 1.0
plugin.logger.atlasInfo(
"ExperienceExtractor at ${location.coordinates} " +
"pushed 1 experience fluid (buffer: $storedXp)",
)
}
}

private fun pullFromHoppers() {
if (storedXp >= MAX_XP_BUFFER) return
if (currentPower < POWER_PER_EXTRACT) return

val world = location.world ?: return

for (face in ADJACENT_FACES) {
if (storedXp >= MAX_XP_BUFFER) break
if (currentPower < POWER_PER_EXTRACT) break

val adjacentBlock =
world.getBlockAt(
location.blockX + face.modX,
location.blockY + face.modY,
location.blockZ + face.modZ,
)

val hopper = adjacentBlock.state as? Hopper ?: continue
val inventory = hopper.inventory

for (slot in 0 until inventory.size) {
if (storedXp >= MAX_XP_BUFFER) break
if (currentPower < POWER_PER_EXTRACT) break

val stack = inventory.getItem(slot) ?: continue
if (stack.type == Material.AIR) continue
val xpValue = XP_VALUES[stack.type] ?: DEFAULT_XP

removePower(POWER_PER_EXTRACT)
storedXp += xpValue

if (stack.amount > 1) {
stack.amount = stack.amount - 1
inventory.setItem(slot, stack)
} else {
inventory.setItem(slot, null)
}

plugin.logger.atlasInfo(
"ExperienceExtractor at ${location.coordinates} " +
"consumed ${stack.type.name}, gained $xpValue XP (buffer: $storedXp)",
)
}
}
}
}
Loading
Loading