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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/Pylon.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.github.pylonmc.pylon.command.PylonCommand;
import io.github.pylonmc.pylon.content.building.Immobilizer;
import io.github.pylonmc.pylon.content.machines.experience.LiquidXPBottle;
import io.github.pylonmc.pylon.content.machines.fluid.Sprinkler;
import io.github.pylonmc.pylon.content.machines.simple.Grindstone;
import io.github.pylonmc.pylon.content.machines.smelting.Bloomery;
Expand Down Expand Up @@ -62,6 +63,7 @@ public void onEnable() {
pm.registerEvents(new SoulboundRune.SoulboundRuneListener(), this);
pm.registerEvents(new Bloomery.CreationListener(), this);
pm.registerEvents(new Grindstone.PlaceListener(), this);
pm.registerEvents(new LiquidXPBottle.XPBottleListener(), this);

new ShimmerMagnet.Ticker().runTaskTimer(this, 0, 10);

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/PylonBlocks.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import io.github.pylonmc.pylon.content.machines.diesel.production.Biorefinery;
import io.github.pylonmc.pylon.content.components.SmokestackCap;
import io.github.pylonmc.pylon.content.machines.diesel.production.Fermenter;
import io.github.pylonmc.pylon.content.machines.experience.FluidExperienceBottler;
import io.github.pylonmc.pylon.content.machines.experience.ExperienceDrain;
import io.github.pylonmc.pylon.content.machines.experience.ExperienceFountain;
import io.github.pylonmc.pylon.content.machines.fluid.*;
import io.github.pylonmc.pylon.content.machines.hydraulics.*;
import io.github.pylonmc.pylon.content.machines.simple.*;
Expand Down Expand Up @@ -187,5 +190,10 @@ public static void initialize() {
RebarBlock.register(PylonKeys.STEEL_SILO, Material.GRAY_TERRACOTTA, Silo.class);
RebarBlock.register(PylonKeys.PALLADIUM_SILO, Material.BLUE_TERRACOTTA, Silo.class);
RebarBlock.register(PylonKeys.SILO_CONVERTER, Material.STRIPPED_OAK_LOG, SiloConverter.class);
RebarBlock.register(PylonKeys.EXPERIENCE_DRAIN, Material.BLACKSTONE_SLAB, ExperienceDrain.class);
RebarBlock.register(PylonKeys.EXPERIENCE_FOUNTAIN, Material.END_STONE, ExperienceFountain.class);
RebarBlock.register(PylonKeys.EXPERIENCE_FOUNTAIN_SPOUT, Material.END_ROD, RebarBlock.class);
RebarBlock.register(PylonKeys.HYDRAULIC_EXPERIENCE_BOTTLER, Material.BREWING_STAND, FluidExperienceBottler.class);
RebarBlock.register(PylonKeys.DIESEL_EXPERIENCE_BOTTLER, Material.BREWING_STAND, FluidExperienceBottler.class);
}
}
8 changes: 8 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/PylonFluids.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,14 @@ private PylonFluids() {
SPONGE_IRON_SLURRY.register();
}

public static final RebarFluid LIQUID_XP = new RebarFluid(
pylonKey("liquid_xp"),
Material.GREEN_CONCRETE
).addTag(FluidTemperature.NORMAL);
static {
LIQUID_XP.register();
}

/**
* Calling this function will run the static blocks
*/
Expand Down
60 changes: 60 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/PylonItems.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
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.experience.FluidExperienceBottler;
import io.github.pylonmc.pylon.content.machines.experience.ExperienceDrain;
import io.github.pylonmc.pylon.content.machines.experience.ExperienceFountain;
import io.github.pylonmc.pylon.content.machines.experience.LiquidXPBottle;
import io.github.pylonmc.pylon.content.machines.fluid.*;
import io.github.pylonmc.pylon.content.machines.hydraulics.*;
import io.github.pylonmc.pylon.content.machines.simple.*;
Expand Down Expand Up @@ -3198,6 +3202,62 @@ private PylonItems() {
RecipeType.VANILLA_SHAPELESS.addRecipe(recipe);
}

public static final ItemStack EXPERIENCE_DRAIN = ItemStackBuilder.rebar(Material.BLACKSTONE_SLAB, PylonKeys.EXPERIENCE_DRAIN)
.build();
static {
RebarItem.register(ExperienceDrain.Item.class, EXPERIENCE_DRAIN, PylonKeys.EXPERIENCE_DRAIN);
PylonPages.FLUID_MACHINES.addItem(EXPERIENCE_DRAIN);
}

public static final ItemStack EXPERIENCE_FOUNTAIN = ItemStackBuilder.rebar(Material.END_STONE, PylonKeys.EXPERIENCE_FOUNTAIN)
.build();
static {
RebarItem.register(ExperienceFountain.Item.class, EXPERIENCE_FOUNTAIN, PylonKeys.EXPERIENCE_FOUNTAIN);
PylonPages.FLUID_MACHINES.addItem(EXPERIENCE_FOUNTAIN);
}

public static final ItemStack EXPERIENCE_FOUNTAIN_SPOUT = ItemStackBuilder.rebar(Material.END_ROD, PylonKeys.EXPERIENCE_FOUNTAIN_SPOUT)
.build();
static {
RebarItem.register(RebarItem.class, EXPERIENCE_FOUNTAIN_SPOUT, PylonKeys.EXPERIENCE_FOUNTAIN_SPOUT);
PylonPages.FLUID_MACHINES.addItem(EXPERIENCE_FOUNTAIN_SPOUT);
}

public static final ItemStack HYDRAULIC_EXPERIENCE_BOTTLER = ItemStackBuilder.rebar(Material.BREWING_STAND, PylonKeys.HYDRAULIC_EXPERIENCE_BOTTLER)
.build();
static {
RebarItem.register(FluidExperienceBottler.Item.class, HYDRAULIC_EXPERIENCE_BOTTLER, PylonKeys.HYDRAULIC_EXPERIENCE_BOTTLER);
PylonPages.HYDRAULIC_MACHINES.addItem(HYDRAULIC_EXPERIENCE_BOTTLER);
}

public static final ItemStack DIESEL_EXPERIENCE_BOTTLER = ItemStackBuilder.rebar(Material.BREWING_STAND, PylonKeys.DIESEL_EXPERIENCE_BOTTLER)
.build();
static {
RebarItem.register(FluidExperienceBottler.Item.class, DIESEL_EXPERIENCE_BOTTLER, PylonKeys.DIESEL_EXPERIENCE_BOTTLER);
PylonPages.DIESEL_MACHINES.addItem(DIESEL_EXPERIENCE_BOTTLER);
}

public static final ItemStack LIQUID_XP_BOTTLE = ItemStackBuilder.rebar(Material.EXPERIENCE_BOTTLE, PylonKeys.LIQUID_XP_BOTTLE)
.build();
static {
RebarItem.register(LiquidXPBottle.class, LIQUID_XP_BOTTLE);
PylonPages.MAGIC.addItem(LIQUID_XP_BOTTLE);
}

public static final ItemStack LIQUID_XP_BOTTLE_SUPER = ItemStackBuilder.rebar(Material.EXPERIENCE_BOTTLE, PylonKeys.LIQUID_XP_BOTTLE_SUPER)
.build();
static {
RebarItem.register(LiquidXPBottle.class, LIQUID_XP_BOTTLE_SUPER);
PylonPages.MAGIC.addItem(LIQUID_XP_BOTTLE_SUPER);
}

public static final ItemStack LIQUID_XP_BOTTLE_ULTRA = ItemStackBuilder.rebar(Material.EXPERIENCE_BOTTLE, PylonKeys.LIQUID_XP_BOTTLE_ULTRA)
.build();
static {
RebarItem.register(LiquidXPBottle.class, LIQUID_XP_BOTTLE_ULTRA);
PylonPages.MAGIC.addItem(LIQUID_XP_BOTTLE_ULTRA);
}

static {
PylonPages.initialise();
PylonHelpPages.initialise();
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/PylonKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -462,4 +462,14 @@ 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 EXPERIENCE_DRAIN = pylonKey("experience_drain");
public static final NamespacedKey EXPERIENCE_FOUNTAIN = pylonKey("experience_fountain");
public static final NamespacedKey EXPERIENCE_FOUNTAIN_SPOUT = pylonKey("experience_fountain_spout");
public static final NamespacedKey HYDRAULIC_EXPERIENCE_BOTTLER = pylonKey("hydraulic_experience_bottler");
public static final NamespacedKey DIESEL_EXPERIENCE_BOTTLER = pylonKey("diesel_experience_bottler");

public static final NamespacedKey LIQUID_XP_BOTTLE = pylonKey("liquid_xp_bottle");
public static final NamespacedKey LIQUID_XP_BOTTLE_SUPER = pylonKey("liquid_xp_bottle_super");
public static final NamespacedKey LIQUID_XP_BOTTLE_ULTRA = pylonKey("liquid_xp_bottle_ultra");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reckon some particles when XP is drained would be really nice here

Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package io.github.pylonmc.pylon.content.machines.experience;

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 net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Material;
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3i;

import java.util.List;
import java.util.Map;

public class ExperienceDrain extends RebarBlock implements RebarTickingBlock, RebarFluidBufferBlock, RebarSimpleMultiblock {
public final int xpDrainPeriodTicks = getSettings().getOrThrow("xp-drain-period-ticks", ConfigAdapter.INTEGER);
public final int xpDrainAmount = getSettings().getOrThrow("xp-drain-amount", ConfigAdapter.INTEGER);
public final int xpBufferAmount = getSettings().getOrThrow("xp-buffer-amount", ConfigAdapter.INTEGER);
private static final MultiblockComponent PEDESTAL_COMPONENT = new RebarSimpleMultiblock.VanillaMultiblockComponent(Material.COBBLESTONE_WALL);

public ExperienceDrain(@NotNull Block block, BlockCreateContext ctx) {
super(block, ctx);
createFluidBuffer(PylonFluids.LIQUID_XP, xpBufferAmount, false, true);
createFluidPoint(FluidPointType.OUTPUT, BlockFace.NORTH, ctx, false, 0.5f);
setTickInterval(xpDrainPeriodTicks);
}

public ExperienceDrain(@NotNull Block block, PersistentDataContainer pdc) {
super(block, pdc);
}

@Override
public @Nullable WailaDisplay getWaila(@NotNull Player player) {
return new WailaDisplay(getDefaultWailaTranslationKey().arguments(
RebarArgument.of("bar", PylonUtils.createFluidAmountBar(
fluidAmount(PylonFluids.LIQUID_XP),
fluidCapacity(PylonFluids.LIQUID_XP),
20,
TextColor.fromHexString("#1dcE420")
))
));
}

@Override
public @NotNull Map<@NotNull Vector3i, @NotNull MultiblockComponent> getComponents() {
return Map.of(
new Vector3i(3, 0, 0), PEDESTAL_COMPONENT,
new Vector3i(2, 0, 2), PEDESTAL_COMPONENT,
new Vector3i(0, 0, 3), PEDESTAL_COMPONENT,
new Vector3i(-2, 0, 2), PEDESTAL_COMPONENT,
new Vector3i(-3, 0, 0), PEDESTAL_COMPONENT,
new Vector3i(-2, 0, -2), PEDESTAL_COMPONENT,
new Vector3i(0, 0, -3), PEDESTAL_COMPONENT,
new Vector3i(2, 0, -2), PEDESTAL_COMPONENT
);
}
Comment on lines +61 to +73
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason for this to be a multiblock? It seems a bit illogical to me especially as the other blocks are not magic components and serve no functional purpose


// For some unknown reason, if you use /xp to give someone xp, it doesn't update player.getTotalExperience
public int getRealTotalExperience(Player player) {
int level = player.getLevel();
int totalExp = 0;

if (level <= 16) {
totalExp = (int) (Math.pow(level, 2) + (6 * level));
} else if (level <= 31) {
totalExp = (int) (2.5 * Math.pow(level, 2) - (40.5 * level) + 360);
} else {
totalExp = (int) (4.5 * Math.pow(level, 2) - (162.5 * level) + 2220);
}

float progress = player.getExp();
int expForNextLevel = Math.round(getExpToNextLevel(level) * progress);

return totalExp + expForNextLevel;
}

private int getExpToNextLevel(int level) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be static (probably doesnt need to be its own function anyway as it's only used in 1 place)

if (level <= 15) return 2 * level + 7;
if (level <= 30) return 5 * level - 38;
return 9 * level - 158;
}

public int subtractExperience(Player player, int amountToSubtract) {
int currentTotalExp = getRealTotalExperience(player);

int actualSubtracted = Math.min(currentTotalExp, amountToSubtract);
int newTotalExp = currentTotalExp - actualSubtracted;

player.setExp(0);
player.setLevel(0);
player.setTotalExperience(0);

player.giveExp(newTotalExp);

return actualSubtracted;
}

@Override
public void tick() {
for (Player player : getBlock().getWorld().getNearbyPlayers(getBlock().getLocation(), 1d, 5d, Player::isSneaking)) {
int actualSubtracted = subtractExperience(player, Math.min(xpDrainAmount, (int) Math.floor(fluidSpaceRemaining(PylonFluids.LIQUID_XP))));
addFluid(PylonFluids.LIQUID_XP, actualSubtracted);
}
}

public static class Item extends RebarItem {
public final int xpDrainPeriodTicks = getSettings().getOrThrow("xp-drain-period-ticks", ConfigAdapter.INTEGER);
public final int xpDrainAmount = getSettings().getOrThrow("xp-drain-amount", ConfigAdapter.INTEGER);

public Item(@NotNull ItemStack stack) {
super(stack);
}

@Override
public @NotNull List<@NotNull RebarArgument> getPlaceholders() {
return List.of(
RebarArgument.of("xp-drain-rate", UnitFormat.EXPERIENCE_PER_SECOND.format((double) xpDrainAmount / ((double) xpDrainPeriodTicks / 20))));
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XP is spawned on the corner of this block atm, I think it would be nice to have the XP orbs come out of the top of the spout

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The orbs aren't merging together for some reason. No idea why this would be but it's probably a good idea to look into it because that could cause a lot of lag if the machine is just left on

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package io.github.pylonmc.pylon.content.machines.experience;

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 net.kyori.adventure.text.format.TextColor;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;
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;

import java.util.List;
import java.util.Map;

public class ExperienceFountain extends RebarBlock implements RebarTickingBlock, RebarFluidBufferBlock, RebarSimpleMultiblock {
public final int amountToConvert = getSettings().getOrThrow("amount-to-convert", ConfigAdapter.INTEGER);
public final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER);
public final int liquidXpCapacity = getSettings().getOrThrow("liquid-xp-capacity", ConfigAdapter.INTEGER);

public ExperienceFountain(@NotNull Block block, BlockCreateContext ctx) {
super(block, ctx);
createFluidBuffer(PylonFluids.LIQUID_XP, liquidXpCapacity, true, false);
createFluidPoint(FluidPointType.INPUT, BlockFace.WEST, ctx, false, 0.5f);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fluid point should spawn facing the player by convention (north or south, can't remember which)

setTickInterval(tickInterval);
}

public ExperienceFountain(@NotNull Block block, PersistentDataContainer pdc) {
super(block, pdc);
}

@Override
public void tick() {
if(!isFormedAndFullyLoaded()){
return;
}
int amountConverted = (int)Math.min(amountToConvert, fluidAmount(PylonFluids.LIQUID_XP));
if(amountConverted <= 0){
return;
}
removeFluid(PylonFluids.LIQUID_XP, amountConverted);
ExperienceOrb orb = (ExperienceOrb) getBlock().getWorld().spawnEntity(getBlock().getLocation(), EntityType.EXPERIENCE_ORB);
orb.setExperience(amountConverted);
}

@Override
public @Nullable WailaDisplay getWaila(@NotNull Player player) {
return new WailaDisplay(getDefaultWailaTranslationKey().arguments(
RebarArgument.of("bar", PylonUtils.createFluidAmountBar(
fluidAmount(PylonFluids.LIQUID_XP),
fluidCapacity(PylonFluids.LIQUID_XP),
20,
TextColor.fromHexString("#1dc420")
))
));
}

@Override
public @NotNull Map<@NotNull Vector3i, @NotNull MultiblockComponent> getComponents() {
return Map.of(new Vector3i(0, 1, 0), new RebarSimpleMultiblock.RebarMultiblockComponent(PylonKeys.EXPERIENCE_FOUNTAIN_SPOUT));
}

public static class Item extends RebarItem {
public final int amountToConvert = getSettings().getOrThrow("amount-to-convert", ConfigAdapter.INTEGER);
public final int tickInterval = getSettings().getOrThrow("tick-interval", ConfigAdapter.INTEGER);

public Item(@NotNull ItemStack stack) {
super(stack);
}

@Override
public @NotNull List<@NotNull RebarArgument> getPlaceholders() {
return List.of(RebarArgument.of("production-rate", UnitFormat.EXPERIENCE_PER_SECOND.format((double) amountToConvert / ((double) tickInterval / 20)).decimalPlaces(0)));
}
}
}
Loading
Loading