Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b5dd101
Make AssemblyTypeRecipe
Intybyte Dec 5, 2025
d8ec92d
Fix AssemblyTableRecipe
Intybyte Dec 5, 2025
480920b
Add AssemblyTable
Intybyte Dec 13, 2025
eae013e
Fixes and crafting support
Intybyte Dec 13, 2025
1cbc86c
Doesn't work... but made some display entity handling
Intybyte Dec 13, 2025
e817237
Make entity work
Intybyte Dec 13, 2025
57e9d9f
Add item damage support
Intybyte Dec 14, 2025
d9e95fd
Regionize methods
Intybyte Dec 14, 2025
554c324
Add mirror support
Intybyte Dec 14, 2025
9513735
Add method for entity, still doesn't fix it though
Intybyte Dec 14, 2025
8e1809e
Rename to removeAllEntities
Intybyte Dec 14, 2025
899f830
Extract messages
Intybyte Dec 14, 2025
edf5758
Only allow 1 to not remove unnecessary items
Intybyte Dec 14, 2025
975437d
Use pylon serializers
Intybyte Dec 14, 2025
b38358c
Forgot this
Intybyte Dec 14, 2025
8632365
Cancel breaking
Intybyte Dec 14, 2025
a948f1b
Create and use new serializers
Intybyte Dec 15, 2025
fb77396
Use normal background
Intybyte Dec 15, 2025
c85e357
Blueprint workbench
Intybyte Dec 15, 2025
517dde7
Move classes to new package
Intybyte Dec 15, 2025
cecc5fd
Moving to post load did nothing
Intybyte Dec 16, 2025
629cef3
Added proper working displays
Intybyte Dec 16, 2025
f1ec3e7
Allow more items and update on output
Intybyte Dec 16, 2025
b61d790
Finalize Step.StateDisplay
Intybyte Dec 16, 2025
b95c280
No need to update in this case
Intybyte Dec 16, 2025
9953539
empty item is valid? fix
Intybyte Dec 16, 2025
e492950
Billboard
Intybyte Dec 16, 2025
2aa2c5e
Extract stuff for future assembly table
Intybyte Dec 16, 2025
35e1cd6
Remove unused
Intybyte Dec 16, 2025
5cf34f5
Solved issue, remove todo
Intybyte Dec 16, 2025
20ce610
Extract procedural crafting section
Intybyte Dec 16, 2025
2283c2f
Add getter annotation
Intybyte Dec 16, 2025
2e37851
More generic recipe
Intybyte Dec 16, 2025
434149c
Fix recipe matching in some edge cases
Intybyte Dec 16, 2025
07cb7c9
Remove unnecessary messages
Intybyte Dec 16, 2025
ad86f00
Nuke messages altogether
Intybyte Dec 16, 2025
8ed29f6
Extract more stuff
Intybyte Dec 16, 2025
2ef2b41
Add preserve offset
Intybyte Dec 16, 2025
047fada
PitKiln? really man?
Intybyte Dec 16, 2025
481347c
Extract getItemStep to holder
Intybyte Dec 16, 2025
4224844
Make AssemblyTable
Intybyte Dec 16, 2025
80b504f
Rename methods
Intybyte Dec 21, 2025
8f310ed
Add 4th entity on both mirrors
Intybyte Jan 2, 2026
bb262d0
Better name
Intybyte Jan 2, 2026
27f3d4c
Rename field to craftingInventory
Intybyte Jan 2, 2026
cd3aaf4
Merge branch 'master' into vaan/feature/procedural-crafting
Intybyte Jan 2, 2026
3334a61
Update en.yml
Intybyte Jan 2, 2026
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/base/BaseBlocks.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public static void initialize() {
PylonBlock.register(BaseKeys.MAGIC_PEDESTAL, Material.MOSSY_STONE_BRICK_WALL, Pedestal.class);
PylonBlock.register(BaseKeys.PEDESTAL, Material.STONE_BRICK_WALL, Pedestal.class);
PylonBlock.register(BaseKeys.MAGIC_ALTAR, Material.SMOOTH_STONE_SLAB, MagicAltar.class);
PylonBlock.register(BaseKeys.BLUEPRINT_WORKBENCH, Material.CRAFTING_TABLE, BlueprintWorkbench.class);
PylonBlock.register(BaseKeys.ASSEMBLY_TABLE, Material.CRAFTING_TABLE, AssemblyTable.class);
PylonBlock.register(BaseKeys.GRINDSTONE, Material.SMOOTH_STONE_SLAB, Grindstone.class);
PylonBlock.register(BaseKeys.GRINDSTONE_HANDLE, Material.OAK_FENCE, GrindstoneHandle.class);
PylonBlock.register(BaseKeys.ENRICHED_SOUL_SOIL, Material.SOUL_SOIL, EnrichedSoulSoil.class);
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/base/BaseItems.java
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,21 @@ private BaseItems() {
BasePages.TOOLS.addItem(CONFETTI_POPPER);
}

public static final ItemStack ASSEMBLY_TABLE = ItemStackBuilder.pylon(Material.CRAFTING_TABLE, BaseKeys.ASSEMBLY_TABLE)
.build();

static {
PylonItem.register(PylonItem.class, ASSEMBLY_TABLE, BaseKeys.ASSEMBLY_TABLE);
BasePages.SIMPLE_MACHINES.addItem(ASSEMBLY_TABLE);
}

public static final ItemStack BLUEPRINT_WORKBENCH = ItemStackBuilder.pylon(Material.CRAFTING_TABLE, BaseKeys.BLUEPRINT_WORKBENCH)
.build();
static {
PylonItem.register(PylonItem.class, BLUEPRINT_WORKBENCH, BaseKeys.BLUEPRINT_WORKBENCH);
BasePages.SIMPLE_MACHINES.addItem(BLUEPRINT_WORKBENCH);
}

public static final ItemStack GRINDSTONE = ItemStackBuilder.pylon(Material.SMOOTH_STONE_SLAB, BaseKeys.GRINDSTONE)
.build();
static {
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/base/BaseKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ public class BaseKeys {
public static final NamespacedKey MAGIC_PEDESTAL = baseKey("magic_pedestal");
public static final NamespacedKey MAGIC_ALTAR = baseKey("magic_altar");

public static final NamespacedKey ASSEMBLY_TABLE = baseKey("assembly_table");
public static final NamespacedKey BLUEPRINT_WORKBENCH = baseKey("blueprint_workbench");

public static final NamespacedKey GRINDSTONE = baseKey("grindstone");
public static final NamespacedKey GRINDSTONE_HANDLE = baseKey("grindstone_handle");

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/github/pylonmc/pylon/base/BaseRecipes.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ public static void initialize() {
PitKilnRecipe.RECIPE_TYPE.register();
StrainingRecipe.RECIPE_TYPE.register();
TableSawRecipe.RECIPE_TYPE.register();
AssemblyTableRecipe.RECIPE_TYPE.register();
BlueprintWorkbenchRecipe.RECIPE_TYPE.register();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package io.github.pylonmc.pylon.base.content.machines.simple;

import io.github.pylonmc.pylon.base.recipes.AssemblyTableRecipe;
import io.github.pylonmc.pylon.base.recipes.intermediate.Step;
import io.github.pylonmc.pylon.core.block.base.PylonBreakHandler;
import io.github.pylonmc.pylon.core.block.base.PylonInteractBlock;
import io.github.pylonmc.pylon.core.block.context.BlockBreakContext;
import io.github.pylonmc.pylon.core.block.context.BlockCreateContext;
import io.github.pylonmc.pylon.core.util.gui.GuiItems;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.jetbrains.annotations.NotNull;
import xyz.xenondevs.inventoryaccess.component.AdventureComponentWrapper;
import xyz.xenondevs.invui.gui.Gui;
import xyz.xenondevs.invui.inventory.VirtualInventory;
import xyz.xenondevs.invui.inventory.event.ItemPostUpdateEvent;
import xyz.xenondevs.invui.inventory.event.ItemPreUpdateEvent;
import xyz.xenondevs.invui.inventory.event.UpdateReason;
import xyz.xenondevs.invui.window.Window;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class AssemblyTable extends ProceduralCraftingTable<AssemblyTableRecipe> implements PylonBreakHandler, PylonInteractBlock {
private final VirtualInventory stepDisplay = new VirtualInventory(1);

private int offset = 1;

@SuppressWarnings("unused")
public AssemblyTable(@NotNull Block block, @NotNull BlockCreateContext context) {
super(block, context);

this.craftingInventory.setPostUpdateHandler(this::updateInputGrid);
this.stepDisplay.setPreUpdateHandler(this::cancelEverything);

updateStep();
}

@SuppressWarnings("unused")
public AssemblyTable(@NotNull Block block, @NotNull PersistentDataContainer pdc) {
super(block, pdc);

this.craftingInventory.setPostUpdateHandler(this::updateInputGrid);
this.stepDisplay.setPreUpdateHandler(this::cancelEverything);
}

@Override
protected AssemblyTableRecipe deserializeRecipeKey(NamespacedKey key) {
return AssemblyTableRecipe.RECIPE_TYPE.getRecipe(key);
}

@Override
public void updateStep() {
updateStepItem();
updateRecipeEntities();

if (currentRecipe != null) {
this.currentStateDisplay.update(getStep(), currentProgress);
} else {
this.currentStateDisplay.setVisibility(false);
}
}

@Override
public void completeRecipe() {
Location above = getBlock().getRelative(BlockFace.UP).getLocation().toCenterLocation();
currentRecipe.results().forEach(stack ->
above.getWorld().spawn(above, Item.class, (item) -> item.setItemStack(stack))
);

List<ItemStack> requiredItems = new ArrayList<>(currentRecipe.ingredients());
ItemStack[] unsafeItems = craftingInventory.getUnsafeItems();
for (int i = 0; i < unsafeItems.length; i++) {
ItemStack item = unsafeItems[i];
if (item == null) continue;

Iterator<ItemStack> iterator = requiredItems.iterator();
while (iterator.hasNext()) {
ItemStack requirement = iterator.next();

if (requirement.getAmount() > item.getAmount()) continue;
if (!requirement.isSimilar(item)) continue;

item.subtract(requirement.getAmount());
if (item.getAmount() == 0) {
craftingInventory.setItem(UpdateReason.SUPPRESSED, i, null);
}

iterator.remove();
break;
}
}

this.currentRecipe = null;
this.currentProgress = null;

updateInputGrid(true);
removeAddedEntities();
}

private void updateStepItem() {
ItemStack stack;
if (currentRecipe == null || currentProgress == null) {
stack = GuiItems.background().getItemProvider().get();
} else {
Step current = this.getStep();
stack = current.asStack(current.uses() - currentProgress.getUsedAmount());
}

this.stepDisplay.setItem(UpdateReason.SUPPRESSED, 0, stack);
}

private void cancelEverything(@NotNull ItemPreUpdateEvent event) {
UpdateReason reason = event.getUpdateReason();
if (reason == null || reason.equals(UpdateReason.SUPPRESSED)) return;

event.setCancelled(true);
}

@Override
public void onBreak(@NotNull List<@NotNull ItemStack> drops, @NotNull BlockBreakContext context) {
for (ItemStack item : craftingInventory.getItems()) {
if (item != null) {
drops.add(item);
}
}
}

public @NotNull Gui createGui() {
var builder = Gui.normal()
.setStructure(
"# # # # # # # # #",
"# # # x x x # # #",
"# # # x x x # $ #",
"# # # x x x # # #",
"# # # # # # # # #"
)
.addIngredient('x', craftingInventory)
.addIngredient('$', stepDisplay)
.addIngredient('#', GuiItems.background());

return builder.build();
}

public void handleGui(PlayerInteractEvent event) {
Copy link
Member

Choose a reason for hiding this comment

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

...why do you have a custom gui handler?

if (event.getPlayer().isSneaking()
|| event.useInteractedBlock() == Event.Result.DENY) {
return;
}

event.setUseInteractedBlock(Event.Result.DENY);
event.setUseItemInHand(Event.Result.DENY);

Window.single()
.setGui(createGui())
.setTitle(new AdventureComponentWrapper(getNameTranslationKey()))
.setViewer(event.getPlayer())
.build()
.open();
}

@Override
public void onInteract(@NotNull PlayerInteractEvent event) {
if (event.getHand() != EquipmentSlot.HAND) return;

if (event.getAction().isRightClick()) {
handleGui(event);
return;
}

Player player = event.getPlayer();
ItemStack mainHand = player.getInventory().getItemInMainHand();
if (mainHand.isEmpty()) {
offset++; // left click changes selected recipe, if any
updateInputGrid(true);
} else {
if (currentRecipe == null || currentProgress == null) return;
boolean outcome = progressRecipe(mainHand, player, true);
if (outcome) {
event.setCancelled(true);
}
}
}

private void updateInputGrid(@NotNull ItemPostUpdateEvent event) {
updateInputGrid(false);
}

public void updateInputGrid(boolean preserveOffset) {
if (!preserveOffset) {
this.offset = 1;
}

this.currentRecipe = AssemblyTableRecipe.findRecipe(craftingInventory.getItems(), offset);
if (this.currentRecipe == null) {
this.currentProgress = null;
} else {
this.currentProgress = new Step.ActionStep(0, 0);
}

updateStep();
}
}
Loading
Loading