diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index b60a158..43c576c 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { library(kotlin("stdlib")) libraryAndTest("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0-RC2") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") // For some reason libraryloader doesn't like this + libraryAndTest(kotlin("reflect")) libraryAndTest(kotlin("scripting-common")) libraryAndTest(kotlin("scripting-jvm")) diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/atmosphere/Gas.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/atmosphere/Gas.kt index 8c9ecb3..44c409e 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/atmosphere/Gas.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/objects/properties/atmosphere/Gas.kt @@ -1,5 +1,7 @@ package io.github.addoncommunity.galactifun.api.objects.properties.atmosphere +import io.github.addoncommunity.galactifun.units.Density +import io.github.addoncommunity.galactifun.units.Density.Companion.kilogramsPerLiter import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType @@ -9,22 +11,22 @@ import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils import org.bukkit.inventory.ItemStack /** - * @property liquidDensity density of the gas in the liquid state, in kg/l + * @property liquidDensity density of the gas in the liquid state */ -enum class Gas(texture: String?, val liquidDensity: Double) { - OXYGEN("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 1.146), - NITROGEN("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 0.811), - CARBON_DIOXIDE("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 0.002), - WATER("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 0.997), - HELIUM("93dfa904fe3d0306666a573c22eec1dd0a8051e32a38ea2d19c4b5867e232a49", 0.141), - ARGON("ea005531b6167a86fb09d6c0f3db60f2650162d0656c2908d07b377111d8f2a2", 1.401), - METHANE("ea005531b6167a86fb09d6c0f3db60f2650162d0656c2908d07b377111d8f2a2", 0.424), +enum class Gas(texture: String?, val liquidDensity: Density) { + OXYGEN("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 1.146.kilogramsPerLiter), + NITROGEN("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 0.811.kilogramsPerLiter), + CARBON_DIOXIDE("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 0.002.kilogramsPerLiter), + WATER("5b3ad76aadb80ecf4b4cdbe76b8704b0f2dc090b49b65c36d87ed879f1065ef2", 0.997.kilogramsPerLiter), + HELIUM("93dfa904fe3d0306666a573c22eec1dd0a8051e32a38ea2d19c4b5867e232a49", 0.141.kilogramsPerLiter), + ARGON("ea005531b6167a86fb09d6c0f3db60f2650162d0656c2908d07b377111d8f2a2", 1.401.kilogramsPerLiter), + METHANE("ea005531b6167a86fb09d6c0f3db60f2650162d0656c2908d07b377111d8f2a2", 0.424.kilogramsPerLiter), // Using hexane for reference density - HYDROCARBONS("725691372e0734bfb57bb03690490661a83f053a3488860df3436ce1caa24d11", 0.655), - HYDROGEN("725691372e0734bfb57bb03690490661a83f053a3488860df3436ce1caa24d11", 0.072), - SULFUR("c7a1ece691ad28d17bbbcecb22270c85e1c9581485806264c676de67c272e2d0", 1.819), - AMMONIA("c7a1ece691ad28d17bbbcecb22270c85e1c9581485806264c676de67c272e2d0", 0.683), - OTHER(null, Double.NaN); + HYDROCARBONS("725691372e0734bfb57bb03690490661a83f053a3488860df3436ce1caa24d11", 0.655.kilogramsPerLiter), + HYDROGEN("725691372e0734bfb57bb03690490661a83f053a3488860df3436ce1caa24d11", 0.072.kilogramsPerLiter), + SULFUR("c7a1ece691ad28d17bbbcecb22270c85e1c9581485806264c676de67c272e2d0", 1.819.kilogramsPerLiter), + AMMONIA("c7a1ece691ad28d17bbbcecb22270c85e1c9581485806264c676de67c272e2d0", 0.683.kilogramsPerLiter), + OTHER(null, Double.NaN.kilogramsPerLiter); val item = texture?.let { SlimefunItemStack( diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/rockets/RocketInfo.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/rockets/RocketInfo.kt index 885b1a4..1659209 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/rockets/RocketInfo.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/api/rockets/RocketInfo.kt @@ -1,46 +1,101 @@ package io.github.addoncommunity.galactifun.api.rockets +import io.github.addoncommunity.galactifun.Constants import io.github.addoncommunity.galactifun.api.objects.properties.atmosphere.Gas +import io.github.addoncommunity.galactifun.impl.items.FuelTank import io.github.addoncommunity.galactifun.impl.items.RocketEngine import io.github.addoncommunity.galactifun.impl.managers.PlanetManager -import io.github.addoncommunity.galactifun.units.Acceleration -import io.github.addoncommunity.galactifun.units.Force -import io.github.addoncommunity.galactifun.units.Mass -import io.github.addoncommunity.galactifun.units.times -import io.github.addoncommunity.galactifun.util.items.mass +import io.github.addoncommunity.galactifun.units.* +import io.github.addoncommunity.galactifun.util.general.mergeMaps +import io.github.addoncommunity.galactifun.util.items.wetMass +import io.github.addoncommunity.galactifun.util.processSlimefunBlocks import io.github.thebusybiscuit.slimefun4.libraries.dough.blocks.BlockPosition +import kotlin.math.ln +import kotlin.time.Duration.Companion.seconds -data class RocketInfo( +class RocketInfo( val commandComputer: BlockPosition, val blocks: Set, - val engines: List>> + engineData: Map, Set> ) { - val thrust: Force - get() = engines.fold(Force.ZERO) { acc, engine -> acc + engine.first.thrust } - val mass: Mass - get() = blocks.fold(Mass.ZERO) { acc, block -> acc + block.block.mass } - fun twr(gravity: Acceleration): Double { - if (gravity == Acceleration.ZERO) return Double.POSITIVE_INFINITY - return thrust / (mass * gravity) - } + val engines = engineData.keys.map { it.first } + + val thrust = engines.sumBy { it.thrust } + val wetMass = blocks.sumBy { it.block.wetMass } + + val stages: List + + init { + stages = if (engineData.isEmpty()) { + emptyList() + } else { + val sortedEngines = engineData.toList().sortedBy { it.first.second.y } + val stageList = mutableListOf() - val fuel: List, Map>> - get() { - TODO() + for ((engine, fuelBlocks) in sortedEngines) { + val engines = stageList.flatMap { it.engines }.map { it.second } + if (stageList.any { engine.second in engines }) continue + val stage = sortedEngines.filter { it.second == fuelBlocks }.map { it.first } + stageList.add(Stage(stage, fuelBlocks)) + } + + stageList } + } - val info: String - get() = buildString { - val planet = PlanetManager.getByWorld(commandComputer.world) ?: error("Planet not found") - appendLine("Fuel:") - for ((gas, amount) in fuel) { - append(" ".repeat(4)) - append(gas) - //appendLine(": %.2f l, %.2f kg".format(amount, amount * gas.liquidDensity)) + val dryMass = wetMass - stages.sumBy { it.fuelMass } + + val info = buildString { + val planet = PlanetManager.getByWorld(commandComputer.world) ?: error("Planet not found") + appendLine("Stages:") + var stageNum = 1 + for (stage in stages) { + appendLine(" Stage ${stageNum++}: ") + appendLine(" Fuel:") + for ((gas, volume) in stage.fuel) { + appendLine(" $gas: %.2f liters, %.2s".format(volume.liters, volume * gas.liquidDensity)) + } + appendLine(" Engines:") + var engineNum = 1 + for (engine in stage.engines) { + appendLine(" Engine ${engineNum++}: %.2f kilonewtons".format(engine.first.thrust.kilonewtons)) } - appendLine("Thrust: %.2f kN".format(thrust.kilonewtons)) - appendLine("Mass: %.2f kg".format(mass.kilograms)) - appendLine("TWR: %.2f".format(twr(planet.gravity))) + appendLine(" Delta-V: %.2f m/s".format(stage.deltaV.metersPerSecond)) } + appendLine("Thrust: %.2f kilonewtons".format(thrust.kilonewtons)) + appendLine("Wet mass: %.2s".format(wetMass)) + appendLine("Dry mass: %.2s".format(dryMass)) + appendLine("TWR: %.2f".format(twr(planet.gravity))) + appendLine("Delta-V: %.2f m/s".format(deltaV(engines, wetMass, dryMass).metersPerSecond)) + } + + fun twr(gravity: Acceleration): Double { + if (gravity == Acceleration.ZERO) return Double.POSITIVE_INFINITY + return thrust / (wetMass * gravity) + } + + inner class Stage( + val engines: List>, + val fuelBlocks: Set + ) { + val fuel: Map = fuelBlocks.processSlimefunBlocks(FuelTank::getFuelLevel) + .fold(mapOf()) { acc, fuel -> acc.mergeMaps(fuel, Volume::plus) } + + val fuelMass = fuel.toList().sumBy { (gas, volume) -> gas.liquidDensity * volume } + + val deltaV = deltaV( + engines.map { it.first }, + wetMass, + wetMass - fuelMass + ) + } +} + +private fun deltaV(engines: List, wetMass: Mass, dryMass: Mass): Velocity { + val ispNeum = engines.sumBy { it.thrust }.newtons + val ispDenom = engines.sumOf { it.thrust.newtons / it.specificImpulse.doubleSeconds } + val isp = (ispNeum / ispDenom).seconds + + return Constants.EARTH_GRAVITY * isp * ln(wetMass / dryMass) } diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/blocks/CustomMass.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/blocks/CustomMass.kt new file mode 100644 index 0000000..81afa39 --- /dev/null +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/blocks/CustomMass.kt @@ -0,0 +1,10 @@ +package io.github.addoncommunity.galactifun.blocks + +import io.github.addoncommunity.galactifun.units.Mass +import org.bukkit.block.Block + +interface CustomMass { + fun getMass(block: Block): Mass + + fun getWetMass(block: Block): Mass = getMass(block) +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/blocks/MassedBlock.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/blocks/MassedBlock.kt new file mode 100644 index 0000000..a99babb --- /dev/null +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/blocks/MassedBlock.kt @@ -0,0 +1,27 @@ +package io.github.addoncommunity.galactifun.blocks + +import io.github.addoncommunity.galactifun.units.Mass +import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack +import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler +import org.bukkit.block.Block +import org.bukkit.inventory.ItemStack + +open class BasicMassedBlock( + itemGroup: ItemGroup, + item: SlimefunItemStack, + recipeType: RecipeType, + recipe: Array, + private val mass: Mass +) : SlimefunItem(itemGroup, item, recipeType, recipe), CustomMass { + + init { + addItemHandler(BlockUseHandler { + it.cancel() + }) + } + + override fun getMass(block: Block): Mass = mass +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt index 1b5dfaf..2ee874c 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/GalactifunItems.kt @@ -3,30 +3,57 @@ package io.github.addoncommunity.galactifun.impl import io.github.addoncommunity.galactifun.api.objects.properties.atmosphere.Gas import io.github.addoncommunity.galactifun.impl.items.CommandComputer import io.github.addoncommunity.galactifun.impl.items.FuelTank +import io.github.addoncommunity.galactifun.impl.items.RocketEngine import io.github.addoncommunity.galactifun.pluginInstance +import io.github.addoncommunity.galactifun.units.Force.Companion.kilonewtons +import io.github.addoncommunity.galactifun.units.Volume.Companion.liters import io.github.addoncommunity.galactifun.util.items.MaterialType import io.github.addoncommunity.galactifun.util.items.buildSlimefunItem import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType import org.bukkit.Material +import kotlin.time.Duration.Companion.seconds object GalactifunItems { val COMMAND_COMPUTER = buildSlimefunItem { category = GalactifunCategories.ROCKET_COMPONENTS id = "COMMAND_COMPUTER" - name = "&fCommand Computer" + name = "Command Computer" material = MaterialType.Material(Material.CHISELED_QUARTZ_BLOCK) recipeType = RecipeType.NULL recipe = emptyArray() + + +"&7The core for any rocket" } - val FUEL_TANK_I = buildSlimefunItem(1000.0) { + val FUEL_TANK_I = buildSlimefunItem(1000.liters) { category = GalactifunCategories.ROCKET_COMPONENTS id = "FUEL_TANK_I" - name = "&fFuel Tank I" + name = "TNK-1000 \"Big Boy\"" material = MaterialType.Material(Material.IRON_BLOCK) recipeType = RecipeType.NULL recipe = emptyArray() + + +"Definitely not a tin can" + +"" + +"Manufacturer: Found by the side of the road" + +"Capacity: 1000 L" + } + + val ROCKET_ENGINE_I = buildSlimefunItem(300.seconds, 100.kilonewtons, Gas.HYDROGEN) { + category = GalactifunCategories.ROCKET_COMPONENTS + id = "ROCKET_ENGINE_I" + name = "BMR-50 \"Lil' Boomer\"" + material = MaterialType.Material(Material.FURNACE) + recipeType = RecipeType.NULL + recipe = emptyArray() + + +"Not to be used for grilling burgers" + +"" + +"Manufacturer: Boomer & Bros. Explosives, Inc." + +"Thrust: 100 kN" + +"Specific impulse: 300 s" + +"Fuel: Hydrogen" } init { diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt index 4ad9134..9063f99 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/CommandComputer.kt @@ -6,17 +6,16 @@ import io.github.addoncommunity.galactifun.util.checkBlock import io.github.addoncommunity.galactifun.util.floodSearch import io.github.addoncommunity.galactifun.util.items.TickingBlock import io.github.addoncommunity.galactifun.util.plus +import io.github.addoncommunity.galactifun.util.processSlimefunBlocks import io.github.seggan.sf4k.location.position import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup -import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler import io.github.thebusybiscuit.slimefun4.libraries.dough.blocks.BlockPosition import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap -import me.mrCookieSlime.Slimefun.api.BlockStorage import net.kyori.adventure.text.format.NamedTextColor import org.bukkit.block.Block import org.bukkit.event.block.BlockPlaceEvent @@ -51,12 +50,14 @@ class CommandComputer( } private fun rescanRocket(pos: BlockPosition) { - val rocketBlocks = pos.floodSearch { this.checkBlock() != null } + val rocketBlocks = pos.floodSearch { this.checkBlock() == null } val detected = if (!rocketBlocks.exceededMax) rocketBlocks.found else emptySet() val blocks = detected.map(BlockPosition::getBlock) - val engines = blocks.processSlimefunBlocks { - this to it.position.floodSearch { this.checkBlock() != null }.found - } + val engines = blocks.processSlimefunBlocks { b -> + val fuel = b.position.floodSearch { it.checkBlock() != null }.found.toMutableSet() + fuel.remove(b.position) + (this to b.position) to fuel + }.toMap() RocketManager.register(RocketInfo(pos, detected, engines)) } @@ -70,17 +71,4 @@ class CommandComputer( } e.player.sendMessage(NamedTextColor.GOLD + info.info) } -} - -private inline fun Iterable.processSlimefunBlocks( - processor: S.(Block) -> T -): List { - val result = mutableListOf() - for (b in this) { - val item = BlockStorage.check(b) - if (item is S) { - result.add(item.processor(b)) - } - } - return result } \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/FuelTank.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/FuelTank.kt index 8de2f19..62213bd 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/FuelTank.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/FuelTank.kt @@ -1,6 +1,13 @@ package io.github.addoncommunity.galactifun.impl.items import io.github.addoncommunity.galactifun.api.objects.properties.atmosphere.Gas +import io.github.addoncommunity.galactifun.blocks.CustomMass +import io.github.addoncommunity.galactifun.units.Mass +import io.github.addoncommunity.galactifun.units.Mass.Companion.kilograms +import io.github.addoncommunity.galactifun.units.Volume +import io.github.addoncommunity.galactifun.units.Volume.Companion.liters +import io.github.addoncommunity.galactifun.units.sumBy +import io.github.addoncommunity.galactifun.units.times import io.github.addoncommunity.galactifun.util.adjacentFaces import io.github.addoncommunity.galactifun.util.checkBlock import io.github.addoncommunity.galactifun.util.general.enumMapOf @@ -20,8 +27,8 @@ class FuelTank( item: SlimefunItemStack, recipeType: RecipeType, recipe: Array, - private val capacity: Double -) : TickingBlock(itemGroup, item, recipeType, recipe) { + private val capacity: Volume +) : TickingBlock(itemGroup, item, recipeType, recipe), CustomMass { private companion object { private const val INPUT = 4 @@ -45,7 +52,7 @@ class FuelTank( val consumed = item.amount.coerceAtMost(8) menu.consumeItem(INPUT, consumed) val fuel = getFuelLevel(b).toMutableMap() - fuel.merge(gasItem.gas, consumed.toDouble(), Double::plus) + fuel.merge(gasItem.gas, consumed.liters, Volume::plus) setFuelLevel(b, fuel) } @@ -57,27 +64,27 @@ class FuelTank( var distributable = blocks.size val fuels = blocks .map(::getFuelLevel) - .reduce { acc, map -> acc.mergeMaps(map, Double::plus) } + .reduce { acc, map -> acc.mergeMaps(map, Volume::plus) } .mapValuesTo(enumMapOf()) { (_, amount) -> amount / distributable } for (block in blocks) { val tank = BlockStorage.check(block) as FuelTank val cap = tank.capacity - val toAdd = if (result.values.sum() > cap) { + val toAdd = if (result.values.sum() > cap.liters) { distributable-- - val stuffed = enumMapOf() + val stuffed = enumMapOf() for ((gas, amount) in fuels) { - val space = cap - stuffed.values.sum() + val space = cap - stuffed.values.sumOf { it.liters }.liters val toAdd = amount.coerceAtMost(space) - stuffed.merge(gas, toAdd, Double::plus) + stuffed.merge(gas, toAdd, Volume::plus) } val notStuffed = fuels - .mapValues { (gas, amount) -> amount - stuffed.getOrDefault(gas, 0.0) } - .filterValues { it > 0 } + .mapValues { (gas, amount) -> amount - stuffed.getOrDefault(gas, Volume.ZERO) } + .filterValues { it > Volume.ZERO } .mapValues { (_, amount) -> amount / distributable } for ((gas, amount) in notStuffed) { - fuels.merge(gas, amount, Double::plus) + fuels.merge(gas, amount, Volume::plus) } stuffed @@ -90,12 +97,20 @@ class FuelTank( } } - fun getFuelLevel(block: Block): Map { - return block.getBlockStorage("fuel", fuelDataType) ?: emptyMap() + fun getFuelLevel(block: Block): Map { + return block.getBlockStorage("fuel", fuelDataType)?.mapValues { it.value.liters } ?: emptyMap() } - fun setFuelLevel(block: Block, fuel: Map) { - block.setBlockStorage("fuel", fuel, fuelDataType) + fun setFuelLevel(block: Block, fuel: Map) { + block.setBlockStorage("fuel", fuel.mapValues { it.value.liters }, fuelDataType) + } + + override fun getMass(block: Block): Mass = 729.kilograms // based on a 1 m^3 tank with 0.5 m thick walls of aluminum + + override fun getWetMass(block: Block): Mass { + val fuelMass = getFuelLevel(block).toList() + .sumBy { (gas, amount) -> gas.liquidDensity * amount } + return getMass(block) + fuelMass } override fun preRegister() { diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/RocketEngine.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/RocketEngine.kt index 3331a6d..410e503 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/RocketEngine.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/impl/items/RocketEngine.kt @@ -6,6 +6,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler import org.bukkit.inventory.ItemStack import kotlin.time.Duration @@ -17,4 +18,11 @@ class RocketEngine( val specificImpulse: Duration, val thrust: Force, val fuel: Gas -) : SlimefunItem(itemGroup, item, recipeType, recipe) \ No newline at end of file +) : SlimefunItem(itemGroup, item, recipeType, recipe) { + + init { + addItemHandler(BlockUseHandler { + it.cancel() + }) + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Time.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Time.kt index a371f5b..d582ff6 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Time.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/Time.kt @@ -11,4 +11,6 @@ inline val Int.years: Duration get() = this.toDouble().years inline val Duration.doubleSeconds: Double - get() = toDouble(DurationUnit.SECONDS) \ No newline at end of file + get() = toDouble(DurationUnit.SECONDS) + +fun Iterable.sumBy(): Duration = fold(Duration.ZERO) { acc, duration -> acc + duration } \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt index b71c23b..2bc0cad 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/units/UnitsOfMeasure.kt @@ -20,6 +20,7 @@ private class ADistance @Measure(base = "kilograms") @AlternateUnit(unit = "tons", ratio = 1000.0) +@DividesTo(other = AVolume::class, quotient = ADensity::class) private class AMass @Measure(base = "metersPerSecond") @@ -37,4 +38,12 @@ operator fun Acceleration.times(time: Duration): Velocity = (metersPerSecondSqua @AlternateUnit(unit = "kilonewtons", ratio = 1000.0) @AlternateUnit(unit = "meganewtons", ratio = 1_000_000.0) @DividesTo(other = AAcceleration::class, quotient = AMass::class) -private class AForce \ No newline at end of file +private class AForce + +@Measure(base = "liters") +@AlternateUnit(unit = "cubicMeters", ratio = 1000.0) +private class AVolume + +@Measure(base = "kilogramsPerLiter") +@MultipliesTo(other = AVolume::class, product = AMass::class) +private class ADensity \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/BlockPosUtils.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/BlockPosUtils.kt index 56fa362..e8f31be 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/BlockPosUtils.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/BlockPosUtils.kt @@ -17,7 +17,11 @@ val adjacentFaces = arrayOf( BlockFace.WEST ) -inline fun BlockPosition.floodSearch(searchLimit: Int = 1024, predicate: Block.(Block) -> Boolean): FloodSearchResult { +inline fun BlockPosition.floodSearch( + searchLimit: Int = 1024, + ignoreAir: Boolean = true, + predicate: Block.(Block) -> Boolean +): FloodSearchResult { var toSearch = mutableSetOf(this) var toSearchNext = mutableSetOf() val found = mutableSetOf() @@ -36,7 +40,9 @@ inline fun BlockPosition.floodSearch(searchLimit: Int = 1024, predicate: Block.( val next = pos.getFace(face) if (next !in found && next !in toSearch && next !in toSearchNext) { try { - if (block.predicate(next.block)) { + val nextBlock = next.block + if (ignoreAir && nextBlock.type.isAir) continue + if (block.predicate(nextBlock)) { toSearchNext.add(next) } } catch (e: IllegalArgumentException) { diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/McUtils.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/McUtils.kt index 4c5cf79..5668976 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/McUtils.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/McUtils.kt @@ -4,6 +4,8 @@ import io.github.addoncommunity.galactifun.pluginInstance import net.kyori.adventure.text.Component import net.kyori.adventure.text.TextComponent import net.kyori.adventure.text.format.TextColor +import net.kyori.adventure.text.minimessage.MiniMessage +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer import org.bukkit.* import org.bukkit.entity.Entity import org.bukkit.event.player.PlayerTeleportEvent @@ -51,3 +53,6 @@ fun Entity.galactifunTeleport( operator fun TextColor.plus(s: String): TextComponent = Component.text().color(this).content(s).build() operator fun Tag.contains(item: T): Boolean = isTagged(item) + +fun String.miniMessageToLegacy(): String = + LegacyComponentSerializer.legacyAmpersand().serialize(MiniMessage.miniMessage().deserialize(this)) diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt index 88893ea..abd4497 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/SfUtils.kt @@ -1,6 +1,7 @@ package io.github.addoncommunity.galactifun.util import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem +import io.github.thebusybiscuit.slimefun4.libraries.dough.blocks.BlockPosition import io.github.thebusybiscuit.slimefun4.libraries.dough.collections.RandomizedSet import me.mrCookieSlime.Slimefun.api.BlockStorage import org.bukkit.Location @@ -20,4 +21,22 @@ inline fun Location.checkBlock(): Block? { inline fun Block.checkBlock(): Block? = location.checkBlock() inline fun buildRandomizedSet(builder: RandomizedSet.() -> Unit): RandomizedSet = - RandomizedSet().apply(builder) \ No newline at end of file + RandomizedSet().apply(builder) + +inline fun Iterable.processSlimefunBlocks( + processor: S.(Block) -> T +): List { + val result = mutableListOf() + for (b in this) { + val item = BlockStorage.check(b) + if (item is S) { + result.add(item.processor(b)) + } + } + return result +} + +@JvmName("processSlimefunBlocksPosition") +inline fun Iterable.processSlimefunBlocks( + processor: S.(Block) -> T +): List = map(BlockPosition::getBlock).processSlimefunBlocks(processor) diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt index 4aabc1f..83caacd 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/ItemBuilder.kt @@ -2,18 +2,21 @@ package io.github.addoncommunity.galactifun.util.items import io.github.addoncommunity.galactifun.pluginInstance import io.github.addoncommunity.galactifun.util.general.RequiredProperty +import io.github.addoncommunity.galactifun.util.miniMessageToLegacy import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils import org.bukkit.inventory.ItemStack +import kotlin.reflect.KClass +import kotlin.reflect.full.primaryConstructor class ItemBuilder { var name: String by RequiredProperty() var material: MaterialType by RequiredProperty() - var id: String by RequiredProperty(setter = { "AUTOMATION_" + it.uppercase() }) + var id: String by RequiredProperty(setter = { "GF2_" + it.uppercase() }) var category: ItemGroup by RequiredProperty() var recipeType: RecipeType by RequiredProperty() @@ -22,29 +25,22 @@ class ItemBuilder { private val lore = mutableListOf() operator fun String.unaryPlus() { - lore += this + lore += this.miniMessageToLegacy() } - fun build(clazz: Class, vararg otherArgs: Any?): SlimefunItemStack { + fun build(clazz: KClass, vararg otherArgs: Any?): SlimefunItemStack { val sfi = SlimefunItemStack( id, material.convert(), - name, + name.miniMessageToLegacy(), *lore.toTypedArray() ) - val constructor = clazz.getDeclaredConstructor( - ItemGroup::class.java, - SlimefunItemStack::class.java, - RecipeType::class.java, - Array::class.java, - *otherArgs.map { it!!::class.javaPrimitiveType ?: it::class.java }.toTypedArray() - ) - constructor.newInstance(category, sfi, recipeType, recipe, *otherArgs).register(pluginInstance) + val constructor = clazz.primaryConstructor ?: error("Primary constructor not found for $clazz") + constructor.call(category, sfi, recipeType, recipe, *otherArgs).register(pluginInstance) return sfi } } - sealed interface MaterialType { fun convert(): org.bukkit.inventory.ItemStack @@ -66,11 +62,11 @@ inline fun buildSlimefunItem( vararg otherArgs: Any?, builder: ItemBuilder.() -> Unit ): SlimefunItemStack { - return ItemBuilder().apply(builder).build(I::class.java, *otherArgs) + return ItemBuilder().apply(builder).build(I::class, *otherArgs) } inline fun buildSlimefunItem( builder: ItemBuilder.() -> Unit ): SlimefunItemStack { - return ItemBuilder().apply(builder).build(SlimefunItem::class.java) + return ItemBuilder().apply(builder).build(SlimefunItem::class) } \ No newline at end of file diff --git a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/Mass.kt b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/Mass.kt index 0ae6099..47c9e61 100644 --- a/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/Mass.kt +++ b/plugin/src/main/kotlin/io/github/addoncommunity/galactifun/util/items/Mass.kt @@ -1,41 +1,62 @@ package io.github.addoncommunity.galactifun.util.items +import io.github.addoncommunity.galactifun.blocks.CustomMass import io.github.addoncommunity.galactifun.units.Mass import io.github.addoncommunity.galactifun.units.Mass.Companion.kilograms import io.github.addoncommunity.galactifun.units.Mass.Companion.tons import io.github.addoncommunity.galactifun.util.contains import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag +import me.mrCookieSlime.Slimefun.api.BlockStorage import org.bukkit.Material import org.bukkit.block.Block private val INFINITE_MASS = Double.POSITIVE_INFINITY.kilograms -val Block.mass: Mass - get() = when { - type == Material.AIR || type == Material.CAVE_AIR || type == Material.VOID_AIR -> Mass.ZERO - type in SlimefunTag.UNBREAKABLE_MATERIALS -> INFINITE_MASS - type in SlimefunTag.FLUID_SENSITIVE_MATERIALS -> 100.kilograms - type in SlimefunTag.SENSITIVE_MATERIALS -> 100.kilograms - !type.isCollidable -> 200.kilograms - type == Material.IRON_BLOCK -> 7.9.tons - type == Material.GOLD_BLOCK -> 19.3.tons - type == Material.DIAMOND_BLOCK -> 3.5.tons - type == Material.EMERALD_BLOCK -> 2.8.tons - type == Material.QUARTZ_BLOCK -> 2.6.tons - type == Material.NETHERITE_BLOCK -> 19.3.tons - type == Material.GRANITE -> 2.6.tons - type == Material.DIORITE -> 2.8.tons - type == Material.ANDESITE -> 2.4.tons - type == Material.CALCITE -> 2.7.tons - type == Material.STONE || type == Material.COBBLESTONE -> 2.7.tons - type in SlimefunTag.WOODEN_DOORS - || type in SlimefunTag.WOODEN_SLABS - || type in SlimefunTag.WOODEN_STAIRS - || type in SlimefunTag.WOODEN_TRAPDOORS - || type in SlimefunTag.WOODEN_FENCES - || type in SlimefunTag.WOODEN_PRESSURE_PLATES - || type in SlimefunTag.WOODEN_BUTTONS - -> 0.3.tons - type in SlimefunTag.LOGS || type in SlimefunTag.PLANKS -> 0.6.tons +val Block.dryMass: Mass + get() { + val sfi = BlockStorage.check(this) + return if (sfi is CustomMass) { + sfi.getMass(this) + } else { + this.type.mass + } + } + +val Block.wetMass: Mass + get() { + val sfi = BlockStorage.check(this) + return if (sfi is CustomMass) { + sfi.getWetMass(this) + } else { + this.type.mass + } + } + +private val Material.mass: Mass + get() = when (this) { + Material.AIR, Material.CAVE_AIR, Material.VOID_AIR -> Mass.ZERO + in SlimefunTag.UNBREAKABLE_MATERIALS -> INFINITE_MASS + in SlimefunTag.FLUID_SENSITIVE_MATERIALS -> 100.kilograms + in SlimefunTag.SENSITIVE_MATERIALS -> 100.kilograms + Material.IRON_BLOCK -> 7.9.tons + Material.GOLD_BLOCK -> 19.3.tons + Material.DIAMOND_BLOCK -> 3.5.tons + Material.EMERALD_BLOCK -> 2.8.tons + Material.QUARTZ_BLOCK -> 2.6.tons + Material.NETHERITE_BLOCK -> 19.3.tons // tungsten used as reference + Material.GRANITE -> 2.6.tons + Material.DIORITE -> 2.8.tons + Material.ANDESITE -> 2.4.tons + Material.CALCITE -> 2.7.tons + Material.STONE, Material.COBBLESTONE -> 2.7.tons + in SlimefunTag.WOODEN_DOORS, + in SlimefunTag.WOODEN_SLABS, + in SlimefunTag.WOODEN_STAIRS, + in SlimefunTag.WOODEN_TRAPDOORS, + in SlimefunTag.WOODEN_FENCES, + in SlimefunTag.WOODEN_PRESSURE_PLATES, + in SlimefunTag.WOODEN_BUTTONS -> 0.3.tons + + in SlimefunTag.LOGS, in SlimefunTag.PLANKS -> 0.6.tons else -> 2.5.tons } \ No newline at end of file diff --git a/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt b/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt index d566b66..49e63d7 100644 --- a/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt +++ b/uom-processor/src/main/kotlin/io/github/seggan/uom/UomProcessor.kt @@ -12,6 +12,8 @@ import com.squareup.kotlinpoet.jvm.jvmInline import com.squareup.kotlinpoet.ksp.addOriginatingKSFile import com.squareup.kotlinpoet.ksp.writeTo import java.math.BigDecimal +import java.util.* +import kotlin.experimental.ExperimentalTypeInference class UomProcessor( private val generator: CodeGenerator, @@ -46,6 +48,7 @@ class UomProcessor( .addModifiers(KModifier.VALUE) .jvmInline() .addSuperinterface(COMPARABLE.parameterizedBy(clazzName)) + .addSuperinterface(Formattable::class) .primaryConstructor( FunSpec.constructorBuilder() @@ -106,6 +109,21 @@ class UomProcessor( ) .build() ) + .addFunction( + FunSpec.builder("formatTo") + .addModifiers(KModifier.OVERRIDE) + .addParameter("formatter", Formatter::class) + .addParameter("flags", Int::class) + .addParameter("width", Int::class) + .addParameter("precision", Int::class) + .addStatement( + "formatter.format(%S + precision + %S, %N)", + "%.", + "f $baseUnit", + baseUnit, + ) + .build() + ) val companion = TypeSpec.companionObjectBuilder() .addProperty( @@ -146,6 +164,33 @@ class UomProcessor( .addStatement("return kotlin.math.abs(value.%L).%L", baseUnit, baseUnit) .build() ) + .addFunction( + FunSpec.builder("sum") + .returns(clazzName) + .receiver(ITERABLE.parameterizedBy(clazzName)) + .addStatement("return this.fold(%T.ZERO) { acc, value -> acc + value }", clazzName) + .build() + ) + val t = TypeVariableName("T") + @OptIn(ExperimentalTypeInference::class) + file.addFunction( + FunSpec.builder("sumBy") + .addAnnotation( + AnnotationSpec.builder(ClassName("kotlin", "OptIn")) + .addMember("%T::class", ClassName("kotlin.experimental", "ExperimentalTypeInference")) + .build() + ) + .addAnnotation(OverloadResolutionByLambdaReturnType::class) + .addModifiers(KModifier.INLINE) + .addTypeVariable(t) + .returns(clazzName) + .receiver(ITERABLE.parameterizedBy(t)) + .addParameter("selector", LambdaTypeName.get(null, t, returnType = clazzName)) + .addStatement("var sum = %T.ZERO", clazzName) + .addStatement("for (element in this) sum += selector(element)") + .addStatement("return sum") + .build() + ) val extraImports = mutableListOf>() val mulAnnotations = classDeclaration.annotations.filter { it.shortName.asString() == "MultipliesTo" }