diff --git a/src/main/java/io/github/pylonmc/pylon/Pylon.java b/src/main/java/io/github/pylonmc/pylon/Pylon.java index e74504208..71433f28f 100644 --- a/src/main/java/io/github/pylonmc/pylon/Pylon.java +++ b/src/main/java/io/github/pylonmc/pylon/Pylon.java @@ -12,6 +12,8 @@ import io.github.pylonmc.rebar.addon.RebarAddon; import io.github.pylonmc.rebar.registry.RebarRegistry; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import java.util.Locale; +import java.util.Set; import lombok.Getter; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; @@ -20,9 +22,6 @@ import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; -import java.util.Locale; -import java.util.Set; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; public class Pylon extends JavaPlugin implements RebarAddon { diff --git a/src/main/java/io/github/pylonmc/pylon/PylonBlocks.java b/src/main/java/io/github/pylonmc/pylon/PylonBlocks.java index 06175390d..a95119bd4 100644 --- a/src/main/java/io/github/pylonmc/pylon/PylonBlocks.java +++ b/src/main/java/io/github/pylonmc/pylon/PylonBlocks.java @@ -7,6 +7,13 @@ import io.github.pylonmc.pylon.content.machines.diesel.machines.*; import io.github.pylonmc.pylon.content.machines.diesel.production.Biorefinery; import io.github.pylonmc.pylon.content.machines.diesel.production.Fermenter; +import io.github.pylonmc.pylon.content.machines.electricity.Capacitor; +import io.github.pylonmc.pylon.content.machines.electricity.ElectricityInputHatch; +import io.github.pylonmc.pylon.content.machines.electricity.ElectricityOutputHatch; +import io.github.pylonmc.pylon.content.machines.electricity.ElectricityPylon; +import io.github.pylonmc.pylon.content.machines.electricity.generation.*; +import io.github.pylonmc.pylon.content.machines.electricity.machines.ElectricBrickMolder; +import io.github.pylonmc.pylon.content.machines.electricity.machines.ElectricGrindstone; import io.github.pylonmc.pylon.content.machines.fluid.*; import io.github.pylonmc.pylon.content.machines.hydraulics.*; import io.github.pylonmc.pylon.content.machines.simple.*; @@ -187,5 +194,18 @@ public static void initialize() { RebarBlock.register(PylonKeys.PALLADIUM_SILO, Material.BLUE_TERRACOTTA, Silo.class); RebarBlock.register(PylonKeys.SILO_CONVERTER, Material.STRIPPED_OAK_LOG, SiloConverter.class); RebarBlock.register(PylonKeys.LISELETTE_COLLECTOR, Material.STRUCTURE_VOID, LiseletteCollector.class); + RebarBlock.register(PylonKeys.ELECTRICITY_PYLON, Material.SEA_LANTERN, ElectricityPylon.class); + RebarBlock.register(PylonKeys.CAPACITOR_1_KJ, Material.BLUE_GLAZED_TERRACOTTA, Capacitor.class); + RebarBlock.register(PylonKeys.CREATIVE_POWER_SOURCE, Material.PINK_STAINED_GLASS, CreativePowerSource.class); + RebarBlock.register(PylonKeys.ELECTRICITY_INPUT_HATCH, Material.GREEN_CONCRETE, ElectricityInputHatch.class); + RebarBlock.register(PylonKeys.ELECTRICITY_OUTPUT_HATCH, Material.ORANGE_CONCRETE, ElectricityOutputHatch.class); + RebarBlock.register(PylonKeys.BOILER_CASING, Material.NETHERITE_BLOCK, RebarBlock.class); + RebarBlock.register(PylonKeys.BOILER, Material.BLAST_FURNACE, Boiler.class); + RebarBlock.register(PylonKeys.STEAM_ENGINE, Material.IRON_BLOCK, SteamEngine.class); + RebarBlock.register(PylonKeys.GAS_TURBINE, Material.IRON_BLOCK, GasTurbine.class); + RebarBlock.register(PylonKeys.COMBUSTION_TOWER, Material.BRICKS, CombustionTower.class); + RebarBlock.register(PylonKeys.HEAT_EXCHANGER, Material.RED_NETHER_BRICK_WALL, HeatExchanger.class); + RebarBlock.register(PylonKeys.ELECTRIC_GRINDSTONE, Material.SMOOTH_STONE, ElectricGrindstone.class); + RebarBlock.register(PylonKeys.ELECTRIC_BRICK_MOLDER, Material.IRON_BLOCK, ElectricBrickMolder.class); } } diff --git a/src/main/java/io/github/pylonmc/pylon/PylonFluids.java b/src/main/java/io/github/pylonmc/pylon/PylonFluids.java index 551f0b83c..cb6d66c0a 100644 --- a/src/main/java/io/github/pylonmc/pylon/PylonFluids.java +++ b/src/main/java/io/github/pylonmc/pylon/PylonFluids.java @@ -11,6 +11,10 @@ import io.github.pylonmc.rebar.fluid.RebarFluid; import io.github.pylonmc.rebar.fluid.tags.FluidTemperature; import io.github.pylonmc.rebar.recipe.IngredientCalculator; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; public final class PylonFluids { @@ -18,6 +22,8 @@ private PylonFluids() { throw new AssertionError("Utility class"); } + public static final double WATER_TO_STEAM_RATIO = 1.0 / 10.0; + public static final RebarFluid WATER = new RebarFluid( pylonKey("water"), Material.BLUE_CONCRETE @@ -36,6 +42,14 @@ private PylonFluids() { IngredientCalculator.addBaseIngredient(LAVA); } + public static final RebarFluid STEAM = new RebarFluid( + pylonKey("steam"), + Material.WHITE_STAINED_GLASS + ).addTag(FluidTemperature.HOT); + static { + STEAM.register(); + } + public static final RebarFluid PLANT_OIL = new RebarFluid( pylonKey("plant_oil"), Material.YELLOW_CONCRETE_POWDER @@ -53,7 +67,6 @@ private PylonFluids() { HYDRAULIC_FLUID.register(); } - public static final RebarFluid DIRTY_HYDRAULIC_FLUID = new RebarFluid( pylonKey("dirty_hydraulic_fluid"), Material.BROWN_CONCRETE_POWDER @@ -248,6 +261,22 @@ private PylonFluids() { SPONGE_IRON_SLURRY.register(); } + public static final RebarFluid VERY_HOT_EXHAUST = new RebarFluid( + pylonKey("very_hot_exhaust"), + Material.RED_STAINED_GLASS + ).addTag(FluidTemperature.HOT); + static { + VERY_HOT_EXHAUST.register(); + } + + public static final RebarFluid HOT_EXHAUST = new RebarFluid( + pylonKey("hot_exhaust"), + Material.ORANGE_STAINED_GLASS + ).addTag(FluidTemperature.HOT); + static { + HOT_EXHAUST.register(); + } + /** * Calling this function will run the static blocks */ diff --git a/src/main/java/io/github/pylonmc/pylon/PylonItems.java b/src/main/java/io/github/pylonmc/pylon/PylonItems.java index 6a04aaba7..caf55ec24 100644 --- a/src/main/java/io/github/pylonmc/pylon/PylonItems.java +++ b/src/main/java/io/github/pylonmc/pylon/PylonItems.java @@ -16,6 +16,13 @@ import io.github.pylonmc.pylon.content.machines.diesel.machines.*; import io.github.pylonmc.pylon.content.machines.diesel.production.Biorefinery; import io.github.pylonmc.pylon.content.machines.diesel.production.Fermenter; +import io.github.pylonmc.pylon.content.machines.electricity.Capacitor; +import io.github.pylonmc.pylon.content.machines.electricity.Multimeter; +import io.github.pylonmc.pylon.content.machines.electricity.generation.Boiler; +import io.github.pylonmc.pylon.content.machines.electricity.generation.CombustionTower; +import io.github.pylonmc.pylon.content.machines.electricity.generation.SteamEngine; +import io.github.pylonmc.pylon.content.machines.electricity.machines.ElectricBrickMolder; +import io.github.pylonmc.pylon.content.machines.electricity.machines.ElectricGrindstone; import io.github.pylonmc.pylon.content.machines.fluid.*; import io.github.pylonmc.pylon.content.machines.hydraulics.*; import io.github.pylonmc.pylon.content.machines.simple.*; @@ -48,7 +55,6 @@ import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation; import io.papermc.paper.registry.keys.SoundEventKeys; import io.papermc.paper.registry.keys.tags.DamageTypeTagKeys; -import java.util.Objects; import net.kyori.adventure.key.Key; import org.bukkit.*; import org.bukkit.attribute.Attribute; @@ -3245,6 +3251,117 @@ private PylonItems() { RecipeType.VANILLA_SHAPELESS.addRecipe(recipe); } + // + public static final ItemStack ELECTRICITY_PYLON = ItemStackBuilder.rebar(Material.SEA_LANTERN, PylonKeys.ELECTRICITY_PYLON) + .build(); + static { + RebarItem.register(RebarItem.class, ELECTRICITY_PYLON, PylonKeys.ELECTRICITY_PYLON); + PylonPages.ELECTRICITY.addItem(ELECTRICITY_PYLON); + } + + public static final ItemStack CAPACITOR_1_KJ = ItemStackBuilder.rebar(Material.BLUE_GLAZED_TERRACOTTA, PylonKeys.CAPACITOR_1_KJ) + .build(); + static { + RebarItem.register(Capacitor.Item.class, CAPACITOR_1_KJ, PylonKeys.CAPACITOR_1_KJ); + PylonPages.ELECTRICITY.addItem(CAPACITOR_1_KJ); + } + + public static final ItemStack WIRE_1_GAUGE = ItemStackBuilder.rebar(Material.STRING, PylonKeys.WIRE_1_GAUGE) + .build(); + static { + RebarItem.register(PylonWire.class, WIRE_1_GAUGE, PylonKeys.WIRE_1_GAUGE); + PylonPages.TOOLS.addItem(WIRE_1_GAUGE); + } + + public static final ItemStack MULTIMETER = ItemStackBuilder.rebar(Material.CLOCK, PylonKeys.MULTIMETER) + .build(); + static { + RebarItem.register(Multimeter.class, MULTIMETER, PylonKeys.MULTIMETER); + PylonPages.TOOLS.addItem(MULTIMETER); + } + + public static final ItemStack CREATIVE_POWER_SOURCE = ItemStackBuilder.rebar(Material.PINK_STAINED_GLASS, PylonKeys.CREATIVE_POWER_SOURCE) + .build(); + static { + RebarItem.register(RebarItem.class, CREATIVE_POWER_SOURCE, PylonKeys.CREATIVE_POWER_SOURCE); + PylonPages.CREATIVE_ITEMS.addItem(CREATIVE_POWER_SOURCE); + } + + public static final ItemStack ELECTRICITY_INPUT_HATCH = ItemStackBuilder.rebar(Material.GREEN_CONCRETE, PylonKeys.ELECTRICITY_INPUT_HATCH) + .build(); + static { + RebarItem.register(RebarItem.class, ELECTRICITY_INPUT_HATCH, PylonKeys.ELECTRICITY_INPUT_HATCH); + PylonPages.COMPONENTS.addItem(ELECTRICITY_INPUT_HATCH); + } + + public static final ItemStack ELECTRICITY_OUTPUT_HATCH = ItemStackBuilder.rebar(Material.ORANGE_CONCRETE, PylonKeys.ELECTRICITY_OUTPUT_HATCH) + .build(); + static { + RebarItem.register(RebarItem.class, ELECTRICITY_OUTPUT_HATCH, PylonKeys.ELECTRICITY_OUTPUT_HATCH); + PylonPages.COMPONENTS.addItem(ELECTRICITY_OUTPUT_HATCH); + } + + public static final ItemStack BOILER_CASING = ItemStackBuilder.rebar(Material.NETHERITE_BLOCK, PylonKeys.BOILER_CASING) + .build(); + static { + RebarItem.register(RebarItem.class, BOILER_CASING, PylonKeys.BOILER_CASING); + PylonPages.COMPONENTS.addItem(BOILER_CASING); + } + + public static final ItemStack BOILER = ItemStackBuilder.rebar(Material.BLAST_FURNACE, PylonKeys.BOILER) + .build(); + static { + RebarItem.register(Boiler.Item.class, BOILER, PylonKeys.BOILER); + PylonPages.ELECTRICITY.addItem(BOILER); + } + + public static final ItemStack STEAM_ENGINE = ItemStackBuilder.rebar(Material.IRON_BLOCK, PylonKeys.STEAM_ENGINE) + .build(); + static { + RebarItem.register(SteamEngine.Item.class, STEAM_ENGINE, PylonKeys.STEAM_ENGINE); + PylonPages.ELECTRICITY.addItem(STEAM_ENGINE); + } + + public static final ItemStack GAS_TURBINE = ItemStackBuilder.rebar(Material.IRON_BLOCK, PylonKeys.GAS_TURBINE) + .build(); + static { + RebarItem.register(RebarItem.class, GAS_TURBINE, PylonKeys.GAS_TURBINE); + PylonPages.ELECTRICITY.addItem(GAS_TURBINE); + RebarGuide.getOrCreateInfoPage(PylonKeys.GAS_TURBINE) + .addButton(new MachineRecipesButton(GAS_TURBINE, GasTurbineRecipe.RECIPE_TYPE)); + } + + public static final ItemStack COMBUSTION_TOWER = ItemStackBuilder.rebar(Material.BRICKS, PylonKeys.COMBUSTION_TOWER) + .build(); + static { + RebarItem.register(CombustionTower.Item.class, COMBUSTION_TOWER, PylonKeys.COMBUSTION_TOWER); + PylonPages.ELECTRICITY.addItem(COMBUSTION_TOWER); + } + + public static final ItemStack HEAT_EXCHANGER = ItemStackBuilder.rebar(Material.RED_NETHER_BRICK_WALL, PylonKeys.HEAT_EXCHANGER) + .build(); + static { + RebarItem.register(RebarItem.class, HEAT_EXCHANGER, PylonKeys.HEAT_EXCHANGER); + PylonPages.ELECTRICITY.addItem(HEAT_EXCHANGER); + } + + public static final ItemStack ELECTRIC_GRINDSTONE = ItemStackBuilder.rebar(Material.SMOOTH_STONE, PylonKeys.ELECTRIC_GRINDSTONE) + .build(); + static { + RebarItem.register(ElectricGrindstone.Item.class, ELECTRIC_GRINDSTONE, PylonKeys.ELECTRIC_GRINDSTONE); + PylonPages.ELECTRICITY.addItem(ELECTRIC_GRINDSTONE); + RebarGuide.getOrCreateInfoPage(PylonKeys.ELECTRIC_GRINDSTONE) + .addButton(new MachineRecipesButton(ELECTRIC_GRINDSTONE, GrindstoneRecipe.RECIPE_TYPE)); + } + + public static final ItemStack ELECTRIC_BRICK_MOLDER = ItemStackBuilder.rebar(Material.IRON_BLOCK, PylonKeys.ELECTRIC_BRICK_MOLDER) + .build(); + static { + RebarItem.register(ElectricBrickMolder.Item.class, ELECTRIC_BRICK_MOLDER, PylonKeys.ELECTRIC_BRICK_MOLDER); + PylonPages.ELECTRICITY.addItem(ELECTRIC_BRICK_MOLDER); + } + // + static { PylonPages.initialise(); PylonHelpPages.initialise(); diff --git a/src/main/java/io/github/pylonmc/pylon/PylonKeys.java b/src/main/java/io/github/pylonmc/pylon/PylonKeys.java index 6f16fc675..692229c0d 100644 --- a/src/main/java/io/github/pylonmc/pylon/PylonKeys.java +++ b/src/main/java/io/github/pylonmc/pylon/PylonKeys.java @@ -472,4 +472,20 @@ public class PylonKeys { public static final NamespacedKey PALLADIUM_SILO = pylonKey("palladium_silo"); public static final NamespacedKey FINE_SEDIMENT = pylonKey("fine_sediment"); + + public static final NamespacedKey ELECTRICITY_PYLON = pylonKey("electricity_pylon"); + public static final NamespacedKey CAPACITOR_1_KJ = pylonKey("capacitor_1_kj"); + public static final NamespacedKey MULTIMETER = pylonKey("multimeter"); + public static final NamespacedKey WIRE_1_GAUGE = pylonKey("wire_1_gauge"); + public static final NamespacedKey CREATIVE_POWER_SOURCE = pylonKey("creative_power_source"); + public static final NamespacedKey ELECTRICITY_INPUT_HATCH = pylonKey("electricity_input_hatch"); + public static final NamespacedKey ELECTRICITY_OUTPUT_HATCH = pylonKey("electricity_output_hatch"); + public static final NamespacedKey BOILER_CASING = pylonKey("boiler_casing"); + public static final NamespacedKey BOILER = pylonKey("boiler"); + public static final NamespacedKey STEAM_ENGINE = pylonKey("steam_engine"); + public static final NamespacedKey GAS_TURBINE = pylonKey("gas_turbine"); + public static final NamespacedKey COMBUSTION_TOWER = pylonKey("combustion_tower"); + public static final NamespacedKey HEAT_EXCHANGER = pylonKey("heat_exchanger"); + public static final NamespacedKey ELECTRIC_GRINDSTONE = pylonKey("electric_grindstone"); + public static final NamespacedKey ELECTRIC_BRICK_MOLDER = pylonKey("electric_brick_molder"); } \ No newline at end of file diff --git a/src/main/java/io/github/pylonmc/pylon/PylonPages.java b/src/main/java/io/github/pylonmc/pylon/PylonPages.java index 475ddb62d..4d4e51e37 100644 --- a/src/main/java/io/github/pylonmc/pylon/PylonPages.java +++ b/src/main/java/io/github/pylonmc/pylon/PylonPages.java @@ -35,6 +35,7 @@ public class PylonPages { public static final SimpleStaticGuidePage CARGO = new SimpleStaticGuidePage(pylonKey("machines_cargo")); public static final SimpleStaticGuidePage DIESEL_MACHINES = new SimpleStaticGuidePage(pylonKey("machines_diesel_machines")); public static final SimpleStaticGuidePage DIESEL_PRODUCTION = new SimpleStaticGuidePage(pylonKey("machines_diesel_production")); + public static final SimpleStaticGuidePage ELECTRICITY = new SimpleStaticGuidePage(pylonKey("machines_electricity")); public static final SimpleStaticGuidePage MACHINES = new SimpleStaticGuidePage(pylonKey("machines")); public static final SimpleStaticGuidePage ASSEMBLING = new SimpleStaticGuidePage(pylonKey("assembling")); @@ -72,6 +73,7 @@ public static void initialise() { MACHINES.addPage(PylonItems.CARGO_BUFFER, CARGO); MACHINES.addPage(PylonItems.DIESEL_PIPE_BENDER, DIESEL_MACHINES); MACHINES.addPage(PylonItems.BIOREFINERY, DIESEL_PRODUCTION); + MACHINES.addPage(PylonItems.ELECTRICITY_PYLON, ELECTRICITY); RebarGuide.getRootPage().addPage(PylonItems.MIXING_POT, MACHINES); RebarGuide.getRootPage().addPage(PylonItems.ASSEMBLY_TABLE, ASSEMBLING); diff --git a/src/main/java/io/github/pylonmc/pylon/PylonRecipes.java b/src/main/java/io/github/pylonmc/pylon/PylonRecipes.java index d46adfd3f..537237bb2 100644 --- a/src/main/java/io/github/pylonmc/pylon/PylonRecipes.java +++ b/src/main/java/io/github/pylonmc/pylon/PylonRecipes.java @@ -5,6 +5,7 @@ import io.github.pylonmc.rebar.config.Config; import io.github.pylonmc.rebar.config.Settings; import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.fluid.FluidWithAmount; import io.github.pylonmc.rebar.guide.button.FluidButton; import io.github.pylonmc.rebar.guide.button.ItemButton; import io.github.pylonmc.rebar.recipe.FluidOrItem; @@ -45,12 +46,16 @@ public static void initialize() { SiloConverterRecipe.RECIPE_TYPE.register(); HydraulicPurifier.RECIPE_TYPE.register(); FormingRecipe.RECIPE_TYPE.register(); + GasTurbineRecipe.RECIPE_TYPE.register(); + HeatExchangerRecipe.RECIPE_TYPE.register(); //hardcoded initCollimator(); initPalladiumCondenser(); initBiorefinery(); initFermenter(); + initBoiler(); + initCombustionTower(); } private static void initCollimator() { @@ -77,6 +82,31 @@ private static void initCollimator() { ).register(); } + private static void initBoiler() { + NamespacedKey key = PylonKeys.BOILER; + double steamPerSecond = Settings.get(key).getOrThrow("steam-per-second", ConfigAdapter.DOUBLE); + RecipeInput.Fluid input = RecipeInput.of(PylonFluids.WATER, steamPerSecond * PylonFluids.WATER_TO_STEAM_RATIO); + FluidWithAmount output = new FluidWithAmount(PylonFluids.STEAM, steamPerSecond); + new SingleRecipe( + key, + input, + output.asFluidOrItem(), + () -> Gui.builder() + .setStructure( + "# # # # # # # # #", + "# # # # # # # # #", + "# i # # x # # o #", + "# # # # # # # # #", + "# # # # # # # # #" + ) + .addIngredient('#', GuiItems.backgroundBlack()) + .addIngredient('i', new FluidButton(input)) + .addIngredient('x', ItemButton.from(PylonItems.BOILER)) + .addIngredient('o', new FluidButton(output)) + .build() + ).register(); + } + private static void initPalladiumCondenser() { NamespacedKey key = PylonKeys.PALLADIUM_CONDENSER; Config setting = Settings.get(key); @@ -182,4 +212,34 @@ private static void initFermenter() { .build() ).register(); } + + private static void initCombustionTower() { + NamespacedKey key = PylonKeys.COMBUSTION_TOWER; + Config setting = Settings.get(key); + + double dieselUsage = setting.getOrThrow("diesel-usage", ConfigAdapter.DOUBLE); + double exhaustProduction = setting.getOrThrow("exhaust-production", ConfigAdapter.DOUBLE); + + RecipeInput.Fluid input = RecipeInput.of(PylonFluids.BIODIESEL, dieselUsage); + FluidWithAmount output = new FluidWithAmount(PylonFluids.VERY_HOT_EXHAUST, exhaustProduction); + + new SingleRecipe( + key, + input, + output.asFluidOrItem(), + () -> Gui.builder() + .setStructure( + "# # # # # # # # #", + "# # # # # # # # #", + "# d # # x # # e #", + "# # # # # # # # #", + "# # # # # # # # #" + ) + .addIngredient('#', GuiItems.backgroundBlack()) + .addIngredient('d', new FluidButton(input)) + .addIngredient('x', ItemButton.from(PylonItems.COMBUSTION_TOWER)) + .addIngredient('e', new FluidButton(output)) + .build() + ).register(); + } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/components/FluidHatch.java b/src/main/java/io/github/pylonmc/pylon/content/components/FluidHatch.java index 63440849e..d96f5d517 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/components/FluidHatch.java +++ b/src/main/java/io/github/pylonmc/pylon/content/components/FluidHatch.java @@ -7,7 +7,7 @@ import io.github.pylonmc.rebar.block.BlockStorage; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; -import io.github.pylonmc.rebar.block.base.RebarFluidBufferBlock; +import io.github.pylonmc.rebar.block.base.RebarFluidBlock; import io.github.pylonmc.rebar.block.base.RebarSimpleMultiblock; import io.github.pylonmc.rebar.block.context.BlockBreakContext; import io.github.pylonmc.rebar.block.context.BlockCreateContext; @@ -16,12 +16,14 @@ import io.github.pylonmc.rebar.entity.display.transform.TransformBuilder; import io.github.pylonmc.rebar.fluid.RebarFluid; import io.github.pylonmc.rebar.i18n.RebarArgument; -import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.item.RebarItemSchema; import io.github.pylonmc.rebar.registry.RebarRegistry; import io.github.pylonmc.rebar.util.RebarUtils; import io.github.pylonmc.rebar.waila.Waila; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.*; +import kotlin.Pair; +import lombok.Getter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Bukkit; @@ -31,28 +33,36 @@ import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector3i; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; public abstract class FluidHatch extends RebarBlock implements - RebarFluidBufferBlock, + RebarFluidBlock, RebarSimpleMultiblock, RebarDirectionalBlock { + private static final NamespacedKey ALLOWED_FLUIDS_KEY = pylonKey("allowed_fluids"); private static final NamespacedKey FLUID_KEY = pylonKey("fluid"); + private static final NamespacedKey FLUID_AMOUNT_KEY = pylonKey("fluid_amount"); + private static final NamespacedKey CAPACITY_KEY = pylonKey("capacity"); private static MultiblockComponent component; - public @Nullable RebarFluid fluid; + @Getter + private Set allowedFluids = new HashSet<>(); + + @Getter + private @Nullable RebarFluid fluid; + + @Getter + private double fluidAmount = 0; + + @Getter + private double capacity = 0; static { // run on first tick after all addons registered @@ -77,14 +87,21 @@ public FluidHatch(@NotNull Block block, @NotNull BlockCreateContext context) { ); } + @SuppressWarnings("DataFlowIssue") public FluidHatch(@NotNull Block block, @NotNull PersistentDataContainer pdc) { super(block, pdc); + allowedFluids = pdc.get(ALLOWED_FLUIDS_KEY, RebarSerializers.SET.setTypeFrom(RebarSerializers.REBAR_FLUID)); fluid = pdc.get(FLUID_KEY, RebarSerializers.REBAR_FLUID); + fluidAmount = pdc.get(FLUID_AMOUNT_KEY, RebarSerializers.DOUBLE); + capacity = pdc.get(CAPACITY_KEY, RebarSerializers.DOUBLE); } @Override public void write(@NotNull PersistentDataContainer pdc) { + pdc.set(ALLOWED_FLUIDS_KEY, RebarSerializers.SET.setTypeFrom(RebarSerializers.REBAR_FLUID), allowedFluids); RebarUtils.setNullable(pdc, FLUID_KEY, RebarSerializers.REBAR_FLUID, fluid); + pdc.set(FLUID_AMOUNT_KEY, RebarSerializers.DOUBLE, fluidAmount); + pdc.set(CAPACITY_KEY, RebarSerializers.DOUBLE, capacity); } @Override @@ -100,10 +117,7 @@ public boolean checkFormed() { if (formed) { FluidTankCasing casing = BlockStorage.getAs(FluidTankCasing.class, getBlock().getRelative(BlockFace.UP)); Preconditions.checkState(casing != null); - Waila.addWailaOverride(casing.getBlock(), this::getWaila); - if (fluid != null) { - setFluidCapacity(fluid, casing.capacity); - } + setCapacity(casing.capacity); } return formed; } @@ -113,7 +127,7 @@ public void onMultiblockUnformed(boolean partUnloaded) { RebarSimpleMultiblock.super.onMultiblockUnformed(partUnloaded); Waila.removeWailaOverride(getBlock().getRelative(BlockFace.UP)); if (fluid != null) { - setFluidCapacity(fluid, 0); + setCapacity(0); getFluidDisplay().setTransformationMatrix(new TransformBuilder() .scale(0, 0, 0) .buildForItemDisplay() @@ -122,10 +136,59 @@ public void onMultiblockUnformed(boolean partUnloaded) { } @Override - public boolean setFluid(@NotNull RebarFluid fluid, double amount) { - boolean result = RebarFluidBufferBlock.super.setFluid(fluid, amount); - float scale = (float) (0.9 * fluidAmount(fluid) / fluidCapacity(fluid)); + public @NotNull List<@NotNull Pair> getSuppliedFluids() { + if (fluid == null || fluidAmount <= 1e-6) { + return List.of(); + } + return List.of(new Pair<>(fluid, fluidAmount)); + } + + @Override + public double fluidAmountRequested(@NotNull RebarFluid fluid) { + if (Objects.equals(this.fluid, fluid)) { + return capacity - fluidAmount; + } else if (allowedFluids.contains(fluid)) { + return capacity; + } else { + return 0; + } + } + + /** + * Implementation of {@link RebarFluidBlock}. Use {@link #addFluid} instead. + */ + @ApiStatus.Internal + @Override + public void onFluidAdded(@NotNull RebarFluid fluid, double amount) { + setFluid(fluid, fluidAmount + amount); + } + + /** + * Implementation of {@link RebarFluidBlock}. Use {@link #removeFluid} instead. + */ + @ApiStatus.Internal + @Override + public void onFluidRemoved(@NotNull RebarFluid fluid, double amount) { + setFluid(fluid, fluidAmount - amount); + } + + public void addFluid(@NotNull RebarFluid fluid, double amount) { + onFluidAdded(fluid, amount); + } + + public void removeFluid(double amount) { + if (fluid != null) { + onFluidRemoved(fluid, amount); + } + } + + public void setFluid(@NotNull RebarFluid fluid, double amount) { + this.fluid = fluid; + this.fluidAmount = Math.min(amount, capacity); + float scale = (float) (0.9 * fluidAmount / capacity); if (scale < RebarUtils.FLUID_EPSILON) { + this.fluid = null; + this.fluidAmount = 0; getFluidDisplay().setItemStack(null); } else { getFluidDisplay().setItemStack(fluid.getItem()); @@ -135,7 +198,14 @@ public boolean setFluid(@NotNull RebarFluid fluid, double amount) { .scale(0.9, scale, 0.9) .buildForItemDisplay() ); - return result; + } + + public double getFluidSpaceRemaining() { + if (fluid == null) { + return capacity; + } else { + return capacity - fluidAmount; + } } @Override @@ -144,13 +214,13 @@ public boolean setFluid(@NotNull RebarFluid fluid, double amount) { if (!isFormedAndFullyLoaded()) { info = Component.translatable("pylon.message.fluid_hatch.no_casing"); } else if (fluid == null) { - info = Component.translatable("pylon.message.fluid_hatch.no_multiblock"); + info = Component.translatable("pylon.message.fluid_hatch.empty"); } else { info = Component.translatable("pylon.message.fluid_hatch.working") .arguments( RebarArgument.of("bars", PylonUtils.createFluidAmountBar( - fluidAmount(fluid), - fluidCapacity(fluid), + fluidAmount, + capacity, 20, TextColor.color(200, 255, 255) )), @@ -162,27 +232,32 @@ public boolean setFluid(@NotNull RebarFluid fluid, double amount) { )); } - public void setFluidType(@Nullable RebarFluid fluid) { - if (Objects.equals(this.fluid, fluid)) { - return; - } - - if (this.fluid != null) { - deleteFluidBuffer(this.fluid); + public void setAllowedFluids(@NotNull RebarFluid @NotNull ... fluids) { + setAllowedFluids(Set.of(fluids)); + } + + public void setAllowedFluids(@NotNull Set fluids) { + this.allowedFluids = fluids; + + if (this.fluid != null && !fluids.contains(this.fluid)) { + fluid = null; + setCapacity(0); getFluidDisplay().setTransformationMatrix(new TransformBuilder() .scale(0, 0, 0) .buildForItemDisplay() ); + this.fluid = null; } - this.fluid = fluid; - if (fluid != null) { - createFluidBuffer(fluid, 0, true, true); - } - if (isFormedAndFullyLoaded() && fluid != null) { - FluidTankCasing casing = BlockStorage.getAs(FluidTankCasing.class, getBlock().getRelative(BlockFace.UP)); - Preconditions.checkState(casing != null); - setFluidCapacity(fluid, casing.capacity); - } + checkFormed(); + } + + public boolean canAcceptFluid(@NotNull RebarFluid fluid) { + return fluidAmount < capacity && ((this.fluid == null && allowedFluids.contains(fluid)) || Objects.equals(this.fluid, fluid)); + } + + private void setCapacity(double capacity) { + this.capacity = capacity; + this.fluidAmount = Math.min(this.fluidAmount, capacity); } public @NotNull ItemDisplay getFluidDisplay() { @@ -191,7 +266,6 @@ public void setFluidType(@Nullable RebarFluid fluid) { @Override public void postBreak(@NotNull BlockBreakContext context) { - RebarFluidBufferBlock.super.postBreak(context); Waila.removeWailaOverride(getBlock().getRelative(BlockFace.UP)); } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/components/LiseletteCollector.java b/src/main/java/io/github/pylonmc/pylon/content/components/LiseletteCollector.java index d1e613c3d..d46a2ec57 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/components/LiseletteCollector.java +++ b/src/main/java/io/github/pylonmc/pylon/content/components/LiseletteCollector.java @@ -41,7 +41,7 @@ public LiseletteCollector(@NotNull Block block, @NotNull PersistentDataContainer } @Override - protected void postLoad() { + protected void postLoad(@NotNull PersistentDataContainer pdc) { getHeldEntityOrThrow(ItemDisplay.class, "shell") .setBrightness(new Display.Brightness(15, 15)); } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoAccumulator.java b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoAccumulator.java index 5c7bac55a..7fb2a62e3 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoAccumulator.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoAccumulator.java @@ -1,6 +1,7 @@ package io.github.pylonmc.pylon.content.machines.cargo; import com.google.common.base.Preconditions; +import io.github.pylonmc.pylon.util.NumberInputButton; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.RebarCargoBlock; import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; @@ -26,17 +27,12 @@ import org.bukkit.block.Block; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jspecify.annotations.NonNull; -import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.inventory.VirtualInventory; -import xyz.xenondevs.invui.item.AbstractItem; -import xyz.xenondevs.invui.item.ItemProvider; import java.util.Arrays; import java.util.List; @@ -66,8 +62,6 @@ public class CargoAccumulator extends RebarBlock implements .addCustomModelDataString(getKey() + ":input"); public final ItemStackBuilder outputStack = ItemStackBuilder.of(Material.RED_TERRACOTTA) .addCustomModelDataString(getKey() + ":output"); - public final ItemStackBuilder thresholdButtonStack = ItemStackBuilder.gui(Material.WHITE_CONCRETE, getKey() + "threshold_button") - .lore(Component.translatable("pylon.gui.threshold_button.lore")); public static class Item extends RebarItem { @@ -176,7 +170,17 @@ public void write(@NotNull PersistentDataContainer pdc) { .addIngredient('I', GuiItems.input()) .addIngredient('o', outputInventory) .addIngredient('O', GuiItems.output()) - .addIngredient('t', new ThresholdButton()) + .addIngredient('t', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.threshold")) + .increment(1) + .shiftIncrement(10) + .min(1) + .valueGetter(() -> threshold) + .valueSetter(value -> threshold = value) + .valueFormatter(UnitFormat.ITEMS::format) + .reopenWindow(this::openWindow) + .build()) .build(); } @@ -249,26 +253,4 @@ private void doTransfer() { .setBlock(Material.REDSTONE_LAMP.createBlockData("[lit=true]")); } } - - public class ThresholdButton extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NonNull Player viewer) { - return thresholdButtonStack - .name((Component.translatable("pylon.gui.threshold_button.name").arguments( - RebarArgument.of("threshold", threshold) - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - if (clickType.isLeftClick()) { - threshold += 1; - } else { - threshold = Math.max(1, threshold - 1); - } - notifyWindows(); - doTransfer(); - } - } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoFluidAccumulator.java b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoFluidAccumulator.java index 4c6c64fff..d8c2d8780 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoFluidAccumulator.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoFluidAccumulator.java @@ -1,6 +1,7 @@ package io.github.pylonmc.pylon.content.machines.cargo; import com.google.common.base.Preconditions; +import io.github.pylonmc.pylon.util.NumberInputButton; import io.github.pylonmc.pylon.util.PylonUtils; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.*; @@ -29,17 +30,12 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jspecify.annotations.NonNull; -import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.inventory.VirtualInventory; -import xyz.xenondevs.invui.item.AbstractItem; -import xyz.xenondevs.invui.item.ItemProvider; import java.util.Arrays; import java.util.List; @@ -77,10 +73,6 @@ public class CargoFluidAccumulator extends RebarBlock implements .addCustomModelDataString(getKey() + ":input"); public final ItemStackBuilder outputStack = ItemStackBuilder.of(Material.RED_TERRACOTTA) .addCustomModelDataString(getKey() + ":output"); - public final ItemStackBuilder itemThresholdButtonStack = ItemStackBuilder.gui(Material.WHITE_CONCRETE, getKey() + "item_threshold_button") - .lore(Component.translatable("pylon.gui.item_threshold_button.lore")); - public final ItemStackBuilder fluidThresholdButtonStack = ItemStackBuilder.gui(Material.WHITE_CONCRETE, getKey() + "fluid_threshold_button") - .lore(Component.translatable("pylon.gui.fluid_threshold_button.lore")); public static class Item extends RebarItem { @@ -163,7 +155,7 @@ public CargoFluidAccumulator(@NotNull Block block, @NotNull BlockCreateContext c allowFluidInputs = true; } - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "DataFlowIssue"}) public CargoFluidAccumulator(@NotNull Block block, @NotNull PersistentDataContainer pdc) { super(block, pdc); itemThreshold = pdc.get(ITEM_THRESHOLD_KEY, RebarSerializers.INTEGER); @@ -232,8 +224,30 @@ public boolean isAllowedFluid(@NotNull RebarFluid fluid) { .addIngredient('I', GuiItems.input()) .addIngredient('o', outputInventory) .addIngredient('O', GuiItems.output()) - .addIngredient('t', new ItemThresholdButton()) - .addIngredient('T', new FluidThresholdButton()) + .addIngredient('t', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.item-threshold")) + .increment(1) + .shiftIncrement(10) + .min(1) + .max(64) + .valueGetter(() -> itemThreshold) + .valueSetter(value -> itemThreshold = value) + .valueFormatter(UnitFormat.ITEMS::format) + .reopenWindow(this::openWindow) + .build()) + .addIngredient('T', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.fluid-threshold")) + .increment(10) + .shiftIncrement(100) + .min(10) + .max(fluidBuffer) + .valueGetter(() -> fluidThreshold) + .valueSetter(value -> fluidThreshold = value) + .valueFormatter(UnitFormat.MILLIBUCKETS::format) + .reopenWindow(this::openWindow) + .build()) .build(); } @@ -309,49 +323,4 @@ private void doTransfer() { allowFluidInputs = false; } } - - public class ItemThresholdButton extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NotNull Player viewer) { - return itemThresholdButtonStack - .name((Component.translatable("pylon.gui.item_threshold_button.name").arguments( - RebarArgument.of("threshold", itemThreshold) - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - if (clickType.isLeftClick()) { - itemThreshold += 1; - } else { - itemThreshold = Math.max(1, itemThreshold - 1); - } - notifyWindows(); - doTransfer(); - } - } - - public class FluidThresholdButton extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NotNull Player viewer) { - return fluidThresholdButtonStack - .name((Component.translatable("pylon.gui.fluid_threshold_button.name").arguments( - RebarArgument.of("threshold", fluidThreshold) - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - int amount = clickType.isShiftClick() ? 100 : 10; - if (clickType.isLeftClick()) { - fluidThreshold = Math.min(fluidBuffer, fluidThreshold + amount); - } else { - fluidThreshold = Math.min(fluidBuffer, Math.max(10, fluidThreshold - amount)); - } - notifyWindows(); - doTransfer(); - } - } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoGate.java b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoGate.java index f308e8644..7ebcbc161 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoGate.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoGate.java @@ -1,5 +1,6 @@ package io.github.pylonmc.pylon.content.machines.cargo; +import io.github.pylonmc.pylon.util.NumberInputButton; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.RebarCargoBlock; import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; @@ -28,17 +29,12 @@ import org.bukkit.block.BlockFace; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jspecify.annotations.NonNull; -import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.inventory.VirtualInventory; -import xyz.xenondevs.invui.item.AbstractItem; -import xyz.xenondevs.invui.item.ItemProvider; import java.util.List; import java.util.Map; @@ -77,30 +73,6 @@ public class CargoGate extends RebarBlock implements .name(Component.translatable("pylon.gui.left")); public final ItemStackBuilder rightStack = ItemStackBuilder.gui(Material.LIGHT_BLUE_STAINED_GLASS_PANE, getKey() + "right") .name(Component.translatable("pylon.gui.right")); - public final ItemStackBuilder thresholdButtonStack = ItemStackBuilder.gui(Material.WHITE_CONCRETE, getKey() + "threshold_button") - .lore(Component.translatable("pylon.gui.threshold_button.lore")); - - public class ThresholdButton extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NotNull Player viewer) { - return thresholdButtonStack - .name((Component.translatable("pylon.gui.threshold_button.name").arguments( - RebarArgument.of("threshold", threshold) - ))); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - if (clickType.isLeftClick()) { - threshold += 1; - } else { - threshold = Math.max(1, threshold - 1); - } - itemsRemaining = threshold; - notifyWindows(); - } - } public static class Item extends RebarItem { @@ -213,7 +185,17 @@ public void write(@NotNull PersistentDataContainer pdc) { .addIngredient('o', outputInventory) .addIngredient('R', rightStack) .addIngredient('r', rightInventory) - .addIngredient('t', new ThresholdButton()) + .addIngredient('t', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.threshold")) + .increment(1) + .shiftIncrement(10) + .min(1) + .valueGetter(() -> threshold) + .valueSetter(value -> threshold = value) + .valueFormatter(UnitFormat.ITEMS::format) + .reopenWindow(this::openWindow) + .build()) .build(); } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoMeter.java b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoMeter.java index 0032d9e75..902db512b 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoMeter.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/cargo/CargoMeter.java @@ -1,5 +1,6 @@ package io.github.pylonmc.pylon.content.machines.cargo; +import io.github.pylonmc.pylon.util.NumberInputButton; import io.github.pylonmc.pylon.util.PylonUtils; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.*; @@ -27,18 +28,13 @@ import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Player; import org.bukkit.entity.TextDisplay; -import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector3d; -import org.jspecify.annotations.NonNull; -import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.inventory.VirtualInventory; -import xyz.xenondevs.invui.item.AbstractItem; -import xyz.xenondevs.invui.item.ItemProvider; import java.time.Duration; import java.util.ArrayList; @@ -242,7 +238,18 @@ public void postInitialise() { ) .addIngredient('#', GuiItems.background()) .addIngredient('x', inventory) - .addIngredient('m', new MeasurementDurationItem()) + .addIngredient('m', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.measurement-duration")) + .increment(1) + .shiftIncrement(10) + .min(minNumberOfMeasurements) + .max(maxNumberOfMeasurements) + .valueGetter(() -> numberOfMeasurements) + .valueSetter(value -> numberOfMeasurements = value) + .valueFormatter(value -> UnitFormat.formatDuration(getDuration(value), true, true)) + .reopenWindow(this::openWindow) + .build()) .build(); } @@ -278,30 +285,4 @@ public void tick() { public static Duration getDuration(int numberOfMeasurements) { return Duration.ofMillis((long) numberOfMeasurements * RebarConfig.CARGO_TICK_INTERVAL * 50); } - - public class MeasurementDurationItem extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NotNull Player viewer) { - return ItemStackBuilder.of(Material.WHITE_CONCRETE) - .name(Component.translatable("pylon.gui.fluid_meter.name").arguments( - RebarArgument.of("measurement-duration", UnitFormat.formatDuration(getDuration(numberOfMeasurements), true, true)) - )) - .lore(Component.translatable("pylon.gui.fluid_meter.lore")); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - int newValue; - if (clickType.isLeftClick()) { - newValue = numberOfMeasurements + (clickType.isShiftClick() ? 10 : 1); - } else if (clickType.isRightClick()) { - newValue = numberOfMeasurements + (clickType.isShiftClick() ? -10 : -1); - } else { - newValue = numberOfMeasurements; - } - numberOfMeasurements = Math.clamp(newValue, minNumberOfMeasurements, maxNumberOfMeasurements); - notifyWindows(); - } - } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselBrickMolder.java b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselBrickMolder.java index 1ec69244c..ac9785145 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselBrickMolder.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselBrickMolder.java @@ -2,10 +2,10 @@ import com.destroystokyo.paper.ParticleBuilder; import io.github.pylonmc.pylon.PylonFluids; +import io.github.pylonmc.pylon.content.machines.generic.AbstractBrickMolder; import io.github.pylonmc.pylon.recipes.MoldingRecipe; import io.github.pylonmc.pylon.util.PylonUtils; -import io.github.pylonmc.rebar.block.RebarBlock; -import io.github.pylonmc.rebar.block.base.*; +import io.github.pylonmc.rebar.block.base.RebarFluidBufferBlock; import io.github.pylonmc.rebar.block.context.BlockBreakContext; import io.github.pylonmc.rebar.block.context.BlockCreateContext; import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; @@ -15,13 +15,10 @@ import io.github.pylonmc.rebar.i18n.RebarArgument; import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; -import io.github.pylonmc.rebar.logistics.LogisticGroupType; -import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.RebarUtils; -import io.github.pylonmc.rebar.util.gui.GuiItems; -import io.github.pylonmc.rebar.util.gui.ProgressItem; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.List; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Material; import org.bukkit.Particle; @@ -35,29 +32,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector3d; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.inventory.VirtualInventory; - -import java.util.List; -import java.util.Map; -public class DieselBrickMolder extends RebarBlock implements - RebarGuiBlock, - RebarVirtualInventoryBlock, - RebarFluidBufferBlock, - RebarDirectionalBlock, - RebarTickingBlock, - RebarLogisticBlock, - RebarRecipeProcessor { +public class DieselBrickMolder extends AbstractBrickMolder implements RebarFluidBufferBlock { public final double dieselBuffer = getSettings().getOrThrow("diesel-buffer", ConfigAdapter.DOUBLE); public final double dieselPerSecond = getSettings().getOrThrow("diesel-per-second", ConfigAdapter.DOUBLE); - public final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); - public final int ticksPerMoldingCycle = getSettings().getOrThrow("ticks-per-molding-cycle", ConfigAdapter.INTEGER); - - private final VirtualInventory inputInventory = new VirtualInventory(1); - private final VirtualInventory outputInventory = new VirtualInventory(1); public static class Item extends RebarItem { @@ -92,9 +72,7 @@ public Item(@NotNull ItemStack stack) { @SuppressWarnings("unused") public DieselBrickMolder(@NotNull Block block, @NotNull BlockCreateContext context) { super(block, context); - setTickInterval(tickInterval); createFluidPoint(FluidPointType.INPUT, BlockFace.NORTH, context, false, 0.55F); - setFacing(context.getFacing()); addEntity("chimney", new ItemDisplayBuilder() .itemStack(chimneyStack) .transformation(new TransformBuilder() @@ -131,8 +109,6 @@ public DieselBrickMolder(@NotNull Block block, @NotNull BlockCreateContext conte .build(block.getLocation().toCenterLocation().add(0, 0.5, 0)) ); createFluidBuffer(PylonFluids.BIODIESEL, dieselBuffer, true, false); - setRecipeType(MoldingRecipe.RECIPE_TYPE); - setRecipeProgressItem(new ProgressItem(GuiItems.background())); } @SuppressWarnings("unused") @@ -140,19 +116,6 @@ public DieselBrickMolder(@NotNull Block block, @NotNull PersistentDataContainer super(block, pdc); } - @Override - public void postInitialise() { - createLogisticGroup("input", LogisticGroupType.INPUT, inputInventory); - createLogisticGroup("output", LogisticGroupType.OUTPUT, outputInventory); - outputInventory.addPreUpdateHandler(RebarUtils.DISALLOW_PLAYERS_FROM_ADDING_ITEMS_HANDLER); - outputInventory.addPostUpdateHandler(event -> tryStartRecipe()); - inputInventory.addPostUpdateHandler(event -> { - if (!(event.getUpdateReason() instanceof MachineUpdateReason)) { - tryStartRecipe(); - } - }); - } - @Override public void tick() { if (!isProcessingRecipe() || fluidAmount(PylonFluids.BIODIESEL) < dieselPerSecond * tickInterval / 20) { @@ -178,61 +141,13 @@ public void tick() { .spawn(); } - public void tryStartRecipe() { - if (isProcessingRecipe()) { - return; - } - - ItemStack stack = inputInventory.getItem(0); - if (stack == null || stack.isEmpty()) { - return; - } - - if (getLastRecipe() != null && tryStartRecipe(getLastRecipe(), stack)) { - return; - } - - for (MoldingRecipe recipe : MoldingRecipe.RECIPE_TYPE) { - if (tryStartRecipe(recipe, stack)) { - break; - } - } - } - - private boolean tryStartRecipe(MoldingRecipe recipe, ItemStack stack) { - if (!recipe.input().isSimilar(stack) || !outputInventory.canHold(recipe.result())) { - return false; - } - - startRecipe(recipe, recipe.moldingCycles() * tickInterval * ticksPerMoldingCycle); - getRecipeProgressItem().setItem(ItemStackBuilder.of(stack.asOne()).clearLore()); - getHeldEntityOrThrow(ItemDisplay.class, "item").setItemStack(stack); - inputInventory.setItem(new MachineUpdateReason(), 0, stack.subtract(recipe.input().getAmount())); - return true; - } - - @Override - public void onRecipeFinished(@NotNull MoldingRecipe recipe) { - getRecipeProgressItem().setItem(GuiItems.background()); - getHeldEntityOrThrow(ItemDisplay.class, "item").setItemStack(null); - outputInventory.addItem(new MachineUpdateReason(), recipe.result().clone()); - } - @Override - public @NotNull Gui createGui() { - return Gui.builder() - .setStructure( - "# # I # # # O # #", - "# # i # p # o # #", - "# # I # # # O # #" - ) - .addIngredient('#', GuiItems.background()) - .addIngredient('I', GuiItems.input()) - .addIngredient('i', inputInventory) - .addIngredient('p', getRecipeProgressItem()) - .addIngredient('O', GuiItems.output()) - .addIngredient('o', outputInventory) - .build(); + protected boolean tryStartRecipe(MoldingRecipe recipe, ItemStack stack) { + boolean result = super.tryStartRecipe(recipe, stack); + if (result) { + getHeldEntityOrThrow(ItemDisplay.class, "item").setItemStack(stack); + } + return result; } @Override @@ -249,15 +164,13 @@ public void onRecipeFinished(@NotNull MoldingRecipe recipe) { @Override public void onBreak(@NotNull List<@NotNull ItemStack> drops, @NotNull BlockBreakContext context) { - RebarVirtualInventoryBlock.super.onBreak(drops, context); + super.onBreak(drops, context); RebarFluidBufferBlock.super.onBreak(drops, context); } @Override - public @NotNull Map getVirtualInventories() { - return Map.of( - "input", inputInventory, - "output", outputInventory - ); + public void onRecipeFinished(@NotNull MoldingRecipe recipe) { + super.onRecipeFinished(recipe); + getHeldEntityOrThrow(ItemDisplay.class, "item").setItemStack(null); } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselCoreDrill.java b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselCoreDrill.java index 57d42404b..74d5744bb 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselCoreDrill.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselCoreDrill.java @@ -11,6 +11,10 @@ import io.github.pylonmc.rebar.i18n.RebarArgument; import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.bukkit.Particle; import org.bukkit.block.Block; import org.bukkit.inventory.ItemStack; @@ -18,11 +22,6 @@ import org.jetbrains.annotations.NotNull; import org.joml.Vector3i; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class DieselCoreDrill extends CoreDrill { @@ -114,11 +113,11 @@ public void tick() { FluidInputHatch fluidInputHatch = getMultiblockComponentOrThrow(FluidInputHatch.class, FLUID_INPUT_HATCH); ItemOutputHatch itemOutputHatch = getMultiblockComponentOrThrow(ItemOutputHatch.class, ITEM_OUTPUT_HATCH); - if (fluidInputHatch.fluidAmount(PylonFluids.BIODIESEL) < dieselPerRotation || !itemOutputHatch.inventory.canHold(output)) { + if (fluidInputHatch.getFluidAmount() < dieselPerRotation || !itemOutputHatch.inventory.canHold(output)) { return; } - fluidInputHatch.removeFluid(PylonFluids.BIODIESEL, dieselPerRotation); + fluidInputHatch.removeFluid(dieselPerRotation); new ParticleBuilder(Particle.CAMPFIRE_COSY_SMOKE) .location(getMultiblockBlock(SMOKESTACK_CAP).getLocation().toCenterLocation()) @@ -145,6 +144,6 @@ public void onProcessFinished() { public void onMultiblockFormed() { super.onMultiblockFormed(); getMultiblockComponentOrThrow(FluidInputHatch.class, FLUID_INPUT_HATCH) - .setFluidType(PylonFluids.BIODIESEL); + .setAllowedFluids(PylonFluids.BIODIESEL); } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselGrindstone.java b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselGrindstone.java index 5493efc0f..24f289532 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselGrindstone.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/DieselGrindstone.java @@ -2,11 +2,10 @@ import com.destroystokyo.paper.ParticleBuilder; import io.github.pylonmc.pylon.PylonFluids; -import io.github.pylonmc.pylon.content.machines.simple.Grindstone; -import io.github.pylonmc.pylon.recipes.GrindstoneRecipe; +import io.github.pylonmc.pylon.content.machines.generic.AbstractGrindstone; import io.github.pylonmc.pylon.util.PylonUtils; -import io.github.pylonmc.rebar.block.RebarBlock; -import io.github.pylonmc.rebar.block.base.*; +import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; +import io.github.pylonmc.rebar.block.base.RebarFluidBufferBlock; import io.github.pylonmc.rebar.block.context.BlockBreakContext; import io.github.pylonmc.rebar.block.context.BlockCreateContext; import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; @@ -17,13 +16,10 @@ import io.github.pylonmc.rebar.i18n.RebarArgument; import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; -import io.github.pylonmc.rebar.logistics.LogisticGroupType; -import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.RebarUtils; -import io.github.pylonmc.rebar.util.gui.GuiItems; -import io.github.pylonmc.rebar.util.gui.ProgressItem; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.List; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -38,32 +34,18 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector3d; -import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.inventory.VirtualInventory; - -import java.util.List; -import java.util.Map; import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; -public class DieselGrindstone extends RebarBlock implements - RebarGuiBlock, - RebarVirtualInventoryBlock, +public class DieselGrindstone extends AbstractGrindstone implements RebarFluidBufferBlock, - RebarDirectionalBlock, - RebarTickingBlock, - RebarLogisticBlock, - RebarRecipeProcessor { + RebarDirectionalBlock { public static final NamespacedKey STONE_ROTATION_KEY = pylonKey("stone_rotation"); public final double dieselPerSecond = getSettings().getOrThrow("diesel-per-second", ConfigAdapter.DOUBLE); public final double dieselBuffer = getSettings().getOrThrow("diesel-buffer", ConfigAdapter.DOUBLE); - public final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); - - private final VirtualInventory inputInventory = new VirtualInventory(1); - private final VirtualInventory outputInventory = new VirtualInventory(3); private double stoneRotation; public static class Item extends RebarItem { @@ -96,7 +78,7 @@ public Item(@NotNull ItemStack stack) { @SuppressWarnings("unused") public DieselGrindstone(@NotNull Block block, @NotNull BlockCreateContext context) { super(block, context); - setTickInterval(tickInterval); + createFluidPoint(FluidPointType.INPUT, BlockFace.NORTH, context, false, 0.55F); setFacing(context.getFacing()); addEntity("chimney", new ItemDisplayBuilder() @@ -128,12 +110,10 @@ public DieselGrindstone(@NotNull Block block, @NotNull BlockCreateContext contex .build(block.getLocation().toCenterLocation().add(0, 0.5, 0)) ); createFluidBuffer(PylonFluids.BIODIESEL, dieselBuffer, true, false); - setRecipeType(GrindstoneRecipe.RECIPE_TYPE); - setRecipeProgressItem(new ProgressItem(GuiItems.background())); stoneRotation = 0; } - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "DataFlowIssue"}) public DieselGrindstone(@NotNull Block block, @NotNull PersistentDataContainer pdc) { super(block, pdc); stoneRotation = pdc.get(STONE_ROTATION_KEY, RebarSerializers.DOUBLE); @@ -144,19 +124,6 @@ public void write(@NotNull PersistentDataContainer pdc) { pdc.set(STONE_ROTATION_KEY, RebarSerializers.DOUBLE, stoneRotation); } - @Override - public void postInitialise() { - createLogisticGroup("input", LogisticGroupType.INPUT, inputInventory); - createLogisticGroup("output", LogisticGroupType.OUTPUT, outputInventory); - outputInventory.addPreUpdateHandler(RebarUtils.DISALLOW_PLAYERS_FROM_ADDING_ITEMS_HANDLER); - outputInventory.addPostUpdateHandler(event -> tryStartRecipe()); - inputInventory.addPostUpdateHandler(event -> { - if (!(event.getUpdateReason() instanceof MachineUpdateReason)) { - tryStartRecipe(); - } - }); - } - @Override public void tick() { if (!isProcessingRecipe() || fluidAmount(PylonFluids.BIODIESEL) < dieselPerSecond * tickInterval / 20) { @@ -186,65 +153,6 @@ public void tick() { ); } - public void tryStartRecipe() { - if (isProcessingRecipe()) { - return; - } - - ItemStack stack = inputInventory.getItem(0); - if (stack == null) { - return; - } - - if (getLastRecipe() != null && tryStartRecipe(getLastRecipe(), stack)) { - return; - } - - for (GrindstoneRecipe recipe : GrindstoneRecipe.RECIPE_TYPE) { - if (tryStartRecipe(recipe, stack)) { - return; - } - } - } - - private boolean tryStartRecipe(GrindstoneRecipe recipe, ItemStack stack) { - if (!recipe.input().matches(stack)) { - return false; - } - - if (!outputInventory.canHold(List.copyOf(recipe.results().getElements()))) { - return true; - } - - startRecipe(recipe, recipe.cycles() * Grindstone.CYCLE_DURATION_TICKS); - getRecipeProgressItem().setItem(ItemStackBuilder.of(stack.asOne()).clearLore()); - inputInventory.setItem(new MachineUpdateReason(), 0, stack.subtract(recipe.input().getAmount())); - return true; - } - - @Override - public void onRecipeFinished(@NotNull GrindstoneRecipe recipe) { - getRecipeProgressItem().setItem(GuiItems.background()); - outputInventory.addItem(null, recipe.results().getRandom()); - } - - @Override - public @NotNull Gui createGui() { - return Gui.builder() - .setStructure( - "# I # # # O O O #", - "# i # p # o o o #", - "# I # # # O O O #" - ) - .addIngredient('#', GuiItems.background()) - .addIngredient('I', GuiItems.input()) - .addIngredient('i', inputInventory) - .addIngredient('O', GuiItems.output()) - .addIngredient('o', outputInventory) - .addIngredient('p', getRecipeProgressItem()) - .build(); - } - @Override public @Nullable WailaDisplay getWaila(@NotNull Player player) { return new WailaDisplay(getDefaultWailaTranslationKey().arguments( @@ -259,15 +167,7 @@ public void onRecipeFinished(@NotNull GrindstoneRecipe recipe) { @Override public void onBreak(@NotNull List<@NotNull ItemStack> drops, @NotNull BlockBreakContext context) { - RebarVirtualInventoryBlock.super.onBreak(drops, context); + super.onBreak(drops, context); RebarFluidBufferBlock.super.onBreak(drops, context); } - - @Override - public @NotNull Map getVirtualInventories() { - return Map.of( - "input", inputInventory, - "output", outputInventory - ); - } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/PalladiumCondenser.java b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/PalladiumCondenser.java index 9a6f4b773..794e47797 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/PalladiumCondenser.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/machines/PalladiumCondenser.java @@ -21,6 +21,11 @@ import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Particle; @@ -32,12 +37,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Vector3i; -import java.time.Duration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - public class PalladiumCondenser extends RebarBlock implements RebarSimpleMultiblock, @@ -100,7 +99,6 @@ public Item(@NotNull ItemStack stack) { public PalladiumCondenser(@NotNull Block block, @NotNull BlockCreateContext context) { super(block, context); setFacing(context.getFacing()); - setMultiblockDirection(getFacing()); setTickInterval(tickInterval); } @@ -180,16 +178,16 @@ public void tick() { FluidOutputHatch dirtyHydraulicFluidOutputHatch = getMultiblockComponentOrThrow(FluidOutputHatch.class, DIRTY_HYDRAULIC_FLUID_OUTPUT_HATCH); ItemOutputHatch palladiumDustOutputHatch = getMultiblockComponentOrThrow(ItemOutputHatch.class, PALLADIUM_DUST_OUTPUT_HATCH); - if (biodieselInputHatch.fluidAmount(PylonFluids.BIODIESEL) < dieselPerSecond * getTickInterval() / 20 - || hydraulicFluidInputHatch.fluidAmount(PylonFluids.HYDRAULIC_FLUID) < hydraulicFluidPerSecond * getTickInterval() / 20 - || dirtyHydraulicFluidOutputHatch.fluidSpaceRemaining(PylonFluids.DIRTY_HYDRAULIC_FLUID) < hydraulicFluidPerSecond * getTickInterval() / 20 + if (biodieselInputHatch.getFluidAmount() < dieselPerSecond * getTickInterval() / 20 + || hydraulicFluidInputHatch.getFluidAmount() < hydraulicFluidPerSecond * getTickInterval() / 20 + || dirtyHydraulicFluidOutputHatch.getFluidSpaceRemaining() < hydraulicFluidPerSecond * getTickInterval() / 20 || !palladiumDustOutputHatch.inventory.canHold(PylonItems.PALLADIUM_DUST) ) { return; } - biodieselInputHatch.removeFluid(PylonFluids.BIODIESEL, dieselPerSecond * getTickInterval() / 20); - hydraulicFluidInputHatch.removeFluid(PylonFluids.HYDRAULIC_FLUID, hydraulicFluidPerSecond * getTickInterval() / 20); + biodieselInputHatch.removeFluid(dieselPerSecond * getTickInterval() / 20); + hydraulicFluidInputHatch.removeFluid(hydraulicFluidPerSecond * getTickInterval() / 20); dirtyHydraulicFluidOutputHatch.addFluid(PylonFluids.DIRTY_HYDRAULIC_FLUID, hydraulicFluidPerSecond * getTickInterval() / 20); progressProcess(getTickInterval()); @@ -243,11 +241,11 @@ public void onProcessFinished() { public void onMultiblockFormed() { RebarSimpleMultiblock.super.onMultiblockFormed(); getMultiblockComponentOrThrow(FluidInputHatch.class, BIODIESEL_INPUT_HATCH) - .setFluidType(PylonFluids.BIODIESEL); + .setAllowedFluids(PylonFluids.BIODIESEL); getMultiblockComponentOrThrow(FluidInputHatch.class, HYDRAULIC_FLUID_INPUT_HATCH) - .setFluidType(PylonFluids.HYDRAULIC_FLUID); + .setAllowedFluids(PylonFluids.HYDRAULIC_FLUID); getMultiblockComponentOrThrow(FluidOutputHatch.class, DIRTY_HYDRAULIC_FLUID_OUTPUT_HATCH) - .setFluidType(PylonFluids.DIRTY_HYDRAULIC_FLUID); + .setAllowedFluids(PylonFluids.DIRTY_HYDRAULIC_FLUID); } @Override diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Biorefinery.java b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Biorefinery.java index be574a8f7..9263f5c93 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Biorefinery.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Biorefinery.java @@ -21,11 +21,16 @@ import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.RebarUtils; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; -import io.github.pylonmc.rebar.waila.Waila; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextColor; -import org.bukkit.*; +import org.bukkit.Keyed; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Particle; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -36,10 +41,6 @@ import org.joml.Vector3d; import org.joml.Vector3i; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; public class Biorefinery extends RebarBlock implements @@ -81,7 +82,6 @@ public Item(@NotNull ItemStack stack) { public Biorefinery(@NotNull Block block, @NotNull BlockCreateContext context) { super(block, context); setFacing(context.getFacing()); - setMultiblockDirection(context.getFacing()); setTickInterval(tickInterval); } @@ -158,9 +158,9 @@ public Biorefinery(@NotNull Block block, @NotNull PersistentDataContainer pdc) { @Override public void onMultiblockFormed() { RebarSimpleMultiblock.super.onMultiblockFormed(); - getMultiblockComponentOrThrow(FluidInputHatch.class, ETHANOL_INPUT_HATCH).setFluidType(PylonFluids.ETHANOL); - getMultiblockComponentOrThrow(FluidInputHatch.class, PLANT_OIL_INPUT_HATCH).setFluidType(PylonFluids.PLANT_OIL); - getMultiblockComponentOrThrow(FluidOutputHatch.class, BIODIESEL_OUTPUT_HATCH).setFluidType(PylonFluids.BIODIESEL); + getMultiblockComponentOrThrow(FluidInputHatch.class, ETHANOL_INPUT_HATCH).setAllowedFluids(PylonFluids.ETHANOL); + getMultiblockComponentOrThrow(FluidInputHatch.class, PLANT_OIL_INPUT_HATCH).setAllowedFluids(PylonFluids.PLANT_OIL); + getMultiblockComponentOrThrow(FluidOutputHatch.class, BIODIESEL_OUTPUT_HATCH).setAllowedFluids(PylonFluids.BIODIESEL); } @Override @@ -177,19 +177,19 @@ public void tick() { FluidOutputHatch biodieselOutputHatch = getMultiblockComponentOrThrow(FluidOutputHatch.class, BIODIESEL_OUTPUT_HATCH); double biodieselToProduce = Math.min( - biodieselOutputHatch.fluidSpaceRemaining(PylonFluids.BIODIESEL), + biodieselOutputHatch.getFluidSpaceRemaining(), Math.min( biodieselPerSecond * getTickInterval() / 20.0, Math.min( - ethanolInputHatch.fluidAmount(PylonFluids.ETHANOL) / ethanolPerMbOfBiodiesel, - plantOilInputHatch.fluidAmount(PylonFluids.PLANT_OIL) / plantOilPerMbOfBiodiesel + ethanolInputHatch.getFluidAmount() / ethanolPerMbOfBiodiesel, + plantOilInputHatch.getFluidAmount() / plantOilPerMbOfBiodiesel ) ) ); if (biodieselToProduce > RebarUtils.FLUID_EPSILON) { - ethanolInputHatch.removeFluid(PylonFluids.ETHANOL, biodieselToProduce * ethanolPerMbOfBiodiesel); - plantOilInputHatch.removeFluid(PylonFluids.PLANT_OIL, biodieselToProduce * plantOilPerMbOfBiodiesel); + ethanolInputHatch.removeFluid(biodieselToProduce * ethanolPerMbOfBiodiesel); + plantOilInputHatch.removeFluid(biodieselToProduce * plantOilPerMbOfBiodiesel); biodieselOutputHatch.addFluid(PylonFluids.BIODIESEL, biodieselToProduce); } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Fermenter.java b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Fermenter.java index 47387a5dd..72cdc2fb5 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Fermenter.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/diesel/production/Fermenter.java @@ -23,6 +23,9 @@ import io.github.pylonmc.rebar.util.RebarUtils; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Material; import org.bukkit.block.Block; @@ -34,10 +37,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Vector3i; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class Fermenter extends RebarBlock implements RebarSimpleMultiblock, RebarDirectionalBlock, @@ -76,7 +75,6 @@ public Item(@NotNull ItemStack stack) { public Fermenter(@NotNull Block block, @NotNull BlockCreateContext context) { super(block, context); setFacing(context.getFacing()); - setMultiblockDirection(context.getFacing()); setTickInterval(tickInterval); createFluidBuffer(PylonFluids.SUGARCANE, ethanolPerSugarcane * sugarcaneCapacity, false, false); addEntity("sugarcane", new ItemDisplayBuilder() @@ -127,7 +125,6 @@ public Fermenter(@NotNull Block block, @NotNull PersistentDataContainer pdc) { public void onMultiblockFormed() { RebarSimpleMultiblock.super.onMultiblockFormed(); onMultiblockRefreshed(); - getMultiblockComponentOrThrow(FluidOutputHatch.class, OUTPUT_HATCH).setFluidType(PylonFluids.ETHANOL); getHeldEntityOrThrow(ItemDisplay.class, "sugarcane").setItemStack(PylonFluids.SUGARCANE.getItem()); } @@ -187,7 +184,7 @@ && fluidSpaceRemaining(PylonFluids.SUGARCANE) > ethanolPerSugarcane } double sugarcaneProportion = fluidAmount(PylonFluids.SUGARCANE) / fluidCapacity(PylonFluids.SUGARCANE); - double outputSpaceRemaining = outputHatch.fluidSpaceRemaining(PylonFluids.ETHANOL); + double outputSpaceRemaining = outputHatch.getFluidSpaceRemaining(); double ethanolToOutput = Math.min(outputSpaceRemaining, sugarcaneProportion * maxEthanolOutputRate * getTickInterval() / 20); if (ethanolToOutput > RebarUtils.FLUID_EPSILON) { removeFluid(PylonFluids.SUGARCANE, ethanolToOutput); diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/Capacitor.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/Capacitor.java new file mode 100644 index 000000000..f129520f9 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/Capacitor.java @@ -0,0 +1,113 @@ +package io.github.pylonmc.pylon.content.machines.electricity; + +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; +import io.github.pylonmc.rebar.block.base.RebarElectricBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.datatypes.RebarSerializers; +import io.github.pylonmc.rebar.electricity.ElectricNode; +import io.github.pylonmc.rebar.entity.display.TextDisplayBuilder; +import io.github.pylonmc.rebar.entity.display.transform.TransformBuilder; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import io.github.pylonmc.rebar.util.position.BlockPosition; +import java.util.List; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.entity.TextDisplay; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; + +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; + +public class Capacitor extends RebarBlock implements + RebarElectricBlock, + RebarDirectionalBlock { + + public static final class Item extends RebarItem { + + private final double capacity = getSettings().getOrThrow("capacity", ConfigAdapter.DOUBLE); + + public Item(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public @NotNull List<@NotNull RebarArgument> getPlaceholders() { + return List.of(RebarArgument.of("capacity", UnitFormat.JOULES.format(capacity))); + } + } + + private final double capacity = getSettings().getOrThrow("capacity", ConfigAdapter.DOUBLE); + + private static final NamespacedKey STORED_ENERGY_KEY = pylonKey("stored_energy"); + private double storedEnergy; + + private ElectricNode.Producer output; + + @SuppressWarnings("unused") + public Capacitor(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + + setFacing(context.getFacing()); + + addElectricPort(getFacing(), new ElectricNode.Acceptor("input", new BlockPosition(block))); + addElectricPort(getFacing().getOppositeFace(), new ElectricNode.Producer("output", new BlockPosition(block), 0)); + + addEntity("text_0", new TextDisplayBuilder() + .transformation(new TransformBuilder() + .lookAlong(getFacing()) + .rotate(0, Math.PI / 2, 0)) + .build(getBlock().getLocation().toCenterLocation().add(getFacing().getDirection().multiply(0.5001).rotateAroundY(Math.PI / 2))) + ); + + addEntity("text_1", new TextDisplayBuilder() + .transformation(new TransformBuilder() + .lookAlong(getFacing()) + .rotate(0, -Math.PI / 2, 0)) + .build(getBlock().getLocation().toCenterLocation().add(getFacing().getDirection().multiply(0.5001).rotateAroundY(-Math.PI / 2))) + ); + + storedEnergy = 0; + } + + @SuppressWarnings({"unused", "DataFlowIssue"}) + public Capacitor(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + + storedEnergy = pdc.get(STORED_ENERGY_KEY, RebarSerializers.DOUBLE); + } + + @Override + public void write(@NotNull PersistentDataContainer pdc) { + pdc.set(STORED_ENERGY_KEY, RebarSerializers.DOUBLE, storedEnergy); + } + + @Override + public void postInitialise() { + ElectricNode.Acceptor input = (ElectricNode.Acceptor) getElectricNodeOrThrow("input"); + input.onAccept(energy -> { + double accepted = Math.min(energy, capacity - storedEnergy); + setStoredEnergy(storedEnergy + accepted); + return accepted; + }); + + output = (ElectricNode.Producer) getElectricNodeOrThrow("output"); + output.onPowerTake(energy -> { + double taken = Math.min(energy, storedEnergy); + setStoredEnergy(storedEnergy - taken); + }); + + setStoredEnergy(storedEnergy); + } + + public void setStoredEnergy(double energy) { + storedEnergy = energy; + getHeldEntityOrThrow(TextDisplay.class, "text_0").text(UnitFormat.JOULES.format(storedEnergy).decimalPlaces(1).asComponent()); + getHeldEntityOrThrow(TextDisplay.class, "text_1").text(UnitFormat.JOULES.format(storedEnergy).decimalPlaces(1).asComponent()); + output.setPower(storedEnergy); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityInputHatch.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityInputHatch.java new file mode 100644 index 000000000..e07e9eedc --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityInputHatch.java @@ -0,0 +1,22 @@ +package io.github.pylonmc.pylon.content.machines.electricity; + +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarElectricConsumerBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import org.bukkit.block.Block; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; + +public class ElectricityInputHatch extends RebarBlock implements RebarElectricConsumerBlock { + + @SuppressWarnings("unused") + public ElectricityInputHatch(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + } + + @SuppressWarnings("unused") + public ElectricityInputHatch(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityOutputHatch.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityOutputHatch.java new file mode 100644 index 000000000..dfdcf3e32 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityOutputHatch.java @@ -0,0 +1,22 @@ +package io.github.pylonmc.pylon.content.machines.electricity; + +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarElectricProducerBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import org.bukkit.block.Block; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; + +public class ElectricityOutputHatch extends RebarBlock implements RebarElectricProducerBlock { + + @SuppressWarnings("unused") + public ElectricityOutputHatch(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + } + + @SuppressWarnings("unused") + public ElectricityOutputHatch(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityPylon.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityPylon.java new file mode 100644 index 000000000..515c3e1e6 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/ElectricityPylon.java @@ -0,0 +1,50 @@ +package io.github.pylonmc.pylon.content.machines.electricity; + +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarElectricBlock; +import io.github.pylonmc.rebar.block.base.RebarTickingBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.electricity.ElectricNetwork; +import io.github.pylonmc.rebar.electricity.ElectricNode; +import io.github.pylonmc.rebar.util.RebarUtils; +import io.github.pylonmc.rebar.util.position.BlockPosition; +import org.bukkit.Color; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; + +public final class ElectricityPylon extends RebarBlock implements + RebarElectricBlock, + RebarTickingBlock { + + @SuppressWarnings("unused") + public ElectricityPylon(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + + setTickInterval(10); + ElectricNode.Connector centralNode = addElectricNode(new ElectricNode.Connector("center", new BlockPosition(block))); + for (BlockFace face : RebarUtils.IMMEDIATE_FACES) { + ElectricNode.Connector port = addElectricPort(face, new ElectricNode.Connector(face.name(), new BlockPosition(block))); + centralNode.connect(port); + } + } + + @SuppressWarnings("unused") + public ElectricityPylon(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void tick() { + ElectricNetwork network = getElectricNodes().getFirst().getNetwork(); + for (ElectricNode node : network.getNodes()) { + Particle.DUST.builder() + .color(Color.fromARGB(network.hashCode())) + .location(node.getBlock().toLocation().toCenterLocation().add(0, 0.6, 0)) + .receivers(32, true) + .spawn(); + } + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/Multimeter.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/Multimeter.java new file mode 100644 index 000000000..14d769bff --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/Multimeter.java @@ -0,0 +1,28 @@ +package io.github.pylonmc.pylon.content.machines.electricity; + +import io.github.pylonmc.rebar.block.BlockStorage; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.item.base.RebarInteractor; +import java.util.Comparator; +import org.bukkit.Location; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class Multimeter extends RebarItem implements RebarInteractor { + public Multimeter(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public void onUsedToClick(@NotNull PlayerInteractEvent event, @NotNull EventPriority priority) { + Location playerLocation = event.getPlayer().getLocation(); + ElectricityPylon pylon = BlockStorage.getByType(ElectricityPylon.class).stream() + .min(Comparator.comparing(p -> p.getBlock().getLocation().toCenterLocation().distanceSquared(playerLocation))) + .filter(p -> p.getBlock().getLocation().toCenterLocation().distanceSquared(playerLocation) < 64 * 64) + .orElse(null); + if (pylon == null) return; + throw new UnsupportedOperationException("Multimeter interaction not implemented yet"); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/Boiler.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/Boiler.java new file mode 100644 index 000000000..15739aad9 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/Boiler.java @@ -0,0 +1,239 @@ +package io.github.pylonmc.pylon.content.machines.electricity.generation; + +import io.github.pylonmc.pylon.PylonFluids; +import io.github.pylonmc.pylon.PylonKeys; +import io.github.pylonmc.pylon.content.components.FluidInputHatch; +import io.github.pylonmc.pylon.content.components.FluidOutputHatch; +import io.github.pylonmc.pylon.util.BurnerProgressItem; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.*; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.Settings; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.ItemTypeWrapper; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.logistics.LogisticGroupType; +import io.github.pylonmc.rebar.registry.RebarRegistry; +import io.github.pylonmc.rebar.util.MachineUpdateReason; +import io.github.pylonmc.rebar.util.RebarUtils; +import io.github.pylonmc.rebar.util.gui.GuiItems; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import kotlin.Pair; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector3i; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.inventory.VirtualInventory; + +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; + +public class Boiler extends RebarBlock implements + RebarSimpleMultiblock, + RebarDirectionalBlock, + RebarProcessor, + RebarTickingBlock, + RebarVirtualInventoryBlock, + RebarGuiBlock, + RebarLogisticBlock { + + public static class Item extends RebarItem { + + private final double steamPerSecond = getSettings().getOrThrow("steam-per-second", ConfigAdapter.DOUBLE); + + public Item(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public @NotNull List<@NotNull RebarArgument> getPlaceholders() { + return List.of( + RebarArgument.of("water-usage", UnitFormat.MILLIBUCKETS_PER_SECOND.format(steamPerSecond * PylonFluids.WATER_TO_STEAM_RATIO)), + RebarArgument.of("steam-production", UnitFormat.MILLIBUCKETS_PER_SECOND.format(steamPerSecond)) + ); + } + } + + private static final ConfigAdapter> FUELS_TYPE = ConfigAdapter.MAP.from( + ConfigAdapter.ITEM_STACK, + ConfigAdapter.INTEGER + ); + + public record Fuel(@NotNull NamespacedKey key, @NotNull ItemStack item, int burnTimeSeconds) implements Keyed { + @Override + public @NotNull NamespacedKey getKey() { + return key; + } + } + + public static final RebarRegistry FUEL_REGISTRY = new RebarRegistry<>(pylonKey("boiler_fuels")); + + static { + for (var fuel : Settings.get(PylonKeys.BOILER).getOrThrow("fuels", FUELS_TYPE).entrySet()) { + FUEL_REGISTRY.register(new Fuel(ItemTypeWrapper.of(fuel.getKey()).getKey(), fuel.getKey(), fuel.getValue())); + } + } + + private final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + private final double steamPerSecond = getSettings().getOrThrow("steam-per-second", ConfigAdapter.DOUBLE); + + private final VirtualInventory fuelInventory = new VirtualInventory(1); + private final BurnerProgressItem progressItem = new BurnerProgressItem(); + + @SuppressWarnings("unused") + public Boiler(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setTickInterval(tickInterval); + setFacing(context.getFacing()); + } + + @SuppressWarnings("unused") + public Boiler(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void postInitialise() { + setProcessProgressItem(progressItem); + createLogisticGroup("fuel", LogisticGroupType.INPUT, fuelInventory); + fuelInventory.addPreUpdateHandler(event -> { + ItemStack item = event.getNewItem(); + for (Fuel fuel : FUEL_REGISTRY) { + if (fuel.item().isSimilar(item)) { + return; + } + } + event.setCancelled(true); + }); + } + + @Override + public @NotNull Gui createGui() { + return Gui.builder() + .setStructure( + "# # # # # # # # #", + "# # # i p i # # #", + "# # # i x i # # #", + "# # # i i i # # #", + "# # # # # # # # #" + ) + .addIngredient('#', GuiItems.background()) + .addIngredient('i', GuiItems.input()) + .addIngredient('p', progressItem) + .addIngredient('x', fuelInventory) + .build(); + } + + @Override + public @NotNull Map<@NotNull String, @NotNull VirtualInventory> getVirtualInventories() { + return Map.of("fuel", fuelInventory); + } + + private static final Vector3i WATER_INPUT_HATCH = new Vector3i(1, 0, 0); + private static final Vector3i STEAM_OUTPUT_HATCH = new Vector3i(-1, 0, 0); + private static final Vector3i SMOKESTACK_CAP = new Vector3i(0, 2, 1); + + @Override + public @NotNull Map<@NotNull Vector3i, @NotNull MultiblockComponent> getComponents() { + Map components = new HashMap<>(); + + components.put(WATER_INPUT_HATCH, MultiblockComponent.of(PylonKeys.FLUID_INPUT_HATCH)); + components.put(STEAM_OUTPUT_HATCH, MultiblockComponent.of(PylonKeys.FLUID_OUTPUT_HATCH)); + + // bottom layer + for (int x = -1; x <= 1; x++) { + for (int z = 0; z <= 2; z++) { + components.put(new Vector3i(x, -1, z), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + } + } + + // middle layer + components.put(new Vector3i(-1, 0, 1), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + components.put(new Vector3i(1, 0, 1), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + components.put(new Vector3i(-1, 0, 2), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + components.put(new Vector3i(0, 0, 2), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + components.put(new Vector3i(1, 0, 2), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + + // top layer + components.put(new Vector3i(0, 1, 0), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + for (int x = -1; x <= 1; x++) { + for (int z = 1; z <= 2; z++) { + components.put(new Vector3i(x, 1, z), MultiblockComponent.of(PylonKeys.BOILER_CASING)); + } + } + + components.put(SMOKESTACK_CAP, MultiblockComponent.of(PylonKeys.SMOKESTACK_CAP)); + + return components; + } + + @Override + public void onMultiblockFormed() { + RebarSimpleMultiblock.super.onMultiblockFormed(); + getMultiblockComponentOrThrow(FluidInputHatch.class, WATER_INPUT_HATCH).setAllowedFluids(PylonFluids.WATER); + getMultiblockComponentOrThrow(FluidOutputHatch.class, STEAM_OUTPUT_HATCH).setAllowedFluids(PylonFluids.STEAM); + } + + private void tryStartProcessing() { + for (Fuel fuel : FUEL_REGISTRY) { + if (fuelInventory.removeFirstSimilar(new MachineUpdateReason(), 1, fuel.item()) > 0) { + startProcess(fuel.burnTimeSeconds() * 20); + refreshBlockTextureItem(); + return; + } + } + } + + @Override + public void tick() { + if (!isFormedAndFullyLoaded()) return; + + if (!isProcessing()) { + tryStartProcessing(); + } + + if (!isProcessing()) return; + + progressProcess(tickInterval); + + double steamProduction = steamPerSecond * (tickInterval / 20.0); + double waterConsumption = steamProduction * PylonFluids.WATER_TO_STEAM_RATIO; + FluidInputHatch waterInput = getMultiblockComponentOrThrow(FluidInputHatch.class, WATER_INPUT_HATCH); + FluidOutputHatch steamOutput = getMultiblockComponentOrThrow(FluidOutputHatch.class, STEAM_OUTPUT_HATCH); + double ratio = Math.min(1, waterInput.getFluidAmount() / waterConsumption); + ratio = Math.min(ratio, steamOutput.getFluidSpaceRemaining() / steamProduction); + if (ratio <= 0) return; + + waterInput.removeFluid(waterConsumption * ratio); + steamOutput.addFluid(PylonFluids.STEAM, steamProduction * ratio); + + Particle.CAMPFIRE_SIGNAL_SMOKE.builder() + .location(getBlock().getLocation().add(Vector.fromJOML(RebarUtils.rotateVectorToFace(SMOKESTACK_CAP, getFacing()))).toCenterLocation()) + .offset(0, 1, 0) + .count(0) + .extra(0.03) + .spawn(); + } + + @Override + public void finishProcess() { + refreshBlockTextureItem(); + tryStartProcessing(); + } + + @Override + public @NotNull Map<@NotNull String, @NotNull Pair<@NotNull String, @NotNull Integer>> getBlockTextureProperties() { + var properties = super.getBlockTextureProperties(); + properties.put("lit", new Pair<>(String.valueOf(isProcessing()), 2)); + return properties; + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/CombustionTower.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/CombustionTower.java new file mode 100644 index 000000000..95fe22016 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/CombustionTower.java @@ -0,0 +1,129 @@ +package io.github.pylonmc.pylon.content.machines.electricity.generation; + +import io.github.pylonmc.pylon.PylonFluids; +import io.github.pylonmc.pylon.PylonKeys; +import io.github.pylonmc.pylon.util.PylonUtils; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarFluidBufferBlock; +import io.github.pylonmc.rebar.block.base.RebarSimpleMultiblock; +import io.github.pylonmc.rebar.block.base.RebarTickingBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.fluid.FluidPointType; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3i; + +public class CombustionTower extends RebarBlock implements + RebarTickingBlock, + RebarFluidBufferBlock, + RebarSimpleMultiblock { + + private final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + private final double dieselUsage = getSettings().getOrThrow("diesel-usage", ConfigAdapter.DOUBLE); + private final double dieselBuffer = getSettings().getOrThrow("diesel-buffer", ConfigAdapter.DOUBLE); + private final double exhaustProduction = getSettings().getOrThrow("exhaust-production", ConfigAdapter.DOUBLE); + private final double exhaustBuffer = getSettings().getOrThrow("exhaust-buffer", ConfigAdapter.DOUBLE); + + public static class Item extends RebarItem { + + private final double dieselUsage = getSettings().getOrThrow("diesel-usage", ConfigAdapter.DOUBLE); + private final double exhaustProduction = getSettings().getOrThrow("exhaust-production", ConfigAdapter.DOUBLE); + + @SuppressWarnings("unused") + public Item(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public @NotNull List<@NotNull RebarArgument> getPlaceholders() { + return List.of( + RebarArgument.of("diesel-usage", UnitFormat.MILLIBUCKETS_PER_SECOND.format(dieselUsage)), + RebarArgument.of("exhaust-production", UnitFormat.MILLIBUCKETS_PER_SECOND.format(exhaustProduction)) + ); + } + } + + @SuppressWarnings("unused") + public CombustionTower(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + setTickInterval(tickInterval); + + createFluidBuffer(PylonFluids.BIODIESEL, dieselBuffer, true, false); + createFluidBuffer(PylonFluids.VERY_HOT_EXHAUST, exhaustBuffer, false, true); + + createFluidPoint(FluidPointType.INPUT, BlockFace.NORTH); + createFluidPoint(FluidPointType.OUTPUT, BlockFace.SOUTH); + } + + @SuppressWarnings("unused") + public CombustionTower(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void tick() { + if (!isFormedAndFullyLoaded()) return; + + double ratio = Math.min(1, fluidAmount(PylonFluids.BIODIESEL) / dieselUsage); + ratio = Math.min(ratio, fluidSpaceRemaining(PylonFluids.VERY_HOT_EXHAUST) / exhaustProduction); + if (ratio <= 0) return; + + removeFluid(PylonFluids.BIODIESEL, dieselUsage * ratio); + addFluid(PylonFluids.VERY_HOT_EXHAUST, exhaustProduction * ratio); + + Particle.CAMPFIRE_SIGNAL_SMOKE.builder() + .location(getBlock().getLocation().add(Vector.fromJOML(SMOKESTACK_POS)).toCenterLocation()) + .offset(0, 1, 0) + .count(0) + .extra(0.03) + .spawn(); + } + + @Override + public @Nullable WailaDisplay getWaila(@NotNull Player player) { + return new WailaDisplay(getDefaultWailaTranslationKey().arguments( + RebarArgument.of("diesel", PylonUtils.createFluidAmountBar( + fluidAmount(PylonFluids.BIODIESEL), + fluidCapacity(PylonFluids.BIODIESEL), + 20, + TextColor.fromHexString("#eaa627") + )), + RebarArgument.of("exhaust", PylonUtils.createFluidAmountBar( + fluidAmount(PylonFluids.VERY_HOT_EXHAUST), + fluidCapacity(PylonFluids.VERY_HOT_EXHAUST), + 20, + TextColor.fromHexString("#ff2b0f") + )) + )); + } + + private static final Vector3i SMOKESTACK_POS = new Vector3i(0, 3, 0); + + @Override + public @NotNull Map<@NotNull Vector3i, @NotNull MultiblockComponent> getComponents() { + Map components = new HashMap<>(); + + components.put(new Vector3i(0, 1, 0), MultiblockComponent.of(PylonKeys.SMOKESTACK_RING)); + components.put(new Vector3i(0, 2, 0), MultiblockComponent.of(PylonKeys.SMOKESTACK_RING)); + components.put(SMOKESTACK_POS, MultiblockComponent.of(PylonKeys.SMOKESTACK_CAP)); + + return components; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/CreativePowerSource.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/CreativePowerSource.java new file mode 100644 index 000000000..a70afa57f --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/CreativePowerSource.java @@ -0,0 +1,60 @@ +package io.github.pylonmc.pylon.content.machines.electricity.generation; + +import io.github.pylonmc.pylon.util.NumberInputButton; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarElectricProducerBlock; +import io.github.pylonmc.rebar.block.base.RebarGuiBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.util.gui.GuiItems; +import io.github.pylonmc.rebar.util.gui.unit.MetricPrefix; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.gui.Gui; + +public final class CreativePowerSource extends RebarBlock implements + RebarElectricProducerBlock, + RebarGuiBlock { + + @SuppressWarnings("unused") + public CreativePowerSource(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + } + + @SuppressWarnings({"unused"}) + public CreativePowerSource(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public @NotNull Gui createGui() { + return Gui.builder() + .setStructure("# # # # p # # # #") + .addIngredient('#', GuiItems.background()) + .addIngredient('p', NumberInputButton.builder() + .material(Material.NETHER_STAR) + .name(Component.translatable("pylon.gui.power")) + .increment(1) + .shiftIncrement(10) + .min(0) + .valueGetter(() -> (int) getPower()) + .valueSetter(this::setPower) + .valueFormatter(p -> formatQuantity(UnitFormat.WATTS, p)) + .reopenWindow(this::openWindow) + .build()) + .build(); + } + + private static ComponentLike formatQuantity(UnitFormat format, int quantity) { + return format.format(quantity) + .ignorePrefixes(MetricPrefix.DECI, MetricPrefix.DECA, MetricPrefix.HECTO) + .abbreviate(true) + .autoSelectPrefix() + .decimalPlaces(0); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/GasTurbine.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/GasTurbine.java new file mode 100644 index 000000000..9eb94f268 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/GasTurbine.java @@ -0,0 +1,321 @@ +package io.github.pylonmc.pylon.content.machines.electricity.generation; + +import io.github.pylonmc.pylon.Pylon; +import io.github.pylonmc.pylon.PylonKeys; +import io.github.pylonmc.pylon.content.components.FluidInputHatch; +import io.github.pylonmc.pylon.content.components.FluidOutputHatch; +import io.github.pylonmc.pylon.content.machines.electricity.ElectricityOutputHatch; +import io.github.pylonmc.pylon.recipes.GasTurbineRecipe; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarSimpleMultiblock; +import io.github.pylonmc.rebar.block.base.RebarTickingBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.entity.display.ItemDisplayBuilder; +import io.github.pylonmc.rebar.entity.display.transform.TransformUtil; +import io.github.pylonmc.rebar.fluid.RebarFluid; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; +import io.github.pylonmc.rebar.util.Vector3fs; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.util.Vector; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; +import org.joml.Vector3i; + +public class GasTurbine extends RebarBlock implements + RebarSimpleMultiblock, + RebarTickingBlock { + + private final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + + @SuppressWarnings("unused") + public GasTurbine(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + + setFacing(context.getFacing()); + setTickInterval(tickInterval); + } + + @SuppressWarnings("unused") + public GasTurbine(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void tick() { + if (!isFormedAndFullyLoaded()) return; + + ElectricityOutputHatch electricityOutputHatch = getMultiblockComponentOrThrow(ElectricityOutputHatch.class, ELECTRICITY_OUTPUT_HATCH); + electricityOutputHatch.setPower(0); + + FluidInputHatch inputHatch = getMultiblockComponentOrThrow(FluidInputHatch.class, FLUID_INPUT_HATCH); + RebarFluid inputFluid = inputHatch.getFluid(); + if (inputFluid == null) return; + double inputAmount = inputHatch.getFluidAmount(); + + GasTurbineRecipe matchingRecipe = GasTurbineRecipe.RECIPE_TYPE.stream() + .filter(r -> r.input().contains(inputFluid)) + .findFirst() + .orElse(null); + if (matchingRecipe == null) return; + + FluidOutputHatch outputHatch = getMultiblockComponentOrThrow(FluidOutputHatch.class, FLUID_OUTPUT_HATCH); + RebarFluid outputFluid = matchingRecipe.output().fluid(); + if (!outputHatch.canAcceptFluid(outputFluid)) return; + + double recipeInputAmount = matchingRecipe.input().amountMillibuckets(); + double recipeOutputAmount = matchingRecipe.output().amount(); + double ratio = inputAmount / recipeInputAmount; + ratio = Math.min(ratio, outputHatch.getFluidSpaceRemaining() / recipeOutputAmount); + double actualInputAmount = ratio * recipeInputAmount; + double actualOutputAmount = ratio * recipeOutputAmount; + + inputHatch.removeFluid(actualInputAmount); + outputHatch.addFluid(outputFluid, actualOutputAmount); + + double powerOutput = matchingRecipe.powerProduction() * ratio; + electricityOutputHatch.setPower(powerOutput); + + Vector3f direction = getFacing().getOppositeFace().getDirection().toVector3f(); + + for (String entityId : getHeldEntities().keySet()) { + if (!entityId.startsWith("turbine_")) continue; + + ItemDisplay display = getHeldEntityOrThrow(ItemDisplay.class, entityId); + Matrix4f transform = TransformUtil.transformationToMatrix(display.getTransformation()); + + int stepsPerTick = 5; + for (int i = 0; i < stepsPerTick; i++) { + Bukkit.getScheduler().runTaskLater(Pylon.getInstance(), () -> { + transform.rotateLocal(new Quaternionf().rotateAxis((float) (2 * Math.PI / stepsPerTick), direction)); + if (display.isValid()) { + display.setInterpolationDelay(0); + display.setInterpolationDuration(tickInterval / stepsPerTick); + display.setTransformationMatrix(transform); + } + }, i * (tickInterval / stepsPerTick)); + } + } + + Vector3f acrossDirection = new Vector3f(direction).cross(Vector3fs.positiveY()).normalize(); + Vector3f veryBack = new Vector3f(direction).mul(-1.5f).sub(0, 0.5f, 0).sub(new Vector3f(acrossDirection).mul(0.5f)); + int particles = (int) Math.ceil(Math.log(inputAmount)); + for (int i = 0; i < particles; i++) { + Vector3f randomOffset = new Vector3f(acrossDirection).mul(ThreadLocalRandom.current().nextFloat()).add(0, ThreadLocalRandom.current().nextFloat(), 0); + Location spawnLocation = getBlock().getLocation().toCenterLocation().add(Vector.fromJOML(new Vector3f(veryBack).add(randomOffset))); + Bukkit.getScheduler().runTaskLater(Pylon.getInstance(), () -> { + Particle.POOF.builder() + .count(0) + .offset(direction.x, direction.y, direction.z) + .extra(0.5) + .location(spawnLocation) + .spawn(); + }, ThreadLocalRandom.current().nextInt(tickInterval)); + } + } + + @Override + public @Nullable WailaDisplay getWaila(@NotNull Player player) { + return new WailaDisplay(getDefaultWailaTranslationKey().arguments( + RebarArgument.of("power", UnitFormat.WATTS.format(getMultiblockComponentOrThrow(ElectricityOutputHatch.class, ELECTRICITY_OUTPUT_HATCH).getPower()).decimalPlaces(1)) + )); + } + + private static final Vector3i FLUID_INPUT_HATCH = new Vector3i(0, 0, -2); + private static final Vector3i FLUID_OUTPUT_HATCH = new Vector3i(0, 0, 2); + private static final Vector3i ELECTRICITY_OUTPUT_HATCH = new Vector3i(0, -1, 2); + + @Override + public @NotNull Map<@NotNull Vector3i, @NotNull MultiblockComponent> getComponents() { + Map components = new HashMap<>(); + + lineOfThree(0, 1, PylonKeys.REINFORCED_GLASS, components); + lineOfThree(-1, 0, PylonKeys.REINFORCED_GLASS, components); + lineOfThree(1, 0, PylonKeys.REINFORCED_GLASS, components); + + lineOfThree(0, -1, PylonKeys.STEEL_SUPPORT_BEAM, components); + + lineOfThree(0, -2, PylonKeys.BRONZE_FOUNDATION, components); + lineOfThree(-1, -1, PylonKeys.BRONZE_FOUNDATION, components); + lineOfThree(1, -1, PylonKeys.BRONZE_FOUNDATION, components); + + components.put(new Vector3i(0, -1, -2), MultiblockComponent.of(PylonKeys.BRONZE_FOUNDATION)); + + components.put(FLUID_INPUT_HATCH, MultiblockComponent.of(PylonKeys.FLUID_INPUT_HATCH)); + components.put(FLUID_OUTPUT_HATCH, MultiblockComponent.of(PylonKeys.FLUID_OUTPUT_HATCH)); + components.put(ELECTRICITY_OUTPUT_HATCH, MultiblockComponent.of(PylonKeys.ELECTRICITY_OUTPUT_HATCH)); + + return components; + } + + private static void lineOfThree(int x, int y, NamespacedKey key, Map components) { + components.put(new Vector3i(x, y, 0), MultiblockComponent.of(key)); + components.put(new Vector3i(x, y, 1), MultiblockComponent.of(key)); + components.put(new Vector3i(x, y, -1), MultiblockComponent.of(key)); + } + + @Override + public @Nullable ItemStack getBlockTextureItem() { + return isFormedAndFullyLoaded() ? null : super.getBlockTextureItem(); + } + + @Override + public void onMultiblockFormed() { + RebarSimpleMultiblock.super.onMultiblockFormed(); + getMultiblockComponentOrThrow(FluidInputHatch.class, FLUID_INPUT_HATCH) + .setAllowedFluids(GasTurbineRecipe.RECIPE_TYPE.stream().flatMap(r -> r.input().fluids().stream()).collect(Collectors.toSet())); + getMultiblockComponentOrThrow(FluidOutputHatch.class, FLUID_OUTPUT_HATCH) + .setAllowedFluids(GasTurbineRecipe.RECIPE_TYPE.stream().map(r -> r.output().fluid()).collect(Collectors.toSet())); + + if (getHeldEntity("turbine_shaft") == null) { + getBlock().setType(Material.STRUCTURE_VOID); + refreshBlockTextureItem(); + setupDisplay(); + } + } + + @Override + public void onMultiblockUnformed(boolean partUnloaded) { + RebarSimpleMultiblock.super.onMultiblockUnformed(partUnloaded); + + if (!partUnloaded) { + for (String entityId : new ArrayList<>(getHeldEntities().keySet())) { + if (entityId.startsWith("turbine_")) { + tryRemoveEntity(entityId); + } + } + + getBlock().setType(Material.IRON_BLOCK); + refreshBlockTextureItem(); + } + + ElectricityOutputHatch electricityOutputHatch = getMultiblockComponent(ElectricityOutputHatch.class, ELECTRICITY_OUTPUT_HATCH); + if (electricityOutputHatch != null) { + electricityOutputHatch.setPower(0); + } + } + + private void setupDisplay() { + addEntity("turbine_shaft", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_shaft")) + .transformation(new Matrix4f() + .scaleLocal(0.20031818431717888F, 0.1998618756962673F, 2.999928431318844F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.7001095104874101F, 0.7003343199606629F, 0.05027297059664036F) + .translateLocal(0F, 0F, 0.02499999999999991F) + .translateLocal(0F, 0F, -1.2F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk_1", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.5995684032343717F, 0.5998784121385513F, 0.049829445006917716F) + .translateLocal(0F, 0F, -0.025000000000000022F) + .translateLocal(0F, 0F, -1.1F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk_2", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.400408077237662F, 0.39961631863827995F, 0.049862215651233666F) + .translateLocal(0F, 0F, -0.02499999999999991F) + .translateLocal(0F, 0F, -1.05F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk_3", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.4998993373616677F, 0.4999216378035786F, 0.050083582762834034F) + .translateLocal(0F, 0F, -0.025000000000000022F) + .translateLocal(0F, 0F, -0.15000000000000002F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk_4", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.6000367675274685F, 0.6501684628404046F, 0.04992085141408112F) + .translateLocal(0F, -0.025000000000000022F, 0.02499999999999991F) + .translateLocal(0F, 0F, 0.6000000000000001F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk_5", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.5002561171050549F, 0.5496725403840674F, 0.050218433779457734F) + .translateLocal(0F, -0.02499999999999991F, -0.025000000000000133F) + .translateLocal(0F, 0F, 0.7F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk_6", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.2999681211206062F, 0.35012340582916684F, 0.049593864090870424F) + .translateLocal(0F, -0.025000000000000022F, -0.02499999999999991F) + .translateLocal(0F, 0F, 0.75F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + + addEntity("turbine_blisk_7", new ItemDisplayBuilder() + .itemStack(ItemStackBuilder.of(Registry.MATERIAL.getOrThrow(NamespacedKey.minecraft("iron_block"))) + .addCustomModelDataString(getKey() + ":turbine_blisk")) + .transformation(new Matrix4f() + .scaleLocal(0.5002451479182026F, 0.5003304693503907F, 0.049636693814377984F) + .translateLocal(0F, 0F, -0.025000000000000133F) + .translateLocal(0F, 0F, 1.05F) + .rotateLocal(new Quaternionf().lookAlong(getFacing().getDirection().toVector3f().mul(-1F, -1F, 1F), new Vector3f(0, 1, 0))) + ) + .build(getBlock().getLocation().toCenterLocation()) + ); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/HeatExchanger.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/HeatExchanger.java new file mode 100644 index 000000000..bfee90702 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/HeatExchanger.java @@ -0,0 +1,151 @@ +package io.github.pylonmc.pylon.content.machines.electricity.generation; + +import io.github.pylonmc.pylon.PylonKeys; +import io.github.pylonmc.pylon.content.components.FluidInputHatch; +import io.github.pylonmc.pylon.content.components.FluidOutputHatch; +import io.github.pylonmc.pylon.recipes.HeatExchangerRecipe; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.RebarSimpleMultiblock; +import io.github.pylonmc.rebar.block.base.RebarTickingBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.fluid.FluidWithAmount; +import io.github.pylonmc.rebar.fluid.RebarFluid; +import io.github.pylonmc.rebar.recipe.RecipeInput; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.bukkit.block.Block; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector3i; + +public class HeatExchanger extends RebarBlock implements + RebarSimpleMultiblock, + RebarTickingBlock { + + private final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + + @SuppressWarnings("unused") + public HeatExchanger(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + setTickInterval(tickInterval); + } + + @SuppressWarnings("unused") + public HeatExchanger(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void tick() { + if (!isFormedAndFullyLoaded()) return; + + FluidInputHatch inputHatch1 = getMultiblockComponentOrThrow(FluidInputHatch.class, INPUT_HATCH_1); + FluidInputHatch inputHatch2 = getMultiblockComponentOrThrow(FluidInputHatch.class, INPUT_HATCH_2); + FluidOutputHatch outputHatch1 = getMultiblockComponentOrThrow(FluidOutputHatch.class, OUTPUT_HATCH_1); + FluidOutputHatch outputHatch2 = getMultiblockComponentOrThrow(FluidOutputHatch.class, OUTPUT_HATCH_2); + FluidInputHatch fromInputHatch = null; + FluidOutputHatch fromOutputHatch = null; + FluidInputHatch toInputHatch = null; + FluidOutputHatch toOutputHatch = null; + HeatExchangerRecipe matchingRecipe = null; + for (HeatExchangerRecipe recipe : HeatExchangerRecipe.RECIPE_TYPE) { + if (recipe.transferFrom().getFirst().contains(inputHatch1.getFluid())) { + fromInputHatch = inputHatch1; + fromOutputHatch = outputHatch1; + toInputHatch = inputHatch2; + toOutputHatch = outputHatch2; + matchingRecipe = recipe; + break; + } else if (recipe.transferFrom().getFirst().contains(inputHatch2.getFluid())) { + fromInputHatch = inputHatch2; + fromOutputHatch = outputHatch2; + toInputHatch = inputHatch1; + toOutputHatch = outputHatch1; + matchingRecipe = recipe; + break; + } + } + + if (matchingRecipe == null) return; + + double recipeRatio = 1; + + FluidWithAmount fromOutput = matchingRecipe.transferFrom().getSecond(); + if (fromOutput != null) { + if (!fromOutputHatch.canAcceptFluid(fromOutput.fluid())) return; + double outputAmount = fromOutput.amount(); + double actualOutputAmount = Math.min(outputAmount, fromOutputHatch.getFluidSpaceRemaining()); + recipeRatio = Math.min(recipeRatio, actualOutputAmount / outputAmount); + } + FluidWithAmount toOutput = matchingRecipe.transferTo().getSecond(); + if (toOutput != null) { + if (!toOutputHatch.canAcceptFluid(toOutput.fluid())) return; + double outputAmount = toOutput.amount(); + double actualOutputAmount = Math.min(outputAmount, toOutputHatch.getFluidSpaceRemaining()); + recipeRatio = Math.min(recipeRatio, actualOutputAmount / outputAmount); + } + + RecipeInput.Fluid fromInput = matchingRecipe.transferFrom().getFirst(); + if (!fromInput.contains(fromInputHatch.getFluid())) return; + double fromInputAmount = fromInput.amountMillibuckets(); + double actualFromInputAmount = Math.min(fromInputAmount, fromInputHatch.getFluidAmount()); + recipeRatio = Math.min(recipeRatio, actualFromInputAmount / fromInputAmount); + + RecipeInput.Fluid toInput = matchingRecipe.transferTo().getFirst(); + if (!toInput.contains(toInputHatch.getFluid())) return; + double toInputAmount = toInput.amountMillibuckets(); + double actualToInputAmount = Math.min(toInputAmount, toInputHatch.getFluidAmount()); + recipeRatio = Math.min(recipeRatio, actualToInputAmount / toInputAmount); + + recipeRatio /= getTicksPerSecond(); // Convert from per-second to per-tick + + fromInputHatch.removeFluid(fromInputAmount * recipeRatio); + toInputHatch.removeFluid(toInputAmount * recipeRatio); + if (fromOutput != null) { + fromOutputHatch.addFluid(fromOutput.fluid(), fromOutput.amount() * recipeRatio); + } + if (toOutput != null) { + toOutputHatch.addFluid(toOutput.fluid(), toOutput.amount() * recipeRatio); + } + } + + private static final Vector3i INPUT_HATCH_1 = new Vector3i(1, 0, 0); + private static final Vector3i OUTPUT_HATCH_1 = new Vector3i(-1, 0, 0); + private static final Vector3i INPUT_HATCH_2 = new Vector3i(0, 0, -1); + private static final Vector3i OUTPUT_HATCH_2 = new Vector3i(0, 0, 1); + + @Override + public void onMultiblockFormed() { + RebarSimpleMultiblock.super.onMultiblockFormed(); + + Set allowedInputs = HeatExchangerRecipe.RECIPE_TYPE.stream() + .flatMap(recipe -> Stream.of(recipe.transferFrom().getFirst(), recipe.transferTo().getFirst())) + .flatMap(input -> input.fluids().stream()) + .collect(Collectors.toSet()); + getMultiblockComponentOrThrow(FluidInputHatch.class, INPUT_HATCH_1).setAllowedFluids(allowedInputs); + getMultiblockComponentOrThrow(FluidInputHatch.class, INPUT_HATCH_2).setAllowedFluids(allowedInputs); + + Set allowedOutputs = HeatExchangerRecipe.RECIPE_TYPE.stream() + .flatMap(recipe -> Stream.of(recipe.transferFrom().getSecond(), recipe.transferTo().getSecond())) + .filter(Objects::nonNull) + .map(FluidWithAmount::fluid) + .collect(Collectors.toSet()); + getMultiblockComponentOrThrow(FluidOutputHatch.class, OUTPUT_HATCH_1).setAllowedFluids(allowedOutputs); + getMultiblockComponentOrThrow(FluidOutputHatch.class, OUTPUT_HATCH_2).setAllowedFluids(allowedOutputs); + } + + @Override + public @NotNull Map<@NotNull Vector3i, @NotNull MultiblockComponent> getComponents() { + return Map.of( + INPUT_HATCH_1, MultiblockComponent.of(PylonKeys.FLUID_INPUT_HATCH), + OUTPUT_HATCH_1, MultiblockComponent.of(PylonKeys.FLUID_OUTPUT_HATCH), + INPUT_HATCH_2, MultiblockComponent.of(PylonKeys.FLUID_INPUT_HATCH), + OUTPUT_HATCH_2, MultiblockComponent.of(PylonKeys.FLUID_OUTPUT_HATCH) + ); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/SteamEngine.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/SteamEngine.java new file mode 100644 index 000000000..6e9423d62 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/generation/SteamEngine.java @@ -0,0 +1,118 @@ +package io.github.pylonmc.pylon.content.machines.electricity.generation; + +import io.github.pylonmc.pylon.PylonFluids; +import io.github.pylonmc.pylon.PylonKeys; +import io.github.pylonmc.pylon.util.PylonUtils; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.*; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.electricity.ElectricNode; +import io.github.pylonmc.rebar.fluid.FluidPointType; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import io.github.pylonmc.rebar.util.position.BlockPosition; +import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.List; +import java.util.Map; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3i; + +public class SteamEngine extends RebarBlock implements + RebarDirectionalBlock, + RebarFluidBufferBlock, + RebarElectricBlock, + RebarTickingBlock, + RebarSimpleMultiblock { + + private final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + private final double steamUsage = getSettings().getOrThrow("steam-usage", ConfigAdapter.DOUBLE); + private final double steamCapacity = getSettings().getOrThrow("steam-capacity", ConfigAdapter.DOUBLE); + private final double powerProduction = getSettings().getOrThrow("power-production", ConfigAdapter.DOUBLE); + + public static final class Item extends RebarItem { + + private final double steamUsage = getSettings().getOrThrow("steam-usage", ConfigAdapter.DOUBLE); + private final double steamCapacity = getSettings().getOrThrow("steam-capacity", ConfigAdapter.DOUBLE); + private final double powerProduction = getSettings().getOrThrow("power-production", ConfigAdapter.DOUBLE); + + public Item(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public @NotNull List<@NotNull RebarArgument> getPlaceholders() { + return List.of( + RebarArgument.of("steam-usage", UnitFormat.MILLIBUCKETS_PER_SECOND.format(steamUsage)), + RebarArgument.of("steam-capacity", UnitFormat.MILLIBUCKETS.format(steamCapacity)), + RebarArgument.of("power-production", UnitFormat.WATTS.format(powerProduction)) + ); + } + } + + private ElectricNode.Producer node; + + @SuppressWarnings("unused") + public SteamEngine(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + setTickInterval(tickInterval); + createFluidPoint(FluidPointType.INPUT, context.getFacing()); + createFluidBuffer(PylonFluids.STEAM, steamCapacity, true, false); + addElectricPort(context.getFacing().getOppositeFace(), new ElectricNode.Producer("output", new BlockPosition(block), 0)); + } + + @SuppressWarnings("unused") + public SteamEngine(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void postInitialise() { + node = (ElectricNode.Producer) getElectricNodeOrThrow("output"); + } + + @Override + public @NotNull Map<@NotNull Vector3i, @NotNull MultiblockComponent> getComponents() { + return Map.of(new Vector3i(0, 1, 0), MultiblockComponent.of(PylonKeys.SMOKESTACK_CAP)); + } + + @Override + public void tick() { + double adjustedSteamUsage = tickInterval / 20.0 * steamUsage; + if (fluidAmount(PylonFluids.STEAM) < adjustedSteamUsage) { + node.setPower(0); + return; + } + removeFluid(PylonFluids.STEAM, adjustedSteamUsage); + node.setPower(powerProduction); + + Particle.CAMPFIRE_SIGNAL_SMOKE.builder() + .location(getBlock().getLocation().add(0, 1, 0).toCenterLocation()) + .offset(0, 1, 0) + .count(0) + .extra(0.03) + .spawn(); + } + + @Override + public @Nullable WailaDisplay getWaila(@NotNull Player player) { + return new WailaDisplay(getDefaultWailaTranslationKey().arguments( + RebarArgument.of("bar", PylonUtils.createFluidAmountBar( + fluidAmount(PylonFluids.STEAM), + fluidCapacity(PylonFluids.STEAM), + 20, + TextColor.fromHexString("#d8d8d8") + )), + RebarArgument.of("power", UnitFormat.WATTS.format(node.getPower()).decimalPlaces(1)) + )); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/machines/ElectricBrickMolder.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/machines/ElectricBrickMolder.java new file mode 100644 index 000000000..154421573 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/machines/ElectricBrickMolder.java @@ -0,0 +1,60 @@ +package io.github.pylonmc.pylon.content.machines.electricity.machines; + +import io.github.pylonmc.pylon.content.machines.generic.AbstractBrickMolder; +import io.github.pylonmc.rebar.block.base.RebarElectricConsumerBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.List; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; + +public class ElectricBrickMolder extends AbstractBrickMolder implements RebarElectricConsumerBlock { + + private final double powerUsage = getSettings().getOrThrow("power-usage", ConfigAdapter.DOUBLE); + + public static class Item extends RebarItem { + + private final double powerUsage = getSettings().getOrThrow("power-usage", ConfigAdapter.DOUBLE); + private final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + private final int ticksPerMoldingCycle = getSettings().getOrThrow("ticks-per-molding-cycle", ConfigAdapter.INTEGER); + + public Item(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public @NotNull List<@NotNull RebarArgument> getPlaceholders() { + return List.of( + RebarArgument.of("molding-cycles", UnitFormat.CYCLES_PER_SECOND.format(20 / (ticksPerMoldingCycle * tickInterval))), + RebarArgument.of("power-usage", UnitFormat.WATTS.format(powerUsage)) + ); + } + } + + @SuppressWarnings("unused") + public ElectricBrickMolder(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + } + + @SuppressWarnings("unused") + public ElectricBrickMolder(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void postInitialise() { + super.postInitialise(); + setRequiredPower(powerUsage); + } + + @Override + public void tick() { + if (!isProcessingRecipe() || !isPowered()) return; + progressRecipe(tickInterval); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/machines/ElectricGrindstone.java b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/machines/ElectricGrindstone.java new file mode 100644 index 000000000..d7672b80a --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/electricity/machines/ElectricGrindstone.java @@ -0,0 +1,57 @@ +package io.github.pylonmc.pylon.content.machines.electricity.machines; + +import io.github.pylonmc.pylon.content.machines.generic.AbstractGrindstone; +import io.github.pylonmc.rebar.block.base.RebarElectricConsumerBlock; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.List; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; + +public class ElectricGrindstone extends AbstractGrindstone implements + RebarElectricConsumerBlock { + + private final double powerUsage = getSettings().getOrThrow("power-usage", ConfigAdapter.DOUBLE); + + public static class Item extends RebarItem { + + public final double powerUsage = getSettings().getOrThrow("power-usage", ConfigAdapter.DOUBLE); + + public Item(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public @NotNull List<@NotNull RebarArgument> getPlaceholders() { + return List.of(RebarArgument.of("power-usage", UnitFormat.WATTS.format(powerUsage))); + } + } + + @SuppressWarnings("unused") + public ElectricGrindstone(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + } + + @SuppressWarnings("unused") + public ElectricGrindstone(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void postInitialise() { + super.postInitialise(); + setRequiredPower(powerUsage); + } + + @Override + public void tick() { + if (!isProcessingRecipe() || !isPowered()) return; + progressRecipe(tickInterval); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidAccumulator.java b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidAccumulator.java index 180b19d24..a3a2fe2e3 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidAccumulator.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidAccumulator.java @@ -1,5 +1,6 @@ package io.github.pylonmc.pylon.content.machines.fluid; +import io.github.pylonmc.pylon.util.NumberInputButton; import io.github.pylonmc.pylon.util.PylonUtils; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; @@ -29,16 +30,11 @@ import org.bukkit.block.BlockFace; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jspecify.annotations.NonNull; -import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.item.AbstractItem; -import xyz.xenondevs.invui.item.ItemProvider; import java.util.List; @@ -156,7 +152,18 @@ public boolean isAllowedFluid(@NotNull RebarFluid fluid) { return Gui.builder() .setStructure("# # # # m # # # #") .addIngredient('#', GuiItems.background()) - .addIngredient('m', new AmountItem()) + .addIngredient('m', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.amount")) + .increment(10) + .shiftIncrement(100) + .min(minAmount) + .max(maxAmount) + .valueGetter(() -> (int) getFluidCapacity()) + .valueSetter(this::setCapacity) + .valueFormatter(UnitFormat.MILLIBUCKETS::format) + .reopenWindow(this::openWindow) + .build()) .build(); } @@ -197,32 +204,4 @@ public double fluidAmountRequested(@NotNull RebarFluid fluid) { return List.of(); } - - public class AmountItem extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NonNull Player player) { - return ItemStackBuilder.of(Material.WHITE_CONCRETE) - .name(Component.translatable("pylon.gui.fluid_accumulator.name").arguments( - RebarArgument.of("amount", UnitFormat.MILLIBUCKETS.format(getFluidCapacity())) - )) - .lore(Component.translatable("pylon.gui.fluid_accumulator.lore")); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - double delta = clickType.isShiftClick() ? 100 : 10; - double newAmount; - if (clickType.isLeftClick()) { - newAmount = getFluidCapacity() + delta; - } else if (clickType.isRightClick()) { - newAmount = getFluidCapacity() - delta; - } else { - newAmount = getFluidCapacity(); - } - newAmount = Math.clamp(newAmount, minAmount, maxAmount); - setCapacity(newAmount); - notifyWindows(); - } - } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidLimiter.java b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidLimiter.java index dacf80d34..e2a90021f 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidLimiter.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidLimiter.java @@ -1,5 +1,6 @@ package io.github.pylonmc.pylon.content.machines.fluid; +import io.github.pylonmc.pylon.util.NumberInputButton; import io.github.pylonmc.pylon.util.PylonUtils; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; @@ -25,16 +26,11 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jspecify.annotations.NonNull; -import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.item.AbstractItem; -import xyz.xenondevs.invui.item.ItemProvider; import java.util.List; @@ -156,31 +152,18 @@ public double fluidAmountRequested(@NotNull RebarFluid fluid) { return Gui.builder() .setStructure("# # # # m # # # #") .addIngredient('#', GuiItems.background()) - .addIngredient('m', new MaxFlowRateItem()) + .addIngredient('m', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.max-flow-rate")) + .increment(10) + .shiftIncrement(100) + .min(minAmount) + .max(maxAmount) + .valueGetter(() -> maxFlowRate) + .valueSetter(value -> maxFlowRate = value) + .valueFormatter(UnitFormat.MILLIBUCKETS_PER_SECOND::format) + .reopenWindow(this::openWindow) + .build()) .build(); } - - public class MaxFlowRateItem extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NotNull Player player) { - return ItemStackBuilder.of(Material.WHITE_CONCRETE) - .name(Component.translatable("pylon.gui.fluid_accumulator.name").arguments( - RebarArgument.of("amount", UnitFormat.MILLIBUCKETS_PER_SECOND.format(maxFlowRate)) - )) - .lore(Component.translatable("pylon.gui.fluid_accumulator.lore")); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - int delta = clickType.isShiftClick() ? 100 : 10; - if (clickType.isLeftClick()) { - maxFlowRate += delta; - } else if (clickType.isRightClick()) { - maxFlowRate -= delta; - } - maxFlowRate = Math.clamp(maxFlowRate, minAmount, maxAmount); - notifyWindows(); - } - } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidMeter.java b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidMeter.java index 46f88fe23..f56388c5f 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidMeter.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/FluidMeter.java @@ -1,5 +1,6 @@ package io.github.pylonmc.pylon.content.machines.fluid; +import io.github.pylonmc.pylon.util.NumberInputButton; import io.github.pylonmc.pylon.util.PylonUtils; import io.github.pylonmc.rebar.block.RebarBlock; import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; @@ -32,17 +33,12 @@ import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Player; import org.bukkit.entity.TextDisplay; -import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Vector3d; -import org.jspecify.annotations.NonNull; -import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.item.AbstractItem; -import xyz.xenondevs.invui.item.ItemProvider; import java.time.Duration; import java.util.ArrayList; @@ -188,7 +184,18 @@ public void write(@NotNull PersistentDataContainer pdc) { return Gui.builder() .setStructure("# # # # m # # # #") .addIngredient('#', GuiItems.background()) - .addIngredient('m', new MeasurementDurationItem()) + .addIngredient('m', NumberInputButton.builder() + .material(Material.WHITE_CONCRETE) + .name(Component.translatable("pylon.gui.measurement-duration")) + .increment(1) + .shiftIncrement(10) + .min(minNumberOfMeasurements) + .max(maxNumberOfMeasurements) + .valueGetter(() -> numberOfMeasurements) + .valueSetter(value -> numberOfMeasurements = value) + .valueFormatter(value -> UnitFormat.formatDuration(getDuration(value), true, true)) + .reopenWindow(this::openWindow) + .build()) .build(); } @@ -247,30 +254,4 @@ public boolean isAllowedFluid(@NotNull RebarFluid fluid) { public static Duration getDuration(int numberOfMeasurements) { return Duration.ofMillis((long) numberOfMeasurements * RebarConfig.FLUID_TICK_INTERVAL * 50); } - - public class MeasurementDurationItem extends AbstractItem { - - @Override - public @NonNull ItemProvider getItemProvider(@NotNull Player viewer) { - return ItemStackBuilder.of(Material.WHITE_CONCRETE) - .name(Component.translatable("pylon.gui.fluid_meter.name").arguments( - RebarArgument.of("measurement-duration", UnitFormat.formatDuration(getDuration(numberOfMeasurements), true, true)) - )) - .lore(Component.translatable("pylon.gui.fluid_meter.lore")); - } - - @Override - public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { - int newValue; - if (clickType.isLeftClick()) { - newValue = numberOfMeasurements + (clickType.isShiftClick() ? 10 : 1); - } else if (clickType.isRightClick()) { - newValue = numberOfMeasurements + (clickType.isShiftClick() ? -10 : -1); - } else { - newValue = numberOfMeasurements; - } - numberOfMeasurements = Math.clamp(newValue, minNumberOfMeasurements, maxNumberOfMeasurements); - notifyWindows(); - } - } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/PortableFluidTank.java b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/PortableFluidTank.java index 8244ca82a..2b0d62224 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/PortableFluidTank.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/fluid/PortableFluidTank.java @@ -19,6 +19,10 @@ import io.github.pylonmc.rebar.waila.WailaDisplay; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.item.CustomModelData; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import lombok.Getter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.JoinConfiguration; @@ -34,11 +38,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; @@ -167,7 +166,7 @@ public boolean isAllowedFluid(@NotNull RebarFluid fluid) { @Override public @Nullable ItemStack getPickItem() { // TODO implement clone for RebarItem and just clone it - ItemStack stack = RebarRegistry.ITEMS.getOrThrow(getKey()).getItemStack(); + ItemStack stack = RebarRegistry.ITEMS.getOrThrow(getKey()).createNewItemStack(); Item item = new Item(stack); item.setFluid(getFluidType()); diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/generic/AbstractBrickMolder.java b/src/main/java/io/github/pylonmc/pylon/content/machines/generic/AbstractBrickMolder.java new file mode 100644 index 000000000..3f6059fc7 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/generic/AbstractBrickMolder.java @@ -0,0 +1,123 @@ +package io.github.pylonmc.pylon.content.machines.generic; + +import io.github.pylonmc.pylon.recipes.MoldingRecipe; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.*; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; +import io.github.pylonmc.rebar.logistics.LogisticGroupType; +import io.github.pylonmc.rebar.util.MachineUpdateReason; +import io.github.pylonmc.rebar.util.RebarUtils; +import io.github.pylonmc.rebar.util.gui.GuiItems; +import io.github.pylonmc.rebar.util.gui.ProgressItem; +import java.util.Map; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.inventory.VirtualInventory; + +public abstract class AbstractBrickMolder extends RebarBlock implements + RebarGuiBlock, + RebarVirtualInventoryBlock, + RebarDirectionalBlock, + RebarTickingBlock, + RebarLogisticBlock, + RebarRecipeProcessor { + + public final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + public final int ticksPerMoldingCycle = getSettings().getOrThrow("ticks-per-molding-cycle", ConfigAdapter.INTEGER); + + public AbstractBrickMolder(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + setFacing(context.getFacing()); + setTickInterval(tickInterval); + setRecipeType(MoldingRecipe.RECIPE_TYPE); + setRecipeProgressItem(new ProgressItem(GuiItems.background())); + } + + public AbstractBrickMolder(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + private final VirtualInventory inputInventory = new VirtualInventory(1); + private final VirtualInventory outputInventory = new VirtualInventory(1); + + @Override + public @NotNull Map getVirtualInventories() { + return Map.of( + "input", inputInventory, + "output", outputInventory + ); + } + + @Override + public void postInitialise() { + createLogisticGroup("input", LogisticGroupType.INPUT, inputInventory); + createLogisticGroup("output", LogisticGroupType.OUTPUT, outputInventory); + outputInventory.addPreUpdateHandler(RebarUtils.DISALLOW_PLAYERS_FROM_ADDING_ITEMS_HANDLER); + outputInventory.addPostUpdateHandler(event -> tryStartRecipe()); + inputInventory.addPostUpdateHandler(event -> { + if (!(event.getUpdateReason() instanceof MachineUpdateReason)) { + tryStartRecipe(); + } + }); + } + + public void tryStartRecipe() { + if (isProcessingRecipe()) { + return; + } + + ItemStack stack = inputInventory.getItem(0); + if (stack == null || stack.isEmpty()) { + return; + } + + if (getLastRecipe() != null && tryStartRecipe(getLastRecipe(), stack)) { + return; + } + + for (MoldingRecipe recipe : MoldingRecipe.RECIPE_TYPE) { + if (tryStartRecipe(recipe, stack)) { + break; + } + } + } + + protected boolean tryStartRecipe(MoldingRecipe recipe, ItemStack stack) { + if (!recipe.input().isSimilar(stack) || !outputInventory.canHold(recipe.result())) { + return false; + } + + startRecipe(recipe, recipe.moldingCycles() * tickInterval * ticksPerMoldingCycle); + getRecipeProgressItem().setItem(ItemStackBuilder.of(stack.asOne()).clearLore()); + inputInventory.setItem(new MachineUpdateReason(), 0, stack.subtract(recipe.input().getAmount())); + return true; + } + + @Override + public void onRecipeFinished(@NotNull MoldingRecipe recipe) { + getRecipeProgressItem().setItem(GuiItems.background()); + outputInventory.addItem(new MachineUpdateReason(), recipe.result().clone()); + } + + @Override + public @NotNull Gui createGui() { + return Gui.builder() + .setStructure( + "# # I # # # O # #", + "# # i # p # o # #", + "# # I # # # O # #" + ) + .addIngredient('#', GuiItems.background()) + .addIngredient('I', GuiItems.input()) + .addIngredient('i', inputInventory) + .addIngredient('p', getRecipeProgressItem()) + .addIngredient('O', GuiItems.output()) + .addIngredient('o', outputInventory) + .build(); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/generic/AbstractGrindstone.java b/src/main/java/io/github/pylonmc/pylon/content/machines/generic/AbstractGrindstone.java new file mode 100644 index 000000000..8fb578600 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/generic/AbstractGrindstone.java @@ -0,0 +1,128 @@ +package io.github.pylonmc.pylon.content.machines.generic; + +import io.github.pylonmc.pylon.content.machines.simple.Grindstone; +import io.github.pylonmc.pylon.recipes.GrindstoneRecipe; +import io.github.pylonmc.rebar.block.RebarBlock; +import io.github.pylonmc.rebar.block.base.*; +import io.github.pylonmc.rebar.block.context.BlockCreateContext; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; +import io.github.pylonmc.rebar.logistics.LogisticGroupType; +import io.github.pylonmc.rebar.util.MachineUpdateReason; +import io.github.pylonmc.rebar.util.RebarUtils; +import io.github.pylonmc.rebar.util.gui.GuiItems; +import io.github.pylonmc.rebar.util.gui.ProgressItem; +import java.util.List; +import java.util.Map; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.inventory.VirtualInventory; + +public abstract class AbstractGrindstone extends RebarBlock implements + RebarGuiBlock, + RebarVirtualInventoryBlock, + RebarTickingBlock, + RebarLogisticBlock, + RebarRecipeProcessor { + + public final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER); + + private final VirtualInventory inputInventory = new VirtualInventory(1); + private final VirtualInventory outputInventory = new VirtualInventory(3); + + @SuppressWarnings("unused") + public AbstractGrindstone(@NotNull Block block, @NotNull BlockCreateContext context) { + super(block, context); + + setTickInterval(tickInterval); + setRecipeType(GrindstoneRecipe.RECIPE_TYPE); + setRecipeProgressItem(new ProgressItem(GuiItems.background())); + } + + @SuppressWarnings("unused") + public AbstractGrindstone(@NotNull Block block, @NotNull PersistentDataContainer pdc) { + super(block, pdc); + } + + @Override + public void postInitialise() { + createLogisticGroup("input", LogisticGroupType.INPUT, inputInventory); + createLogisticGroup("output", LogisticGroupType.OUTPUT, outputInventory); + outputInventory.addPreUpdateHandler(RebarUtils.DISALLOW_PLAYERS_FROM_ADDING_ITEMS_HANDLER); + outputInventory.addPostUpdateHandler(event -> tryStartRecipe()); + inputInventory.addPostUpdateHandler(event -> { + if (!(event.getUpdateReason() instanceof MachineUpdateReason)) { + tryStartRecipe(); + } + }); + } + + public void tryStartRecipe() { + if (isProcessingRecipe()) { + return; + } + + ItemStack stack = inputInventory.getItem(0); + if (stack == null) { + return; + } + + if (getLastRecipe() != null && tryStartRecipe(getLastRecipe(), stack)) { + return; + } + + for (GrindstoneRecipe recipe : GrindstoneRecipe.RECIPE_TYPE) { + if (tryStartRecipe(recipe, stack)) { + return; + } + } + } + + private boolean tryStartRecipe(GrindstoneRecipe recipe, ItemStack stack) { + if (!recipe.input().matches(stack)) { + return false; + } + + if (!outputInventory.canHold(List.copyOf(recipe.results().getElements()))) { + return true; + } + + startRecipe(recipe, recipe.cycles() * Grindstone.CYCLE_DURATION_TICKS); + getRecipeProgressItem().setItem(ItemStackBuilder.of(stack.asOne()).clearLore()); + inputInventory.setItem(new MachineUpdateReason(), 0, stack.subtract(recipe.input().getAmount())); + return true; + } + + @Override + public void onRecipeFinished(@NotNull GrindstoneRecipe recipe) { + getRecipeProgressItem().setItem(GuiItems.background()); + outputInventory.addItem(null, recipe.results().getRandom()); + } + + @Override + public @NotNull Gui createGui() { + return Gui.builder() + .setStructure( + "# I # # # O O O #", + "# i # p # o o o #", + "# I # # # O O O #" + ) + .addIngredient('#', GuiItems.background()) + .addIngredient('I', GuiItems.input()) + .addIngredient('i', inputInventory) + .addIngredient('O', GuiItems.output()) + .addIngredient('o', outputInventory) + .addIngredient('p', getRecipeProgressItem()) + .build(); + } + @Override + public @NotNull Map getVirtualInventories() { + return Map.of( + "input", inputInventory, + "output", outputInventory + ); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/BurnerHydraulicPurifier.java b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/BurnerHydraulicPurifier.java index 8a0a89ac0..b8b3c5fc6 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/BurnerHydraulicPurifier.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/BurnerHydraulicPurifier.java @@ -16,7 +16,10 @@ import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.waila.WailaDisplay; -import net.kyori.adventure.text.Component; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Particle; import org.bukkit.block.Block; @@ -32,11 +35,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Vector3i; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - public class BurnerHydraulicPurifier extends RebarBlock implements RebarSimpleMultiblock, @@ -83,7 +81,6 @@ public BurnerHydraulicPurifier(@NotNull Block block, @NotNull BlockCreateContext super(block, context); setTickInterval(tickInterval); setFacing(context.getFacing()); - setMultiblockDirection(context.getFacing()); } @SuppressWarnings("unused") @@ -149,8 +146,8 @@ public void tick() { double fluidToPurify = Math.min( hydraulicFluidPerMachineTick, Math.min( - fluidInput.fluidAmount(PylonFluids.DIRTY_HYDRAULIC_FLUID), - fluidOutput.fluidSpaceRemaining(PylonFluids.HYDRAULIC_FLUID) + fluidInput.getFluidAmount(), + fluidOutput.getFluidSpaceRemaining() ) ); if (fluidToPurify < hydraulicFluidPerMachineTick) { @@ -161,7 +158,7 @@ public void tick() { lightable.setLit(true); getBlock().setBlockData(lightable); - fluidInput.removeFluid(PylonFluids.DIRTY_HYDRAULIC_FLUID, fluidToPurify); + fluidInput.removeFluid(fluidToPurify); fluidOutput.addFluid(PylonFluids.HYDRAULIC_FLUID, fluidToPurify * purificationEfficiency); progressProcess(getTickInterval()); @@ -200,11 +197,11 @@ public void tryConsumeFuel() { @Override public void onMultiblockFormed() { + RebarSimpleMultiblock.super.onMultiblockFormed(); getMultiblockComponentOrThrow(FluidInputHatch.class, FLUID_INPUT) - .setFluidType(PylonFluids.DIRTY_HYDRAULIC_FLUID); + .setAllowedFluids(PylonFluids.DIRTY_HYDRAULIC_FLUID); getMultiblockComponentOrThrow(FluidOutputHatch.class, FLUID_OUTPUT) - .setFluidType(PylonFluids.HYDRAULIC_FLUID); - RebarSimpleMultiblock.super.onMultiblockFormed(); + .setAllowedFluids(PylonFluids.HYDRAULIC_FLUID); } @Override diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/CoalFiredPurificationTower.java b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/CoalFiredPurificationTower.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/ConvectionHydraulicPurifier.java b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/ConvectionHydraulicPurifier.java index 24bf19fd7..02214fdc1 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/ConvectionHydraulicPurifier.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/ConvectionHydraulicPurifier.java @@ -19,6 +19,10 @@ import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Material; @@ -35,11 +39,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Vector3i; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - public class ConvectionHydraulicPurifier extends RebarBlock implements RebarSimpleMultiblock, @@ -91,7 +90,6 @@ public ConvectionHydraulicPurifier(@NotNull Block block, @NotNull BlockCreateCon super(block, context); setTickInterval(tickInterval); setFacing(context.getFacing()); - setMultiblockDirection(context.getFacing()); addEntity("lava1", new ItemDisplayBuilder() .material(Material.ORANGE_CONCRETE) .brightness(0) @@ -180,8 +178,8 @@ public void tick() { FluidInputHatch hydraulicInput = getMultiblockComponentOrThrow(FluidInputHatch.class, HYDRAULIC_FLUID_INPUT); FluidOutputHatch hydraulicOutput = getMultiblockComponentOrThrow(FluidOutputHatch.class, HYDRAULIC_FLUID_OUTPUT); - double inputFluidAvailable = hydraulicInput.fluidAmount(PylonFluids.DIRTY_HYDRAULIC_FLUID); - double outputSpaceRemaining = hydraulicOutput.fluidSpaceRemaining(PylonFluids.HYDRAULIC_FLUID); + double inputFluidAvailable = hydraulicInput.getFluidAmount(); + double outputSpaceRemaining = hydraulicOutput.getFluidSpaceRemaining(); double fluidToPurify = Math.min(purificationSpeed, Math.min(inputFluidAvailable, outputSpaceRemaining)) * getTickInterval() / 20; double efficiency = getEfficiency(); @@ -200,16 +198,14 @@ public void tick() { .spawn(); } - hydraulicInput.removeFluid(PylonFluids.DIRTY_HYDRAULIC_FLUID, fluidToPurify); + hydraulicInput.removeFluid(fluidToPurify); hydraulicOutput.addFluid(PylonFluids.HYDRAULIC_FLUID, efficiency * fluidToPurify); } @Override public void onMultiblockFormed() { - FluidInputHatch hydraulicInput = getMultiblockComponentOrThrow(FluidInputHatch.class, HYDRAULIC_FLUID_INPUT); - FluidOutputHatch hydraulicOutput = getMultiblockComponentOrThrow(FluidOutputHatch.class, HYDRAULIC_FLUID_OUTPUT); - hydraulicInput.setFluidType(PylonFluids.DIRTY_HYDRAULIC_FLUID); - hydraulicOutput.setFluidType(PylonFluids.HYDRAULIC_FLUID); + getMultiblockComponentOrThrow(FluidInputHatch.class, HYDRAULIC_FLUID_INPUT).setAllowedFluids(PylonFluids.DIRTY_HYDRAULIC_FLUID); + getMultiblockComponentOrThrow(FluidOutputHatch.class, HYDRAULIC_FLUID_OUTPUT).setAllowedFluids(PylonFluids.HYDRAULIC_FLUID); Block light = getBlock().getRelative(BlockFace.UP); if (light.getType().isAir()) { light.setType(Material.LIGHT); diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicCoreDrill.java b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicCoreDrill.java index 83efc1d39..1e98d3b4a 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicCoreDrill.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicCoreDrill.java @@ -11,6 +11,10 @@ import io.github.pylonmc.rebar.i18n.RebarArgument; import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.inventory.ItemStack; @@ -18,11 +22,6 @@ import org.jetbrains.annotations.NotNull; import org.joml.Vector3i; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class HydraulicCoreDrill extends CoreDrill { public final int hydraulicFluidUsage = getSettings().getOrThrow("hydraulic-fluid-usage", ConfigAdapter.INTEGER); @@ -105,14 +104,14 @@ public void tick() { FluidOutputHatch outputHatch = getMultiblockComponentOrThrow(FluidOutputHatch.class, FLUID_OUTPUT_HATCH); ItemOutputHatch itemOutputHatch = getMultiblockComponentOrThrow(ItemOutputHatch.class, ITEM_OUTPUT_HATCH); - if (inputHatch.fluidAmount(PylonFluids.HYDRAULIC_FLUID) < hydraulicFluidPerRotation - || outputHatch.fluidSpaceRemaining(PylonFluids.DIRTY_HYDRAULIC_FLUID) < hydraulicFluidPerRotation + if (inputHatch.getFluidAmount() < hydraulicFluidPerRotation + || outputHatch.getFluidSpaceRemaining() < hydraulicFluidPerRotation || !itemOutputHatch.inventory.canHold(output) ) { return; } - inputHatch.removeFluid(PylonFluids.HYDRAULIC_FLUID, hydraulicFluidPerRotation); + inputHatch.removeFluid(hydraulicFluidPerRotation); outputHatch.addFluid(PylonFluids.DIRTY_HYDRAULIC_FLUID, hydraulicFluidPerRotation); if (!isProcessing()) { @@ -133,8 +132,8 @@ public void onProcessFinished() { public void onMultiblockFormed() { super.onMultiblockFormed(); getMultiblockComponentOrThrow(FluidInputHatch.class, FLUID_INPUT_HATCH) - .setFluidType(PylonFluids.HYDRAULIC_FLUID); + .setAllowedFluids(PylonFluids.HYDRAULIC_FLUID); getMultiblockComponentOrThrow(FluidOutputHatch.class, FLUID_OUTPUT_HATCH) - .setFluidType(PylonFluids.DIRTY_HYDRAULIC_FLUID); + .setAllowedFluids(PylonFluids.DIRTY_HYDRAULIC_FLUID); } } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicPurifier.java b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicPurifier.java index 6189d2278..3a1d99bfa 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicPurifier.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/HydraulicPurifier.java @@ -11,14 +11,13 @@ import io.github.pylonmc.rebar.recipe.RecipeType; import io.github.pylonmc.rebar.registry.RebarRegistry; import io.github.pylonmc.rebar.util.gui.GuiItems; +import java.util.ArrayList; +import java.util.List; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import xyz.xenondevs.invui.gui.Gui; -import java.util.ArrayList; -import java.util.List; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; public interface HydraulicPurifier { @@ -71,7 +70,7 @@ static List getPurifiers() { RebarItemSchema itemSchema = RebarRegistry.ITEMS.get(blockSchema.getKey()); if (itemSchema == null) continue; // should never happen - purifiers.add(itemSchema.getItemStack()); + purifiers.add(itemSchema.createNewItemStack()); } return purifiers; diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/LiseletteHydraulicPurifier.java b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/LiseletteHydraulicPurifier.java index cd35081b7..035a1327c 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/LiseletteHydraulicPurifier.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/hydraulics/LiseletteHydraulicPurifier.java @@ -18,6 +18,10 @@ import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.util.position.BlockPosition; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; import org.bukkit.Bukkit; import org.bukkit.Particle; import org.bukkit.block.Block; @@ -29,11 +33,6 @@ import org.jetbrains.annotations.NotNull; import org.joml.Vector3i; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - public class LiseletteHydraulicPurifier extends RebarBlock implements RebarSimpleMultiblock, @@ -123,8 +122,8 @@ public LiseletteHydraulicPurifier(@NotNull Block block, @NotNull PersistentDataC @Override public void onMultiblockFormed() { RebarSimpleMultiblock.super.onMultiblockFormed(); - getMultiblockComponentOrThrow(FluidInputHatch.class, INPUT_HATCH).setFluidType(PylonFluids.DIRTY_HYDRAULIC_FLUID); - getMultiblockComponentOrThrow(FluidOutputHatch.class, OUTPUT_HATCH).setFluidType(PylonFluids.HYDRAULIC_FLUID); + getMultiblockComponentOrThrow(FluidInputHatch.class, INPUT_HATCH).setAllowedFluids(PylonFluids.DIRTY_HYDRAULIC_FLUID); + getMultiblockComponentOrThrow(FluidOutputHatch.class, OUTPUT_HATCH).setAllowedFluids(PylonFluids.HYDRAULIC_FLUID); } @Override @@ -166,11 +165,11 @@ public void tick() { double toPurify = Math.min( speed * maxFluidPurifiedPerStrike, Math.min( - inputHatch.fluidAmount(PylonFluids.DIRTY_HYDRAULIC_FLUID), - outputHatch.fluidSpaceRemaining(PylonFluids.HYDRAULIC_FLUID) / purificationEfficiency + inputHatch.getFluidAmount(), + outputHatch.getFluidSpaceRemaining() / purificationEfficiency ) ); - inputHatch.removeFluid(PylonFluids.DIRTY_HYDRAULIC_FLUID, toPurify); + inputHatch.removeFluid(toPurify); outputHatch.addFluid(PylonFluids.HYDRAULIC_FLUID, toPurify * purificationEfficiency); }, 45); diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Collimator.java b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Collimator.java index 60f99b388..71065d50d 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Collimator.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Collimator.java @@ -3,17 +3,9 @@ import io.github.pylonmc.pylon.PylonFluids; import io.github.pylonmc.pylon.PylonItems; import io.github.pylonmc.pylon.PylonKeys; -import io.github.pylonmc.pylon.content.machines.fluid.FluidTank; -import io.github.pylonmc.pylon.content.machines.fluid.FluidTankWithDisplayEntity; import io.github.pylonmc.pylon.util.PylonUtils; import io.github.pylonmc.rebar.block.RebarBlock; -import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; -import io.github.pylonmc.rebar.block.base.RebarFluidTank; -import io.github.pylonmc.rebar.block.base.RebarGuiBlock; -import io.github.pylonmc.rebar.block.base.RebarProcessor; -import io.github.pylonmc.rebar.block.base.RebarSimpleMultiblock; -import io.github.pylonmc.rebar.block.base.RebarTickingBlock; -import io.github.pylonmc.rebar.block.base.RebarVirtualInventoryBlock; +import io.github.pylonmc.rebar.block.base.*; import io.github.pylonmc.rebar.block.context.BlockBreakContext; import io.github.pylonmc.rebar.block.context.BlockCreateContext; import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; @@ -25,6 +17,9 @@ import io.github.pylonmc.rebar.util.gui.GuiItems; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.time.Duration; +import java.util.List; +import java.util.Map; import net.kyori.adventure.text.format.TextColor; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -37,10 +32,6 @@ import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.inventory.VirtualInventory; -import java.time.Duration; -import java.util.List; -import java.util.Map; - public class Collimator extends RebarBlock implements RebarFluidTank, @@ -79,7 +70,6 @@ public Collimator(@NotNull Block block, @NotNull BlockCreateContext context) { setFacing(context.getFacing()); createFluidPoint(FluidPointType.INPUT, BlockFace.NORTH, context, false); setCapacity(obscyraPerCohesiveUnit); - setMultiblockDirection(context.getFacing()); startProcess(secondsPerCohesiveUnit * 20); } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/CoreDrill.java b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/CoreDrill.java index f5b70d7f7..2e80d523f 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/CoreDrill.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/CoreDrill.java @@ -18,6 +18,7 @@ import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; import io.github.pylonmc.rebar.util.position.BlockPosition; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.List; import lombok.Getter; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -34,9 +35,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import java.time.Duration; -import java.util.List; - public abstract class CoreDrill extends RebarBlock implements RebarSimpleMultiblock, RebarDirectionalBlock, @@ -74,7 +72,6 @@ public Item(@NotNull ItemStack stack) { protected CoreDrill(@NotNull Block block, @NotNull BlockCreateContext context) { super(block, context); setFacing(context.getFacing()); - setMultiblockDirection(getFacing()); addEntity("drill", new ItemDisplayBuilder() .itemStack(drillStack) .transformation(new TransformBuilder() diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Grindstone.java b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Grindstone.java index 184fc1558..cf16f1b3c 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Grindstone.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Grindstone.java @@ -21,6 +21,8 @@ import io.github.pylonmc.rebar.logistics.slot.ItemDisplayLogisticSlot; import io.github.pylonmc.rebar.util.position.BlockPosition; import io.github.pylonmc.rebar.waila.WailaDisplay; +import java.util.List; +import java.util.Map; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Bukkit; @@ -44,9 +46,6 @@ import org.joml.Matrix4f; import org.joml.Vector3i; -import java.util.List; -import java.util.Map; - public class Grindstone extends RebarBlock implements RebarSimpleMultiblock, @@ -92,7 +91,7 @@ public void postInitialise() { } @Override - protected void postLoad() { + protected void postLoad(@NotNull PersistentDataContainer pdc) { if (isProcessingRecipe()) { finishRecipe(); } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/PotionAltar.java b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/PotionAltar.java index f3d0f5225..a92d4f1a0 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/PotionAltar.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/PotionAltar.java @@ -9,12 +9,7 @@ import io.github.pylonmc.pylon.util.PylonUtils; import io.github.pylonmc.rebar.block.BlockStorage; import io.github.pylonmc.rebar.block.RebarBlock; -import io.github.pylonmc.rebar.block.base.RebarBreakHandler; -import io.github.pylonmc.rebar.block.base.RebarDirectionalBlock; -import io.github.pylonmc.rebar.block.base.RebarInteractBlock; -import io.github.pylonmc.rebar.block.base.RebarRecipeProcessor; -import io.github.pylonmc.rebar.block.base.RebarSimpleMultiblock; -import io.github.pylonmc.rebar.block.base.RebarTickingBlock; +import io.github.pylonmc.rebar.block.base.*; import io.github.pylonmc.rebar.block.context.BlockBreakContext; import io.github.pylonmc.rebar.block.context.BlockCreateContext; import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; @@ -27,17 +22,13 @@ import io.github.pylonmc.rebar.waila.WailaDisplay; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.item.PotionContents; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; import lombok.Getter; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextColor; -import org.bukkit.Bukkit; -import org.bukkit.Color; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.Particle; -import org.bukkit.Vibration; +import org.bukkit.*; import org.bukkit.Vibration.Destination.BlockDestination; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -55,13 +46,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Vector3i; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; - /** * @author balugaq */ @@ -69,9 +53,9 @@ public class PotionAltar extends RebarBlock implements RebarSimpleMultiblock, RebarInteractBlock, RebarTickingBlock, RebarDirectionalBlock, RebarBreakHandler { private static final NamespacedKey RECIPE_TICKS_REMAINING_KEY = PylonUtils.pylonKey("potion_altar_recipe_ticks_remaining"); - private static final MultiblockComponent SHIMMER_PEDESTAL_COMPONENT = new RebarMultiblockComponent(PylonKeys.SHIMMER_PEDESTAL); - private static final MultiblockComponent POTION_PEDESTAL_COMPONENT = new RebarMultiblockComponent(PylonKeys.POTION_PEDESTAL); - private static final MultiblockComponent LIT_ORANGE_CANDLE_COMPONENT = new VanillaBlockdataMultiblockComponent(Material.ORANGE_CANDLE.createBlockData("[lit=true]")); + private static final MultiblockComponent SHIMMER_PEDESTAL_COMPONENT = MultiblockComponent.of(PylonKeys.SHIMMER_PEDESTAL); + private static final MultiblockComponent POTION_PEDESTAL_COMPONENT = MultiblockComponent.of(PylonKeys.POTION_PEDESTAL); + private static final MultiblockComponent LIT_ORANGE_CANDLE_COMPONENT = MultiblockComponent.of(Material.ORANGE_CANDLE.createBlockData("[lit=true]")); private final Sound START_SOUND = getSettings().getOrThrow("sound.start", ConfigAdapter.SOUND); private final Sound FINISH_SOUND = getSettings().getOrThrow("sound.finish", ConfigAdapter.SOUND); private final Sound CANCEL_SOUND = getSettings().getOrThrow("sound.cancel", ConfigAdapter.SOUND); @@ -110,7 +94,6 @@ public PotionAltar(Block block, BlockCreateContext context) { setTickInterval(tickInterval); setFacing(context.getFacing()); - setMultiblockDirection(getFacing()); addEntity("brewing_stand", new BlockDisplayBuilder() .transformation(new TransformBuilder() @@ -417,7 +400,10 @@ private Pedestal getCatalystPedestal() { @NotNull private List getBlockOffsets(@NotNull MultiblockComponent component) { - return validStructures().getFirst().entrySet().stream().filter(entry -> entry.getValue() == component).map(Map.Entry::getKey).toList(); + return RebarSimpleMultiblock.rotateComponentsToFace(getComponents(), getFacing()).entrySet().stream() + .filter(entry -> entry.getValue() == component) + .map(Map.Entry::getKey) + .toList(); } @NotNull diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Press.java b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Press.java index 951438e9b..277e0e78a 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Press.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/simple/Press.java @@ -100,7 +100,7 @@ public Press(@NotNull Block block, @NotNull PersistentDataContainer pdc) { } @Override - public void postLoad() { + public void postLoad(@NotNull PersistentDataContainer pdc) { if (isProcessingRecipe()) { finishRecipe(); } diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/Bloomery.java b/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/Bloomery.java index 75bc5b9ef..d45d58bb6 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/Bloomery.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/Bloomery.java @@ -21,6 +21,9 @@ import io.github.pylonmc.rebar.logistics.LogisticGroupType; import io.github.pylonmc.rebar.logistics.slot.ItemDisplayLogisticSlot; import io.github.pylonmc.rebar.util.position.BlockPosition; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -43,10 +46,6 @@ import org.jetbrains.annotations.NotNull; import org.joml.Vector3i; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; - public final class Bloomery extends RebarBlock implements RebarSimpleMultiblock, RebarInteractBlock, @@ -69,7 +68,7 @@ public Bloomery(@NotNull Block block, @NotNull BlockCreateContext context) { .build(getBlock().getLocation().toCenterLocation()) ); setTickInterval(TICK_INTERVAL); - setMultiblockDirection(context.getFacing()); + setFacing(context.getFacing()); } @SuppressWarnings("unused") diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryBurner.java b/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryBurner.java index 0c6363455..e72769ffd 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryBurner.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryBurner.java @@ -17,19 +17,31 @@ import java.util.Map; import io.github.pylonmc.pylon.PylonItems; +import io.github.pylonmc.pylon.util.BurnerProgressItem; import io.github.pylonmc.rebar.block.base.*; import io.github.pylonmc.rebar.block.context.BlockCreateContext; import io.github.pylonmc.rebar.datatypes.RebarSerializers; -import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; import io.github.pylonmc.rebar.logistics.LogisticGroupType; import io.github.pylonmc.rebar.registry.RebarRegistry; +import io.github.pylonmc.rebar.util.MachineUpdateReason; import io.github.pylonmc.rebar.util.RebarUtils; import io.github.pylonmc.rebar.util.gui.GuiItems; -import io.github.pylonmc.rebar.util.gui.ProgressItem; +import java.util.Map; import kotlin.Pair; +import org.bukkit.Keyed; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.inventory.VirtualInventory; +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; + public final class SmelteryBurner extends SmelteryComponent implements RebarGuiBlock, RebarVirtualInventoryBlock, @@ -49,13 +61,8 @@ public final class SmelteryBurner extends SmelteryComponent implements private @Nullable Fuel fuel; - private final ItemStackBuilder notBurningProgressItem = ItemStackBuilder.of(Material.CHARCOAL) - .name(Component.translatable("pylon.gui.smeltery_burner.not_burning")); - private final ItemStackBuilder burningProgressItem = ItemStackBuilder.of(Material.BLAZE_POWDER) - .name(Component.translatable("pylon.gui.smeltery_burner.burning")); - - private final VirtualInventory inventory = new VirtualInventory(3); - private final ProgressItem progressItem = new ProgressItem(notBurningProgressItem); + private final VirtualInventory fuelInventory = new VirtualInventory(3); + private final BurnerProgressItem progressItem = new BurnerProgressItem(); @SuppressWarnings("unused") public SmelteryBurner(@NotNull Block block, @NotNull BlockCreateContext context) { @@ -81,7 +88,7 @@ public void write(@NotNull PersistentDataContainer pdc) { @Override public void postInitialise() { setProcessProgressItem(progressItem); - createLogisticGroup("fuel", LogisticGroupType.INPUT, inventory); + createLogisticGroup("fuel", LogisticGroupType.INPUT, fuelInventory); } @Override @@ -96,16 +103,29 @@ public void postInitialise() { return Gui.builder() .setStructure( "# # # # # # # # #", - "# # # # f # # # #", - "# # # x x x # # #", + "# # # i p i # # #", + "# # # i x i # # #", + "# # # i i i # # #", "# # # # # # # # #" ) - .addIngredient('f', progressItem) - .addIngredient('x', inventory) .addIngredient('#', GuiItems.background()) + .addIngredient('i', GuiItems.input()) + .addIngredient('p', progressItem) + .addIngredient('x', fuelInventory) .build(); } + private void tryStartProcessing() { + for (Fuel fuel : FUELS) { + if (fuelInventory.removeFirstSimilar(new MachineUpdateReason(), 1, fuel.material()) > 0) { + this.fuel = fuel; + startProcess(fuel.burnTimeSeconds() * 20); + refreshBlockTextureItem(); + return; + } + } + } + @Override public void tick() { progressProcess(getTickInterval()); @@ -115,43 +135,30 @@ public void tick() { return; } - if (fuel != null) { - controller.heatAsymptotically(fuel.temperature); - return; + if (!isProcessing()) { + tryStartProcessing(); } - itemLoop: - for (int i = 0; i < inventory.getSize(); i++) { - ItemStack item = inventory.getItem(i); - if (item == null) { - continue; - } + if (!isProcessing()) return; - for (Fuel fuel : FUELS) { - if (!item.isSimilar(fuel.material)) { - continue; - } + progressProcess(getTickInterval()); - this.fuel = fuel; - progressItem.setItem(burningProgressItem); - inventory.setItem(null, i, item.subtract()); - startProcess(Math.round(fuel.burnTimeSeconds * 20)); - refreshBlockTextureItem(); - break itemLoop; - } + if (fuel != null) { + controller.heatAsymptotically(fuel.temperature); + return; } } @Override public void onProcessFinished() { - progressItem.setItem(notBurningProgressItem); - fuel = null; refreshBlockTextureItem(); + fuel = null; + tryStartProcessing(); } @Override public @NotNull Map getVirtualInventories() { - return Map.of("fuels", inventory); + return Map.of("fuels", fuelInventory); } // TODO display fuels @@ -159,7 +166,7 @@ public record Fuel( @NotNull NamespacedKey key, @NotNull ItemStack material, double temperature, - long burnTimeSeconds + int burnTimeSeconds ) implements Keyed { @Override public @NotNull NamespacedKey getKey() { diff --git a/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryController.java b/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryController.java index a4079be18..46c8dbc33 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryController.java +++ b/src/main/java/io/github/pylonmc/pylon/content/machines/smelting/SmelteryController.java @@ -1,34 +1,6 @@ package io.github.pylonmc.pylon.content.machines.smelting; -import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; - import com.google.common.base.Preconditions; - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.Style; - -import org.apache.commons.lang3.ArrayUtils; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.data.Directional; -import org.bukkit.damage.DamageSource; -import org.bukkit.damage.DamageType; -import org.bukkit.entity.*; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.util.BoundingBox; -import org.bukkit.util.noise.SimplexOctaveGenerator; -import org.jetbrains.annotations.NotNull; -import org.joml.Vector3i; -import org.jspecify.annotations.NonNull; - -import java.util.*; -import java.util.concurrent.ThreadLocalRandom; -import java.util.stream.Collectors; - import io.github.pylonmc.pylon.PylonKeys; import io.github.pylonmc.pylon.recipes.SmelteryRecipe; import io.github.pylonmc.pylon.util.HslColor; @@ -55,14 +27,38 @@ import io.github.pylonmc.rebar.util.position.ChunkPosition; import it.unimi.dsi.fastutil.objects.Object2DoubleMap; import it.unimi.dsi.fastutil.objects.Object2DoubleRBTreeMap; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; import kotlin.Pair; import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.Style; +import org.apache.commons.lang3.ArrayUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Directional; +import org.bukkit.damage.DamageSource; +import org.bukkit.damage.DamageType; +import org.bukkit.entity.*; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.noise.SimplexOctaveGenerator; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector3i; +import org.jspecify.annotations.NonNull; import xyz.xenondevs.invui.Click; import xyz.xenondevs.invui.gui.Gui; import xyz.xenondevs.invui.item.AbstractItem; import xyz.xenondevs.invui.item.Item; import xyz.xenondevs.invui.item.ItemProvider; +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; + public final class SmelteryController extends SmelteryComponent implements RebarGuiBlock, RebarMultiblock, RebarTickingBlock { @@ -424,7 +420,7 @@ private void applyHeat() { private void spawnPixels() { pixels.clear(); - Location location = center.getLocation().add(-1, 0, -1); + Location location = center.toLocation().add(-1, 0, -1); for (int x = 0; x < PIXELS_PER_SIDE; x++) { for (int z = 0; z < PIXELS_PER_SIDE; z++) { Location relative = location.clone().add((double) x / RESOLUTION, 0, (double) z / RESOLUTION); @@ -574,7 +570,7 @@ public void tick() { heaters = 0; updateFluidDisplay(); - BoundingBox box = BoundingBox.of(center.getLocation(), 2, 0, 2); + BoundingBox box = BoundingBox.of(center.toLocation(), 2, 0, 2); box.expand(BlockFace.UP, height); double damage = Math.max(0, temperature / 100 + 1); diff --git a/src/main/java/io/github/pylonmc/pylon/content/tools/BrickMold.java b/src/main/java/io/github/pylonmc/pylon/content/tools/BrickMold.java index cab223c10..89a1f4957 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/tools/BrickMold.java +++ b/src/main/java/io/github/pylonmc/pylon/content/tools/BrickMold.java @@ -8,8 +8,10 @@ import io.github.pylonmc.rebar.event.api.annotation.MultiHandler; import io.github.pylonmc.rebar.i18n.RebarArgument; import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.item.RebarItemSchema; import io.github.pylonmc.rebar.item.base.RebarBlockInteractor; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.List; import org.bukkit.Particle; import org.bukkit.event.Event; import org.bukkit.event.EventPriority; @@ -17,8 +19,6 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.List; - public class BrickMold extends RebarItem implements RebarBlockInteractor { @@ -49,7 +49,12 @@ public void onUsedToClickBlock(@NotNull PlayerInteractEvent event, @NotNull Even ItemStack particleType; RebarBlock rebarBlock = BlockStorage.get(event.getClickedBlock()); if (rebarBlock != null) { - particleType = rebarBlock.getDefaultItem().getItemStack(); + RebarItemSchema defaultItem = rebarBlock.getDefaultItem(); + if (defaultItem != null) { + particleType = defaultItem.getOriginalTemplate(); + } else { + particleType = new ItemStack(event.getClickedBlock().getType()); + } } else { particleType = new ItemStack(event.getClickedBlock().getType()); } diff --git a/src/main/java/io/github/pylonmc/pylon/content/tools/HydraulicCannon.java b/src/main/java/io/github/pylonmc/pylon/content/tools/HydraulicCannon.java index 9bc046e2c..4b43af9cb 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/tools/HydraulicCannon.java +++ b/src/main/java/io/github/pylonmc/pylon/content/tools/HydraulicCannon.java @@ -16,6 +16,7 @@ import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.item.base.RebarInteractor; import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.List; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Location; import org.bukkit.Material; @@ -27,8 +28,6 @@ import org.bukkit.util.Vector; import org.jetbrains.annotations.NotNull; -import java.util.List; - public class HydraulicCannon extends RebarItem implements RebarInteractor, HydraulicRefuelable { @@ -113,6 +112,7 @@ public void onUsedToClick(@NotNull PlayerInteractEvent event, @NotNull EventPrio .subtract(0, 0.5, 0); EntityStorage.add(new DisplayProjectile( player, + PylonKeys.TIN_PROJECTILE, projectileMaterial, source, direction, diff --git a/src/main/java/io/github/pylonmc/pylon/content/tools/Moldable.java b/src/main/java/io/github/pylonmc/pylon/content/tools/Moldable.java index 6f847edc4..0abccaa3c 100644 --- a/src/main/java/io/github/pylonmc/pylon/content/tools/Moldable.java +++ b/src/main/java/io/github/pylonmc/pylon/content/tools/Moldable.java @@ -11,7 +11,7 @@ public interface Moldable extends Keyed { boolean isMoldingFinished(); default ItemStack moldingInputStack() { - return RebarRegistry.ITEMS.getOrThrow(getKey()).getItemStack(); + return RebarRegistry.ITEMS.getOrThrow(getKey()).createNewItemStack(); } default ItemStack moldingResult() { diff --git a/src/main/java/io/github/pylonmc/pylon/content/tools/PylonWire.java b/src/main/java/io/github/pylonmc/pylon/content/tools/PylonWire.java new file mode 100644 index 000000000..83eb35abd --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/content/tools/PylonWire.java @@ -0,0 +1,35 @@ +package io.github.pylonmc.pylon.content.tools; + +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.RebarItem; +import io.github.pylonmc.rebar.item.base.RebarWire; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.List; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class PylonWire extends RebarItem implements RebarWire { + + private final double maxPower = getSettings().getOrThrow("max-power", ConfigAdapter.DOUBLE); + + public PylonWire(@NotNull ItemStack stack) { + super(stack); + } + + @Override + public @NotNull List<@NotNull RebarArgument> getPlaceholders() { + return List.of(RebarArgument.of("max-power", UnitFormat.WATTS.format(maxPower))); + } + + @Override + public double getMaxCurrent() { + return maxPower; + } + + @Override + public @NotNull Material getDisplayMaterial() { + return Material.COPPER_BLOCK; + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/guide/HydraulicRefuelableItemsPage.java b/src/main/java/io/github/pylonmc/pylon/guide/HydraulicRefuelableItemsPage.java index e92535fa3..d24e31deb 100644 --- a/src/main/java/io/github/pylonmc/pylon/guide/HydraulicRefuelableItemsPage.java +++ b/src/main/java/io/github/pylonmc/pylon/guide/HydraulicRefuelableItemsPage.java @@ -8,13 +8,12 @@ import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; import io.github.pylonmc.rebar.registry.RebarRegistry; +import java.util.List; import lombok.Getter; import net.kyori.adventure.text.Component; import org.jspecify.annotations.NonNull; import xyz.xenondevs.invui.item.Item; -import java.util.List; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; @@ -34,8 +33,8 @@ public HydraulicRefuelableItemsPage() { private static @NonNull List getButtons() { return RebarRegistry.ITEMS.stream() - .filter(item -> RebarItem.fromStack(item.getItemStack()) instanceof HydraulicRefuelable) - .map(item -> (Item) new ItemButton(item.getItemStack())) + .filter(item -> RebarItem.fromStack(item.createNewItemStack()) instanceof HydraulicRefuelable) + .map(item -> (Item) new ItemButton(item.createNewItemStack())) .toList(); } } diff --git a/src/main/java/io/github/pylonmc/pylon/recipes/GasTurbineRecipe.java b/src/main/java/io/github/pylonmc/pylon/recipes/GasTurbineRecipe.java new file mode 100644 index 000000000..fafd309bd --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/recipes/GasTurbineRecipe.java @@ -0,0 +1,81 @@ +package io.github.pylonmc.pylon.recipes; + +import io.github.pylonmc.pylon.PylonItems; +import io.github.pylonmc.rebar.config.ConfigSection; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.fluid.FluidWithAmount; +import io.github.pylonmc.rebar.guide.button.FluidButton; +import io.github.pylonmc.rebar.guide.button.ItemButton; +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; +import io.github.pylonmc.rebar.recipe.*; +import io.github.pylonmc.rebar.util.gui.GuiItems; +import io.github.pylonmc.rebar.util.gui.unit.UnitFormat; +import java.util.List; +import net.kyori.adventure.text.Component; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; +import xyz.xenondevs.invui.gui.Gui; + +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; + +public record GasTurbineRecipe( + @NotNull NamespacedKey key, + @NotNull RecipeInput.Fluid input, + @NotNull FluidWithAmount output, + double powerProduction +) implements RebarRecipe { + + public static final RecipeType RECIPE_TYPE = new ConfigurableRecipeType<>(pylonKey("gas_turbine")) { + @Override + protected @NonNull GasTurbineRecipe loadRecipe(@NotNull NamespacedKey key, @NotNull ConfigSection section) { + return new GasTurbineRecipe( + key, + section.getOrThrow("input", ConfigAdapter.RECIPE_INPUT_FLUID), + section.getOrThrow("output", ConfigAdapter.FLUID_WITH_AMOUNT), + section.getOrThrow("power-production", ConfigAdapter.DOUBLE) + ); + } + }; + + @Override + public @NotNull List<@NotNull RecipeInput> getInputs() { + return List.of(input); + } + + @Override + public @NotNull List<@NotNull FluidOrItem> getResults() { + return List.of(output.asFluidOrItem()); + } + + @Override + public @NonNull Gui display() { + return Gui.builder() + .setStructure( + "# # # # # # # # #", + "# # # # # # # # #", + "# i # # x # # o #", + "# # # # # # # # #", + "# # # # # # # # #" + ) + .addIngredient('#', GuiItems.backgroundBlack()) + .addIngredient('i', new FluidButton(input)) + .addIngredient('x', ItemButton.from(ItemStackBuilder.of(PylonItems.GAS_TURBINE.clone()) + .lore( + Component.empty(), + Component.translatable( + "pylon.gui.watts-per-mb", + RebarArgument.of("power", UnitFormat.WATTS_PER_MILLIBUCKET.format(powerProduction / input.amountMillibuckets()).decimalPlaces(1)) + ) + ) + .build())) + .addIngredient('o', new FluidButton(output)) + .build(); + } + + @Override + public @NotNull NamespacedKey getKey() { + return key; + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/recipes/HammerRecipe.java b/src/main/java/io/github/pylonmc/pylon/recipes/HammerRecipe.java index 334be707e..0b973c05e 100644 --- a/src/main/java/io/github/pylonmc/pylon/recipes/HammerRecipe.java +++ b/src/main/java/io/github/pylonmc/pylon/recipes/HammerRecipe.java @@ -12,6 +12,8 @@ import io.github.pylonmc.rebar.registry.RebarRegistry; import io.github.pylonmc.rebar.util.MiningLevel; import io.github.pylonmc.rebar.util.gui.GuiItems; +import java.util.ArrayList; +import java.util.List; import net.kyori.adventure.text.Component; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; @@ -19,9 +21,6 @@ import org.jspecify.annotations.NonNull; import xyz.xenondevs.invui.gui.Gui; -import java.util.ArrayList; -import java.util.List; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; /** @@ -92,7 +91,7 @@ public record HammerRecipe( Hammer hammer = RebarItem.fromStack(itemSchema.getOriginalTemplate(), Hammer.class); if (hammer == null) continue; - ItemStack stack = itemSchema.getItemStack(); + ItemStack stack = itemSchema.createNewItemStack(); if (!hammer.miningLevel.isAtLeast(level)) { continue; } diff --git a/src/main/java/io/github/pylonmc/pylon/recipes/HeatExchangerRecipe.java b/src/main/java/io/github/pylonmc/pylon/recipes/HeatExchangerRecipe.java new file mode 100644 index 000000000..d7669168b --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/recipes/HeatExchangerRecipe.java @@ -0,0 +1,80 @@ +package io.github.pylonmc.pylon.recipes; + +import io.github.pylonmc.pylon.PylonItems; +import io.github.pylonmc.rebar.config.ConfigSection; +import io.github.pylonmc.rebar.config.adapter.ConfigAdapter; +import io.github.pylonmc.rebar.fluid.FluidWithAmount; +import io.github.pylonmc.rebar.guide.button.FluidButton; +import io.github.pylonmc.rebar.guide.button.ItemButton; +import io.github.pylonmc.rebar.recipe.*; +import io.github.pylonmc.rebar.util.gui.GuiItems; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; +import kotlin.Pair; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; +import xyz.xenondevs.invui.gui.Gui; + +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; + +public record HeatExchangerRecipe( + @NotNull NamespacedKey key, + @NotNull Pair transferFrom, + @NotNull Pair transferTo +) implements RebarRecipe { + + public static final RecipeType RECIPE_TYPE = new ConfigurableRecipeType<>(pylonKey("heat_exchanger")) { + @Override + protected @NotNull HeatExchangerRecipe loadRecipe(@NotNull NamespacedKey key, @NotNull ConfigSection section) { + RecipeInput.Fluid transferFromInput = section.getOrThrow("from.input", ConfigAdapter.RECIPE_INPUT_FLUID); + FluidWithAmount transferFromOutput = section.get("from.output", ConfigAdapter.FLUID_WITH_AMOUNT); + RecipeInput.Fluid transferToInput = section.getOrThrow("to.input", ConfigAdapter.RECIPE_INPUT_FLUID); + FluidWithAmount transferToOutput = section.get("to.output", ConfigAdapter.FLUID_WITH_AMOUNT); + return new HeatExchangerRecipe( + key, + new Pair<>(transferFromInput, transferFromOutput), + new Pair<>(transferToInput, transferToOutput) + ); + } + }; + + @Override + public @NotNull List<@NotNull RecipeInput> getInputs() { + return List.of(transferFrom.getFirst(), transferTo.getFirst()); + } + + @Override + public @NotNull List<@NotNull FluidOrItem> getResults() { + return Stream.of(transferFrom.getSecond(), transferTo.getSecond()) + .filter(Objects::nonNull) + .map(FluidWithAmount::asFluidOrItem) + .toList(); + } + + @Override + public @NonNull Gui display() { + return Gui.builder() + .setStructure( + "# # # # # # # # #", + "# i # # # # # o #", + "# # # # x # # # #", + "# I # # # # # O #", + "# # # # # # # # #" + ) + .addIngredient('#', GuiItems.backgroundBlack()) + .addIngredient('i', new FluidButton(transferFrom.getFirst())) + .addIngredient('o', transferFrom.getSecond() != null ? new FluidButton(transferFrom.getSecond()) : GuiItems.backgroundBlack()) + .addIngredient('I', new FluidButton(transferTo.getFirst())) + .addIngredient('O', transferTo.getSecond() != null ? new FluidButton(transferTo.getSecond()) : GuiItems.backgroundBlack()) + .addIngredient('x', ItemButton.from(PylonItems.HEAT_EXCHANGER)) + .build(); + } + + @Override + public @NotNull NamespacedKey getKey() { + return key; + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/recipes/SiloConverterRecipe.java b/src/main/java/io/github/pylonmc/pylon/recipes/SiloConverterRecipe.java index d05bfbfa5..0882c7f53 100644 --- a/src/main/java/io/github/pylonmc/pylon/recipes/SiloConverterRecipe.java +++ b/src/main/java/io/github/pylonmc/pylon/recipes/SiloConverterRecipe.java @@ -7,20 +7,15 @@ import io.github.pylonmc.rebar.guide.button.ItemButton; import io.github.pylonmc.rebar.item.RebarItem; import io.github.pylonmc.rebar.item.RebarItemSchema; -import io.github.pylonmc.rebar.recipe.ConfigurableRecipeType; -import io.github.pylonmc.rebar.recipe.FluidOrItem; -import io.github.pylonmc.rebar.recipe.RebarRecipe; -import io.github.pylonmc.rebar.recipe.RecipeInput; -import io.github.pylonmc.rebar.recipe.RecipeType; +import io.github.pylonmc.rebar.recipe.*; import io.github.pylonmc.rebar.registry.RebarRegistry; import io.github.pylonmc.rebar.util.gui.GuiItems; +import java.util.List; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import xyz.xenondevs.invui.gui.Gui; -import java.util.List; - import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; @@ -60,7 +55,7 @@ public record SiloConverterRecipe( public @NotNull Gui display() { List silos = RebarRegistry.ITEMS.getValues() .stream() - .map(RebarItemSchema::getItemStack) + .map(RebarItemSchema::createNewItemStack) .filter(item -> RebarItem.fromStack(item) instanceof Silo.Item) .filter(item -> !item.isSimilar(result)) .toList(); diff --git a/src/main/java/io/github/pylonmc/pylon/util/BurnerProgressItem.java b/src/main/java/io/github/pylonmc/pylon/util/BurnerProgressItem.java new file mode 100644 index 000000000..b8cc06d8c --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/util/BurnerProgressItem.java @@ -0,0 +1,33 @@ +package io.github.pylonmc.pylon.util; + +import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; +import io.github.pylonmc.rebar.util.gui.ProgressItem; +import net.kyori.adventure.text.Component; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.item.ItemProvider; + +public class BurnerProgressItem extends ProgressItem { + + private static final ItemStackBuilder NOT_BURNING = ItemStackBuilder.of(Material.CHARCOAL) + .name(Component.translatable("pylon.gui.burning.not_burning")) + .addCustomModelDataString("pylon:burner_progress/not_burning"); + private static final ItemStackBuilder BURNING = ItemStackBuilder.of(Material.BLAZE_POWDER) + .name(Component.translatable("pylon.gui.burning.burning")) + .addCustomModelDataString("pylon:burner_progress/burning"); + + public BurnerProgressItem() { + super(NOT_BURNING); + } + + @Override + public @NotNull ItemProvider getItemProvider(@NotNull Player viewer) { + if (getTotalTime() != null) { + setItem(BURNING); + } else { + setItem(NOT_BURNING); + } + return super.getItemProvider(viewer); + } +} diff --git a/src/main/java/io/github/pylonmc/pylon/util/DisplayProjectile.java b/src/main/java/io/github/pylonmc/pylon/util/DisplayProjectile.java index 0a10bd7dd..61fc001b8 100644 --- a/src/main/java/io/github/pylonmc/pylon/util/DisplayProjectile.java +++ b/src/main/java/io/github/pylonmc/pylon/util/DisplayProjectile.java @@ -5,8 +5,12 @@ import io.github.pylonmc.rebar.entity.base.RebarTickingEntity; import io.github.pylonmc.rebar.entity.display.ItemDisplayBuilder; import io.github.pylonmc.rebar.entity.display.transform.LineBuilder; +import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; +import java.util.List; +import java.util.Optional; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.Particle; import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; @@ -19,9 +23,6 @@ import org.jetbrains.annotations.NotNull; import org.joml.Vector3d; -import java.util.List; -import java.util.Optional; - public final class DisplayProjectile extends RebarEntity implements RebarTickingEntity { private final Player player; @@ -32,6 +33,7 @@ public final class DisplayProjectile extends RebarEntity implements public DisplayProjectile( Player player, + @NotNull NamespacedKey key, Material material, Location source, @NotNull Vector direction, @@ -49,7 +51,10 @@ public DisplayProjectile( .thickness(thickness) .build() ) - .material(material) + .itemStack( + ItemStackBuilder.of(material) + .addCustomModelDataString("pylon:display_projectile/" + key) + ) .build(source) ); this.player = player; diff --git a/src/main/java/io/github/pylonmc/pylon/util/NumberInputButton.java b/src/main/java/io/github/pylonmc/pylon/util/NumberInputButton.java new file mode 100644 index 000000000..3816bb629 --- /dev/null +++ b/src/main/java/io/github/pylonmc/pylon/util/NumberInputButton.java @@ -0,0 +1,123 @@ +package io.github.pylonmc.pylon.util; + +import io.github.pylonmc.rebar.i18n.RebarArgument; +import io.github.pylonmc.rebar.item.builder.ItemStackBuilder; +import io.github.pylonmc.rebar.util.PlayerInput; +import lombok.Builder; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import xyz.xenondevs.invui.Click; +import xyz.xenondevs.invui.item.AbstractBoundItem; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.window.Window; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import static io.github.pylonmc.pylon.util.PylonUtils.pylonKey; + +@Builder +public class NumberInputButton extends AbstractBoundItem { + + private final Material material; + private final Component name; + + private final int increment; + private final int shiftIncrement; + + private final @Nullable Integer min; + private final @Nullable Integer max; + + private final Supplier valueGetter; + private final Consumer valueSetter; + + @lombok.Builder.Default + private final Function valueFormatter = Component::text; + + @lombok.Builder.Default + private final Consumer reopenWindow = _unused -> {}; + + @Override + public @NotNull ItemProvider getItemProvider(@NotNull Player viewer) { + int value = valueGetter.get(); + List lore = new ArrayList<>(); + lore.add(Component.translatable( + "pylon.gui.number-button.lore", + RebarArgument.of("increment", valueFormatter.apply(increment)), + RebarArgument.of("increment-shift", valueFormatter.apply(shiftIncrement)) + )); + if (min != null) { + lore.add(Component.translatable("pylon.gui.number-button.min", RebarArgument.of("min", valueFormatter.apply(min)))); + } + if (max != null) { + lore.add(Component.translatable("pylon.gui.number-button.max", RebarArgument.of("max", valueFormatter.apply(max)))); + } + return ItemStackBuilder.gui(material, pylonKey("number_input_button")) + .name(Component.translatable( + "pylon.gui.number-button.name", + RebarArgument.of("name", name), + RebarArgument.of("value", valueFormatter.apply(value)) + )) + .lore(lore); + } + + private void setValue(int value) { + if (min != null) { + value = Math.max(value, min); + } + if (max != null) { + value = Math.min(value, max); + } + valueSetter.accept(value); + notifyWindows(); + } + + @Override + public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull Click click) { + int value = valueGetter.get(); + int increment = clickType.isShiftClick() ? shiftIncrement : this.increment; + if (clickType == ClickType.DROP) { + String key; + if (min != null && max != null) { + key = "pylon.gui.number-button.enter-value.both"; + } else if (min != null) { + key = "pylon.gui.number-button.enter-value.min"; + } else if (max != null) { + key = "pylon.gui.number-button.enter-value.max"; + } else { + key = "pylon.gui.number-button.enter-value.none"; + } + Window window = getGui().getWindows().stream().filter(w -> w.getViewer().equals(player)).findAny().orElseThrow(); + window.close(); + player.sendMessage(Component.translatable( + key, + RebarArgument.of("min", min != null ? Component.text(min) : Component.empty()), + RebarArgument.of("max", max != null ? Component.text(max) : Component.empty()) + )); + PlayerInput.requestInput(player).thenAccept(input -> { + if (input == null) return; + try { + int newValue = Integer.parseInt(input); + setValue(newValue); + reopenWindow.accept(player); + } catch (NumberFormatException e) { + player.sendMessage(Component.translatable("pylon.gui.number-button.enter-value.invalid")); + } + }); + return; + } else if (clickType.isLeftClick()) { + value += increment; + } else if (clickType.isRightClick()) { + value -= increment; + } + setValue(value); + } +} diff --git a/src/main/resources/lang/en.yml b/src/main/resources/lang/en.yml index 1b36f6f4d..7eee89f44 100644 --- a/src/main/resources/lang/en.yml +++ b/src/main/resources/lang/en.yml @@ -1662,7 +1662,7 @@ item: diesel_grindstone: name: "Diesel Grindstone" lore: |- - Grinds items + Grinds items using diesel Diesel usage: %diesel-usage% Diesel buffer: %diesel-buffer% waila: "Diesel Grindstone | %diesel-bar%" @@ -1670,7 +1670,7 @@ item: diesel_brick_molder: name: "Diesel Brick Molder" lore: |- - Molds materials into bricks + Molds materials into bricks using diesel Diesel usage: %diesel-usage% Diesel buffer: %diesel-buffer% Molding cycles: %molding-cycles% @@ -2496,6 +2496,125 @@ item: lore: |- Used for forming items out of clay + electricity_pylon: + name: "Electricity Pylon" + lore: |- + Acts as a multidirectional bus for wires to connect to + Power may be transmitted from any port to any other port + + multimeter: + name: "Multimeter" + lore: |- + Measures the power passing through a wire + + creative_power_source: + name: "<#ff00ff>Creative Power Source" + lore: |- + Provides an infinite supply of power + + electricity_input_hatch: + name: "Electricity Input Hatch" + lore: |- + Accepts power from wires + + electricity_output_hatch: + name: "Electricity Output Hatch" + lore: |- + Outputs power to wires + + electric_grindstone: + name: "Electric Grindstone" + lore: |- + Grinds items using electricity + Power usage: %power-usage% + + electric_brick_molder: + name: "Electric Brick Molder" + lore: |- + Molds materials into bricks using electricity + Power usage: %power-usage% + Molding cycles: %molding-cycles% + + boiler_casing: + name: "Boiler Casing" + + boiler: + name: "Boiler" + lore: |- + Burns fuel to produce steam + Water usage: %water-usage% + Steam production: %steam-production% + + Components + 1x + 20x + 1x + 1x + 1x + + steam_engine: + name: "Steam Engine" + lore: |- + Uses steam to produce power + Steam usage: %steam-usage% + Steam capacity: %steam-capacity% + Power production: %power-production% + + Components + 1x + 1x + waila: "Steam Engine | %bar% | producing %power%" + + gas_turbine: + name: "Gas Turbine" + lore: |- + Uses hot gases to produce power + The more hot gases piped into it, the more power it produces + + Components + 1x + 9x + 3x + 10x + 1x + 1x + 1x + waila: "Gas Turbine | producing %power%" + + combustion_tower: + name: "Combustion Tower" + lore: |- + Burns diesel to produce very hot exhaust + Diesel usage: %diesel-usage% + Very hot exhaust production: %exhaust-production% + + Components + 1x + waila: "Combustion Tower | %diesel% | %exhaust%" + + heat_exchanger: + name: "Heat Exchanger" + lore: |- + Exchanges heat between two fluids + + Components + 1x + 2x + 2x + + wire_1_gauge: + name: "1-Gauge Wire" + lore: |- + Transmits electricity + Max power: %max-power% + + capacitor_1_kj: + name: "1 kJ Capacitor" + lore: |- + Stores electricity + Capacitors have a lower priority than most electric blocks, so they will only take excess power that other blocks can't use + Capacity: %capacity% + fluid: tag: melting-point: "Melting point: %temperature%" @@ -2516,9 +2635,10 @@ fluid: slurry_raw_iron: "Raw Iron Slurry" slurry_raw_tin: "Raw Tin Slurry" steel: "Liquid Steel" - sulfur: "Sulfur" + sulfur: "Liquid Sulfur" tin: "Liquid Tin" water: "Water" + steam: "<#d8d8d8>Steam" palladium: "<#b642f5>Liquid Palladium" plant_oil: "<#c4b352>Plant Oil" hydraulic_fluid: "<#212d99>Hydraulic Fluid" @@ -2528,11 +2648,10 @@ fluid: sugarcane: "<#a3ed2d>Sugarcane" ethanol: "<#d8d8d8>Ethanol" biodiesel: "<#eaa627>Biodiesel" + very_hot_exhaust: "<#ff2b0f>Very Hot Exhaust" + hot_exhaust: "<#ff6b0f>Hot Exhaust" waila: - fluid_tank: - empty: "(empty)" - filled: "| %bar%/%capacity% of %fluid%" crucible: item: empty: "| No items |" @@ -2666,6 +2785,7 @@ guide: machines_diesel_machines: "Diesel Machines" machines_diesel_production: "Diesel Production" machines_smelting: "Smelting" + machines_electricity: "Electricity" machines: "Machines" assembling: "Assembling" building: "Building" @@ -2762,7 +2882,7 @@ message: clear: "Clear" fluid_hatch: no_casing: "No casing" - no_multiblock: "No multiblock" + empty: "Empty" working: "%bars% (%fluid%)" biorefinery: no_fuel: "No fuel" @@ -2847,23 +2967,6 @@ gui: lore: |- The will always send items to this side unless it's full Left click to cycle priority - threshold_button: - name: "Threshold: %threshold%" - lore: |- - Left click to increase - Right click to decrease - item_threshold_button: - name: "Item Threshold: %threshold%" - lore: |- - Left click to increase - Right click to decrease - fluid_threshold_button: - name: "Fluid Threshold: %threshold%" - lore: |- - Left click to increase by 10 - Right click to decrease by 10 - Shift left click to increase by 100 - Shift right click to decrease by 100 whitelist-blacklist-toggle: whitelist: name: "Whitelist mode" @@ -2906,7 +3009,7 @@ gui: name: "Contents" fluid: "%amount% of %fluid%" empty: "Empty" - smeltery_burner: + burning: burning: "Burning" not_burning: "Not burning" progress_bar: @@ -2922,27 +3025,6 @@ gui: name: "Current inventory: %inventory%" lore: |- Click to cycle - fluid_meter: - name: "Measurement duration: %measurement-duration%" - lore: |- - Left click to increase - Right click to decrease - Shift left click to increase (10x) - Shift right click to decrease (10x) - fluid_accumulator: - name: "Amount: %amount%" - lore: |- - Left click to increase - Right click to decrease - Shift left click to increase (10x) - Shift right click to decrease (10x) - fluid_limiter: - name: "Max flow rate: %max-flow-rate%" - lore: |- - Left click to increase - Right click to decrease - Shift left click to increase (10x) - Shift right click to decrease (10x) assembly_table: step: "<#adc5ce>Step %step_index%: %tool% (x%clicks%)" no_recipe: "No recipe" @@ -2963,6 +3045,31 @@ gui: Automatic casting: %auto-cast% casting: "Casting %fluid%: %amount%

" mold-border: "Mold" + number-button: + name: "%name%: %value%" + lore: |- + Left click to increase by %increment% + Right click to decrease by %increment% + Shift left click to increase by %increment-shift% + Shift right click to decrease by %increment-shift% + Press to enter the value manually + min: " Minimum value: %min%" + max: " Maximum value: %max%" + enter-value: + none: "Enter a new value:" + min: "Enter a new value (min: %min%):" + max: "Enter a new value (max: %max%):" + both: "Enter a new value (min: %min%, max: %max%):" + invalid: "Invalid input, please enter an integer" + voltage: "Voltage" + power: "Power" + max-flow-rate: "Max flow rate" + amount: "Amount" + measurement-duration: "Measurement duration" + threshold: "Threshold" + item-threshold: "Item threshold" + fluid-threshold: "Fluid threshold" + watts-per-mb: "Power production: %power%" inventory: fuel: "Fuel" diff --git a/src/main/resources/recipes/minecraft/crafting_shaped.yml b/src/main/resources/recipes/minecraft/crafting_shaped.yml index 5fe14fcf8..ca588a305 100644 --- a/src/main/resources/recipes/minecraft/crafting_shaped.yml +++ b/src/main/resources/recipes/minecraft/crafting_shaped.yml @@ -2834,3 +2834,25 @@ pylon:liselette_hydraulic_purifier: A: pylon:liselette_anode result: pylon:liselette_hydraulic_purifier category: building + +pylon:boiler_casing: + pattern: + - "SSS" + - "S S" + - "SSS" + key: + S: pylon:steel_sheet + result: + pylon:boiler_casing: 4 + category: building + +pylon:boiler: + pattern: + - " C " + - "CFC" + - " C " + key: + C: pylon:boiler_casing + F: minecraft:furnace + result: pylon:boiler + category: building \ No newline at end of file diff --git a/src/main/resources/recipes/pylon/gas_turbine.yml b/src/main/resources/recipes/pylon/gas_turbine.yml new file mode 100644 index 000000000..f1d717799 --- /dev/null +++ b/src/main/resources/recipes/pylon/gas_turbine.yml @@ -0,0 +1,13 @@ +pylon:steam: + input: + pylon:steam: 10 + output: + pylon:water: 1 + power-production: 10 + +pylon:exhaust: + input: + pylon:very_hot_exhaust: 10 + output: + pylon:hot_exhaust: 5 + power-production: 10 \ No newline at end of file diff --git a/src/main/resources/recipes/pylon/heat_exchanger.yml b/src/main/resources/recipes/pylon/heat_exchanger.yml new file mode 100644 index 000000000..9c18ab47a --- /dev/null +++ b/src/main/resources/recipes/pylon/heat_exchanger.yml @@ -0,0 +1,21 @@ +pylon:very_hot_exhaust: + from: + input: + pylon:very_hot_exhaust: 500 + output: + pylon:hot_exhaust: 50 + to: + input: + pylon:water: 50 + output: + pylon:steam: 500 + +pylon:hot_exhaust: + from: + input: + pylon:hot_exhaust: 500 + to: + input: + pylon:water: 50 + output: + pylon:steam: 500 \ No newline at end of file diff --git a/src/main/resources/settings/boiler.yml b/src/main/resources/settings/boiler.yml new file mode 100644 index 000000000..72129e9a8 --- /dev/null +++ b/src/main/resources/settings/boiler.yml @@ -0,0 +1,9 @@ +tick-interval: 10 +steam-per-second: 500 + +# map of fluid to burn amount in seconds +fuels: + minecraft:coal: 30 + minecraft:charcoal: 30 + minecraft:coal_block: 270 + pylon:charcoal_block: 270 \ No newline at end of file diff --git a/src/main/resources/settings/capacitor_1_kj.yml b/src/main/resources/settings/capacitor_1_kj.yml new file mode 100644 index 000000000..a394186b5 --- /dev/null +++ b/src/main/resources/settings/capacitor_1_kj.yml @@ -0,0 +1 @@ +capacity: 1000 \ No newline at end of file diff --git a/src/main/resources/settings/combustion_tower.yml b/src/main/resources/settings/combustion_tower.yml new file mode 100644 index 000000000..d0c6754b2 --- /dev/null +++ b/src/main/resources/settings/combustion_tower.yml @@ -0,0 +1,5 @@ +tick-interval: 10 +diesel-usage: 50 +diesel-buffer: 1000 +exhaust-production: 100 +exhaust-buffer: 1000 \ No newline at end of file diff --git a/src/main/resources/settings/electric_brick_molder.yml b/src/main/resources/settings/electric_brick_molder.yml new file mode 100644 index 000000000..4f61777c7 --- /dev/null +++ b/src/main/resources/settings/electric_brick_molder.yml @@ -0,0 +1,3 @@ +power-usage: 10 +tick-interval: 10 +ticks-per-molding-cycle: 2 # Machine ticks, not server ticks \ No newline at end of file diff --git a/src/main/resources/settings/electric_grindstone.yml b/src/main/resources/settings/electric_grindstone.yml new file mode 100644 index 000000000..c551a6f4f --- /dev/null +++ b/src/main/resources/settings/electric_grindstone.yml @@ -0,0 +1,2 @@ +tick-interval: 10 +power-usage: 20 \ No newline at end of file diff --git a/src/main/resources/settings/gas_turbine.yml b/src/main/resources/settings/gas_turbine.yml new file mode 100644 index 000000000..eadac4ca6 --- /dev/null +++ b/src/main/resources/settings/gas_turbine.yml @@ -0,0 +1 @@ +tick-interval: 10 \ No newline at end of file diff --git a/src/main/resources/settings/heat_exchanger.yml b/src/main/resources/settings/heat_exchanger.yml new file mode 100644 index 000000000..eadac4ca6 --- /dev/null +++ b/src/main/resources/settings/heat_exchanger.yml @@ -0,0 +1 @@ +tick-interval: 10 \ No newline at end of file diff --git a/src/main/resources/settings/steam_engine.yml b/src/main/resources/settings/steam_engine.yml new file mode 100644 index 000000000..13a8f74db --- /dev/null +++ b/src/main/resources/settings/steam_engine.yml @@ -0,0 +1,5 @@ +tick-interval: 10 +steam-usage: 100 +steam-capacity: 200 +power-production: 50 +output-voltage: 64 \ No newline at end of file diff --git a/src/main/resources/settings/wire_1_gauge.yml b/src/main/resources/settings/wire_1_gauge.yml new file mode 100644 index 000000000..dcacea5bf --- /dev/null +++ b/src/main/resources/settings/wire_1_gauge.yml @@ -0,0 +1 @@ +max-power: 50 \ No newline at end of file