Skip to content

Add liquid XP#809

Open
OhmV-IR wants to merge 12 commits intomasterfrom
add-liquid-xp
Open

Add liquid XP#809
OhmV-IR wants to merge 12 commits intomasterfrom
add-liquid-xp

Conversation

@OhmV-IR
Copy link
Copy Markdown
Contributor

@OhmV-IR OhmV-IR commented Mar 1, 2026

Closes #745 ,
Closes #744 ,
Closes #743

  • You get liquid XP from vanilla XP by sneaking on top of the XP drain, note that this is done at a slightly net gain rate to make this system worthwhile
  • Liquid XP can be remade into vanilla XP either through the experience fountain which drops it as vanilla XP orbs or via the hydraulic / diesel experience bottlers
  • 1mB of liquid XP = 1 vanilla XP

Copy link
Copy Markdown
Member

@Intybyte Intybyte left a comment

Choose a reason for hiding this comment

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

Github didn't link some of the issues bw

Comment thread src/main/resources/settings/experience_drain.yml Outdated
Comment thread src/main/resources/recipes/minecraft/crafting_shaped.yml Outdated
Comment thread src/main/java/io/github/pylonmc/pylon/PylonFluids.java Outdated
OhmV-IR added 2 commits March 10, 2026 19:09
# Conflicts:
#	src/main/java/io/github/pylonmc/pylon/PylonItems.java
#	src/main/resources/lang/en.yml
#	src/main/resources/recipes/minecraft/crafting_shaped.yml
#	src/main/resources/researches.yml
Copy link
Copy Markdown
Contributor

@LordIdra LordIdra left a comment

Choose a reason for hiding this comment

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

Missing research key
image

Research is far too expensive - this is most useful early-ish game when you want to save XP. May I suggest 10 points?

Research should probably be called something other than 'experience production' given you are not actually producing XP ('Experience Liquefaction'?)

I suggest the drain, fountain, and fountain spout go in Fluid Machines as there are machines related to XP which are in other categories (i.e. bottler and probably more in future), so having a separate category for XP stuff is a bit confusing

I think the recipes for XP stuff are also too expensive as this is very useful early-game to conserve XP - I would suggest a very cheap recipe here, especially for drain

Diesel/hydraulic bottlers look strange as there's nothing connecting the connection point to the brewing stand
Image

Diesel/hydraulic bottler multiblock is quite weird... might I suggest just having an input hatch underneath instead?

As discussed the drain conversion ratio should probably be nuked

Drain appears to require shimmer pedestals (???)

Experience liquid should probably be green to reflect XP orb color - this is how mods do it

Comment thread src/main/resources/lang/en.yml
@OhmV-IR
Copy link
Copy Markdown
Contributor Author

OhmV-IR commented Apr 11, 2026

My todo list of your comments (so I can remember what I've done)

  • Fix research points amount
  • Rename research
  • Move to fluid machines
  • Adjust recipes to be cheaper
  • Fix diesel / hydraulic bottlers multiblock look
  • Drain shouldn't require shimmer pedestals
  • Experience liquid should be green
  • Deal with CW comments

@OhmV-IR OhmV-IR requested a review from LordIdra April 12, 2026 12:47
lore: |-
<arrow> Drains experience when you <insn>Sneak</insn> on top of the block
<arrow> <attr>Experience drain amount:</attr> %xp-drain-rate%
<arrow> <attr>Experience conversion ratio:</attr> %conversion-ratio%
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.

yeet

xp-drain-period-ticks: 5
xp-drain-amount: 10
xp-buffer-amount: 250
conversion-ratio: 1.0 # How much liquid xp for each vanilla XP. If this is more than 1, the machine is profitable No newline at end of file
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.

Not used

Comment on lines +2419 to +2423
experience_fountain:
name: "Experience Fountain"
lore: |-
<arrow> Produces experience when connected to a liquid xp source
<arrow> <attr>Experience production rate:</attr> %production-rate%
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.

Missing multiblock components lore

experience_drain:
name: "Experience Drain"
lore: |-
<arrow> Drains experience when you <insn>Sneak</insn> on top of the block
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.

Idra Lore Nitpick™: Maybe better as <arrow> <insn>Sneak</insn> while standing on top to liquefy your experience

Comment on lines +61 to +73
@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
);
}
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

Comment on lines +97 to +113
public @Nullable FluidInputHatch getFluidInputHatch() {
Vector relativeLocation = Vector.fromJOML(RebarUtils.rotateVectorToFace(new Vector3i(2, 1, 0), getFacing()));
Location inputHatchLocation = getBlock().getLocation().add(relativeLocation);
return BlockStorage.getAs(FluidInputHatch.class, inputHatchLocation);
}

public @Nullable FluidInputHatch getExperienceInputHatch() {
Vector relativeLocation = Vector.fromJOML(RebarUtils.rotateVectorToFace(new Vector3i(0, 2, 0), getFacing()));
Location hatchLoc = getBlock().getLocation().add(relativeLocation);
return BlockStorage.getAs(FluidInputHatch.class, hatchLoc);
}

public @Nullable FluidOutputHatch getFluidOutputHatch() {
Vector relativeLocation = Vector.fromJOML(RebarUtils.rotateVectorToFace(new Vector3i(-2, 1, 0), getFacing()));
Location inputHatchLocation = getBlock().getLocation().add(relativeLocation);
return BlockStorage.getAs(FluidOutputHatch.class, inputHatchLocation);
}
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 have since added nice util methods for this - see how biorefinery does it for example

Comment on lines +123 to +147
if (inputHatch == null || xpHatch == null) {
return;
}
if(outputFluid != null && outputHatch == null){
return;
}
if (!inputHatch.hasFluid(inputFluid) || !xpHatch.hasFluid(PylonFluids.LIQUID_XP)) {
return;
}
if (inputHatch.fluidAmount(inputFluid) < inputFluidConsumptionRate * bottleProductionRateTicks / 20) {
return;
}
if(xpHatch.fluidAmount(PylonFluids.LIQUID_XP) < XP_AMOUNT){
return;
}
if (bottleInput.getItem(0) == null || bottleInput.getItem(0).getType() != Material.GLASS_BOTTLE) {
return;
}
RebarItem bottleOutputItem = RebarItem.fromStack(bottleOutput.getItem(0));
if (bottleOutputItem != null && (!bottleOutputItem.getKey().equals(PylonKeys.LIQUID_XP_BOTTLE) || bottleOutputItem.getStack().getAmount() == bottleOutputItem.getStack().getMaxStackSize())) {
return;
}
if (outputFluid != null && outputHatch.fluidSpaceRemaining(outputFluid) < fluidOutputProductionRate * bottleProductionRateTicks / 20) {
return;
}
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 would collapse some of these for readability since there are so many

Comment on lines +189 to +203
@Override
public @NotNull Gui createGui() {
return Gui.builder()
.setStructure(
"# # I # # # O # #",
"# # i # # # o # #",
"# # I # # # O # #"
)
.addIngredient('#', GuiItems.background())
.addIngredient('I', GuiItems.input())
.addIngredient('O', GuiItems.output())
.addIngredient('i', bottleInput)
.addIngredient('o', bottleOutput)
.build();
}
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.

Missing an indication of progress

Comment on lines +213 to +227
if(xpInputHatch != null && xpInputHatch.hasFluid(PylonFluids.LIQUID_XP)) {
return new WailaDisplay(getDefaultWailaTranslationKey().arguments(RebarArgument.of("xpbar", PylonUtils.createFluidAmountBar(
xpInputHatch.fluidAmount(PylonFluids.LIQUID_XP),
xpInputHatch.fluidCapacity(PylonFluids.LIQUID_XP),
20,
TextColor.fromHexString("#1dc420")
))));
} else {
return new WailaDisplay(getDefaultWailaTranslationKey().arguments(RebarArgument.of("xpbar", PylonUtils.createFluidAmountBar(
0,
0,
20,
TextColor.fromHexString("#1dc420")
))));
}
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 would show a recipe progress bar instead of the amount of liquid XP here as there are multiple fluid inputs/outputs so it can be a bit confusing, and breaks convention with other machines

Comment on lines +232 to +242
Map<@NotNull Vector3i, @NotNull MultiblockComponent> map = new HashMap<>();
map.put(new Vector3i(0, 1, 0), new RebarSimpleMultiblock.VanillaMultiblockComponent(Material.IRON_BARS));
map.put(new Vector3i(0, 2, 0), new RebarSimpleMultiblock.RebarMultiblockComponent(PylonKeys.FLUID_INPUT_HATCH));
map.put(new Vector3i(1, 1, 0), new RebarSimpleMultiblock.VanillaMultiblockComponent(Material.IRON_BARS));
map.put(new Vector3i(1, 0, 0), new RebarSimpleMultiblock.VanillaMultiblockComponent(Material.IRON_BARS));
map.put(new Vector3i(2, 1, 0), new RebarSimpleMultiblock.RebarMultiblockComponent(PylonKeys.FLUID_INPUT_HATCH));
if(outputFluid != null){
map.put(new Vector3i(-1, 1, 0), new RebarSimpleMultiblock.VanillaMultiblockComponent(Material.IRON_BARS));
map.put(new Vector3i(-1, 0, 0), new RebarSimpleMultiblock.VanillaMultiblockComponent(Material.IRON_BARS));
map.put(new Vector3i(-2, 1, 0), new RebarSimpleMultiblock.RebarMultiblockComponent(PylonKeys.FLUID_OUTPUT_HATCH));
}
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.

Have to say I'm not a fan of this multiblock aesthetically... May I suggest something like the following?

Image

(Lapis block and iron support beams)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hydraulic XP bottler XP fountain XP drain

4 participants