Implement more stable WorkbenchBlockEntity, add buttons and GUIs, placement order components and dynamic registries, attachable inventories
This commit is contained in:
parent
2197c69f0f
commit
662aa41c63
@ -1,39 +1,57 @@
|
|||||||
package net.sys42.dakedres.grafting;
|
package net.sys42.dakedres.grafting;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
import eu.pb4.polymer.core.api.block.PolymerBlockUtils;
|
import eu.pb4.polymer.core.api.block.PolymerBlockUtils;
|
||||||
import eu.pb4.polymer.resourcepack.api.PolymerModelData;
|
import eu.pb4.polymer.core.api.other.PolymerComponent;
|
||||||
import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
|
import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
|
import net.fabricmc.fabric.api.event.registry.DynamicRegistries;
|
||||||
|
import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder;
|
||||||
|
import net.fabricmc.fabric.api.event.registry.RegistryAttribute;
|
||||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||||
import net.minecraft.block.AbstractBlock;
|
import net.minecraft.block.AbstractBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.component.ComponentType;
|
||||||
import net.minecraft.item.BlockItem;
|
import net.minecraft.item.BlockItem;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.registry.*;
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.resource.Resource;
|
||||||
import net.minecraft.registry.Registries;
|
|
||||||
import net.minecraft.registry.Registry;
|
|
||||||
import net.minecraft.sound.BlockSoundGroup;
|
import net.minecraft.sound.BlockSoundGroup;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.sys42.dakedres.grafting.blocks.Workbench;
|
import net.sys42.dakedres.grafting.block.Workbench;
|
||||||
import net.sys42.dakedres.grafting.blocks.WorkbenchBlockEntity;
|
import net.sys42.dakedres.grafting.block.entity.WorkbenchBlockEntity;
|
||||||
|
import net.sys42.dakedres.grafting.data.PlacementOrder;
|
||||||
|
import net.sys42.dakedres.grafting.data.PlacementOrderReloadListener;
|
||||||
|
import net.sys42.dakedres.grafting.item.WorkbenchItem;
|
||||||
|
import net.sys42.dakedres.grafting.ui.Icons;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
public class Grafting implements ModInitializer {
|
public class Grafting implements ModInitializer {
|
||||||
public static final String MOD_ID = "grafting";
|
public static final String MOD_ID = "grafting";
|
||||||
|
public static final Identifier WORKBENCH_ID = Identifier.of(Grafting.MOD_ID, "workbench");
|
||||||
|
public static final Identifier PLACEMENT_ORDER_COMPONENT_ID = Identifier.of(Grafting.MOD_ID, "placement_order");
|
||||||
|
|
||||||
// This logger is used to write text to the console and the log file.
|
// This logger is used to write text to the console and the log file.
|
||||||
// It is considered best practice to use your mod id as the logger's name.
|
// It is considered best practice to use your mod id as the logger's name.
|
||||||
// That way, it's clear which mod wrote info, warnings, and errors.
|
// That way, it's clear which mod wrote info, warnings, and errors.
|
||||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||||
|
|
||||||
public static final Identifier WORKBENCH_ID = Identifier.of(Grafting.MOD_ID, "workbench");
|
|
||||||
public static final Block WORKBENCH = Registry.register(
|
public static final Block WORKBENCH = Registry.register(
|
||||||
Registries.BLOCK, WORKBENCH_ID,
|
Registries.BLOCK, WORKBENCH_ID,
|
||||||
new Workbench(AbstractBlock.Settings.create().sounds(BlockSoundGroup.WOOD))
|
new Workbench(
|
||||||
|
// TODO: A bit easier to break?
|
||||||
|
Block.Settings.copy(Blocks.CRAFTING_TABLE)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
public static final BlockEntityType<WorkbenchBlockEntity> WORKBENCH_BLOCK_ENTITY_TYPE = Registry.register(
|
public static final BlockEntityType<WorkbenchBlockEntity> WORKBENCH_BLOCK_ENTITY_TYPE = Registry.register(
|
||||||
Registries.BLOCK_ENTITY_TYPE,
|
Registries.BLOCK_ENTITY_TYPE,
|
||||||
@ -42,9 +60,20 @@ public class Grafting implements ModInitializer {
|
|||||||
);
|
);
|
||||||
public static final BlockItem WORKBENCH_ITEM = Registry.register(
|
public static final BlockItem WORKBENCH_ITEM = Registry.register(
|
||||||
Registries.ITEM, WORKBENCH_ID,
|
Registries.ITEM, WORKBENCH_ID,
|
||||||
new Workbench.WorkbenchItem(WORKBENCH, new Item.Settings())
|
new WorkbenchItem(WORKBENCH, new Item.Settings())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public static final ComponentType<Identifier> PLACEMENT_ORDER_COMPONENT_TYPE = Registry.register(
|
||||||
|
Registries.DATA_COMPONENT_TYPE,
|
||||||
|
PLACEMENT_ORDER_COMPONENT_ID,
|
||||||
|
ComponentType.<Identifier>builder().codec(Identifier.CODEC).build()
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final RegistryKey<Registry<PlacementOrder>> PLACEMENT_ORDER_REGISTRY_KEY = RegistryKey.ofRegistry(Identifier.of(Grafting.MOD_ID, "placement_order"));
|
||||||
|
// public static final Registry<PlacementOrder> PLACEMENT_ORDER_REGISTRY = FabricRegistryBuilder.createSimple(PLACEMENT_ORDER_REGISTRY_KEY)
|
||||||
|
// .attribute(RegistryAttribute.MODDED)
|
||||||
|
// .buildAndRegister();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
// This code runs as soon as Minecraft is in a mod-load-ready state.
|
// This code runs as soon as Minecraft is in a mod-load-ready state.
|
||||||
@ -54,6 +83,11 @@ public class Grafting implements ModInitializer {
|
|||||||
LOGGER.info("Hello Fabric world!");
|
LOGGER.info("Hello Fabric world!");
|
||||||
|
|
||||||
PolymerBlockUtils.registerBlockEntity(WORKBENCH_BLOCK_ENTITY_TYPE);
|
PolymerBlockUtils.registerBlockEntity(WORKBENCH_BLOCK_ENTITY_TYPE);
|
||||||
|
PolymerComponent.registerDataComponent(PLACEMENT_ORDER_COMPONENT_TYPE);
|
||||||
|
Icons.init(); // Static initialization
|
||||||
|
// PlacementOrderReloadListener.init();
|
||||||
|
|
||||||
|
DynamicRegistries.register(PLACEMENT_ORDER_REGISTRY_KEY, PlacementOrder.CODEC);
|
||||||
|
|
||||||
if (PolymerResourcePackUtils.addModAssets(MOD_ID)) {
|
if (PolymerResourcePackUtils.addModAssets(MOD_ID)) {
|
||||||
LOGGER.info("Successfully added mod assets for " + MOD_ID);
|
LOGGER.info("Successfully added mod assets for " + MOD_ID);
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package net.sys42.dakedres.grafting;
|
package net.sys42.dakedres.grafting;
|
||||||
|
|
||||||
public class PlacementMatrixUtils {
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.sys42.dakedres.grafting.data.PlacementOrder;
|
||||||
|
|
||||||
|
public class PlacementUtils {
|
||||||
public static final int[][] ROTATION_MATRICES = {
|
public static final int[][] ROTATION_MATRICES = {
|
||||||
{ 0,1,2,3,4,5,6,7,8 },
|
{ 0,1,2,3,4,5,6,7,8 },
|
||||||
{ 1,2,5,0,4,8,3,6,7 },
|
{ 1,2,5,0,4,8,3,6,7 },
|
||||||
@ -33,6 +38,10 @@ public class PlacementMatrixUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int[] rotate(int[] matrix, int rotation) {
|
public static int[] rotate(int[] matrix, int rotation) {
|
||||||
|
if(rotation == 0) {
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
int[] o = new int[matrix.length];
|
int[] o = new int[matrix.length];
|
||||||
int[] rotationMatrix = ROTATION_MATRICES[rotation];
|
int[] rotationMatrix = ROTATION_MATRICES[rotation];
|
||||||
|
|
||||||
@ -43,12 +52,34 @@ public class PlacementMatrixUtils {
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int[] getPlacements() {
|
public static int[] getPlacements(ItemStack itemStack, ServerWorld world) {
|
||||||
int[] placements = {
|
// int[] placements = {
|
||||||
2,0,8,
|
// 2,0,8,
|
||||||
1,3,4,
|
// 1,3,4,
|
||||||
7,6,5
|
// 7,6,5
|
||||||
};
|
// };
|
||||||
|
// return placements;
|
||||||
|
|
||||||
|
|
||||||
|
return getPlacementOrder(itemStack, world).getOrderMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PlacementOrder getPlacementOrder(ItemStack itemStack, ServerWorld world) {
|
||||||
|
Identifier placementIdentifier = itemStack.get(Grafting.PLACEMENT_ORDER_COMPONENT_TYPE);
|
||||||
|
|
||||||
|
if(placementIdentifier == null) {
|
||||||
|
placementIdentifier = PlacementOrder.DEFAULT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlacementOrder placementOrder = world.getRegistryManager().get(Grafting.PLACEMENT_ORDER_REGISTRY_KEY).get(placementIdentifier);
|
||||||
|
if(placementOrder == null) {
|
||||||
|
return PlacementOrder.DEFAULT_FALLBACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return placementOrder;
|
||||||
|
};
|
||||||
|
|
||||||
|
public static int[] asIndexes(int[] placements) {
|
||||||
int[] out = new int[9];
|
int[] out = new int[9];
|
||||||
|
|
||||||
for(int i = 0; i < 9; i++) {
|
for(int i = 0; i < 9; i++) {
|
@ -0,0 +1,92 @@
|
|||||||
|
package net.sys42.dakedres.grafting.block;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import eu.pb4.polymer.blocks.api.BlockModelType;
|
||||||
|
import eu.pb4.polymer.blocks.api.PolymerBlockModel;
|
||||||
|
import eu.pb4.polymer.blocks.api.PolymerBlockResourceUtils;
|
||||||
|
import eu.pb4.polymer.blocks.api.PolymerTexturedBlock;
|
||||||
|
import eu.pb4.polymer.core.api.block.PolymerBlock;
|
||||||
|
import net.minecraft.block.*;
|
||||||
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.fluid.FluidState;
|
||||||
|
import net.minecraft.fluid.Fluids;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.state.StateManager;
|
||||||
|
import net.minecraft.state.property.Property;
|
||||||
|
import net.minecraft.util.ActionResult;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.WorldAccess;
|
||||||
|
import net.sys42.dakedres.grafting.Grafting;
|
||||||
|
import net.sys42.dakedres.grafting.block.entity.WorkbenchBlockEntity;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import static net.minecraft.state.property.Properties.WATERLOGGED;
|
||||||
|
|
||||||
|
// TODO: Loot tables, including dropping contents when broken
|
||||||
|
public class Workbench extends BlockWithEntity implements PolymerBlock, Waterloggable, PolymerTexturedBlock {
|
||||||
|
private final BlockState waterloggedModel;
|
||||||
|
private final BlockState model;
|
||||||
|
|
||||||
|
public Workbench(Settings settings) {
|
||||||
|
super(settings);
|
||||||
|
this.setDefaultState(this.getDefaultState().with(WATERLOGGED, false));
|
||||||
|
|
||||||
|
var model = PolymerBlockModel.of(Identifier.of(Grafting.MOD_ID, "block/workbench"));
|
||||||
|
this.model = PolymerBlockResourceUtils.requestBlock(BlockModelType.TRANSPARENT_BLOCK, model);
|
||||||
|
waterloggedModel = PolymerBlockResourceUtils.requestBlock(BlockModelType.TRANSPARENT_BLOCK_WATERLOGGED, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MapCodec<? extends BlockWithEntity> getCodec() {
|
||||||
|
return createCodec(Workbench::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
return new WorkbenchBlockEntity(pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getPolymerBlockState(BlockState state) {
|
||||||
|
return state.get(WATERLOGGED) ? waterloggedModel : model;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just so it plays the right break sound
|
||||||
|
@Override
|
||||||
|
public BlockState getPolymerBreakEventBlockState(BlockState state, ServerPlayerEntity player) {
|
||||||
|
return Blocks.CRAFTING_TABLE.getDefaultState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public BlockState getPlacementState(@NotNull ItemPlacementContext ctx) {
|
||||||
|
WorldAccess worldAccess = ctx.getWorld();
|
||||||
|
BlockPos blockPos = ctx.getBlockPos();
|
||||||
|
return this.getDefaultState().with(WATERLOGGED, worldAccess.getFluidState(blockPos).getFluid() == Fluids.WATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FluidState getFluidState(BlockState state) {
|
||||||
|
return state.get(WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(new Property[]{WATERLOGGED});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
||||||
|
if(!player.isSneaking() && world.getBlockEntity(pos) instanceof WorkbenchBlockEntity blockEntity) {
|
||||||
|
// ((ServerPlayerEntity) player).openHandledScreen(blockEntity);
|
||||||
|
blockEntity.openMenu((ServerPlayerEntity) player);
|
||||||
|
return ActionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onUse(state, world, pos, player, hit);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package net.sys42.dakedres.grafting.block.entity;
|
||||||
|
|
||||||
|
import eu.pb4.sgui.api.gui.SimpleGui;
|
||||||
|
import net.minecraft.inventory.Inventory;
|
||||||
|
import net.minecraft.screen.GenericContainerScreenHandler;
|
||||||
|
import net.minecraft.screen.ScreenHandlerType;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
|
||||||
|
public class AttachedInventory {
|
||||||
|
private Inventory inventory;
|
||||||
|
public AttachedInventory(Inventory inventory) {
|
||||||
|
this.inventory = inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean valid(Inventory inventory) {
|
||||||
|
return inventory.size() >= 9 && (inventory.size() % 9) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int rows() {
|
||||||
|
return Math.min(inventory.size() / 9, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenHandlerType<GenericContainerScreenHandler> extendedScreenHandlerType(int basis) {
|
||||||
|
int r = rows() + basis;
|
||||||
|
|
||||||
|
if(r <= 2) {
|
||||||
|
return ScreenHandlerType.GENERIC_9X3;
|
||||||
|
} else if(r == 3) {
|
||||||
|
return ScreenHandlerType.GENERIC_9X4;
|
||||||
|
} else if(r == 4) {
|
||||||
|
return ScreenHandlerType.GENERIC_9X5;
|
||||||
|
} else {
|
||||||
|
return ScreenHandlerType.GENERIC_9X6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void redirectSlots(int basis, SimpleGui gui) {
|
||||||
|
int sizeBefore = (basis + 1) * 9;
|
||||||
|
for(int i = 0; i < rows() * 9; i++) {
|
||||||
|
gui.setSlotRedirect(sizeBefore + i, new Slot(inventory, i, i, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,215 @@
|
|||||||
|
package net.sys42.dakedres.grafting.block.entity;
|
||||||
|
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.inventory.Inventory;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtList;
|
||||||
|
import net.minecraft.registry.RegistryWrapper;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class OrderedContainer extends BlockEntity implements Inventory {
|
||||||
|
private final int size;
|
||||||
|
private final ArrayList<ItemStack> inventory;
|
||||||
|
public OrderedContainer(BlockEntityType<?> type, BlockPos pos, BlockState state, int size) {
|
||||||
|
super(type, pos, state);
|
||||||
|
this.size = size;
|
||||||
|
this.inventory = new ArrayList<>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
|
||||||
|
super.readNbt(nbt, registries);
|
||||||
|
readInventoryNbt(nbt, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readInventoryNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
|
||||||
|
NbtList nbtList = nbt.getList("Items", 10);
|
||||||
|
|
||||||
|
for(int i = 0; i < nbtList.size(); ++i) {
|
||||||
|
NbtCompound nbtCompound = nbtList.getCompound(i);
|
||||||
|
inventory.add((ItemStack) ItemStack.fromNbt(registries, nbtCompound).orElse(ItemStack.EMPTY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
|
||||||
|
super.writeNbt(nbt, registries);
|
||||||
|
writeInventoryNbt(nbt, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeInventoryNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
|
||||||
|
NbtList nbtList = new NbtList();
|
||||||
|
|
||||||
|
for (ItemStack itemStack : inventory) {
|
||||||
|
if (!itemStack.isEmpty()) {
|
||||||
|
NbtCompound nbtCompound = new NbtCompound();
|
||||||
|
nbtList.add(itemStack.encode(registries, nbtCompound));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nbt.put("Items", nbtList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return inventory.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int filledSlots() {
|
||||||
|
return inventory.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasRoom() {
|
||||||
|
return filledSlots() < size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validSlot(int slot) {
|
||||||
|
return slot >= 0 && slot < filledSlots();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getStack(int slot) {
|
||||||
|
if(validSlot(slot)) {
|
||||||
|
return inventory.get(slot);
|
||||||
|
} else {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sometimes Inventory#removeStack will be called in response to the
|
||||||
|
// count being 0, but not always. Here we check if that is the case
|
||||||
|
// before pulling the newest item.
|
||||||
|
//
|
||||||
|
// This is undesired behavior (as it allows the user to remove items
|
||||||
|
// that are not at the top of the stack), and should be avoided. This
|
||||||
|
// method prevents the side effects from being even worse, however.
|
||||||
|
private ItemStack checkSlot(int slot) {
|
||||||
|
if(!validSlot(slot)) {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack stack = this.getStack(slot);
|
||||||
|
|
||||||
|
if(stack.isEmpty()) {
|
||||||
|
inventory.remove(slot);
|
||||||
|
this.markDirty();
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeStack(int slot, int amount) {
|
||||||
|
ItemStack stack = checkSlot(slot);
|
||||||
|
|
||||||
|
if(stack.isEmpty() || amount <= 0) {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(amount <= stack.getCount()) {
|
||||||
|
return stack.split(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeStack(int slot) {
|
||||||
|
ItemStack stack = checkSlot(slot);
|
||||||
|
|
||||||
|
if(stack.isEmpty()) {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pullStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStack(int slot, ItemStack stack) {
|
||||||
|
if(stack.isEmpty()) {
|
||||||
|
if(validSlot(slot) && this.getStack(slot).isEmpty()) {
|
||||||
|
inventory.remove(slot);
|
||||||
|
this.markDirty();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(slot >= this.filledSlots()) {
|
||||||
|
pushStack(stack);
|
||||||
|
} else {
|
||||||
|
this.inventory.set(slot, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushStack(ItemStack stack) {
|
||||||
|
if(stack.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hasRoom()) {
|
||||||
|
inventory.add(stack);
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack pullStack() {
|
||||||
|
if(this.filledSlots() > 0) {
|
||||||
|
ItemStack o = inventory.removeLast();
|
||||||
|
markDirty();
|
||||||
|
return o;
|
||||||
|
} else {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasItemsLike(ItemStack stack) {
|
||||||
|
for(ItemStack itemStack : inventory) {
|
||||||
|
if(ItemStack.areItemsAndComponentsEqual(itemStack, stack)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
inventory.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ItemStack> getHeldStacks() {
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clean() {
|
||||||
|
for(int i = 0; i < inventory.size(); i++) {
|
||||||
|
if(inventory.get(i).isEmpty()) {
|
||||||
|
inventory.remove(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Implement according to LockedContainer
|
||||||
|
@Override
|
||||||
|
public void markDirty() {
|
||||||
|
clean();
|
||||||
|
super.markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack topStack() {
|
||||||
|
return this.inventory.getLast();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,400 @@
|
|||||||
|
package net.sys42.dakedres.grafting.block.entity;
|
||||||
|
|
||||||
|
import eu.pb4.polymer.resourcepack.api.PolymerModelData;
|
||||||
|
import eu.pb4.sgui.api.ClickType;
|
||||||
|
import eu.pb4.sgui.api.elements.GuiElement;
|
||||||
|
import eu.pb4.sgui.api.gui.SimpleGui;
|
||||||
|
import eu.pb4.sgui.virtual.inventory.VirtualSlot;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.CrafterBlock;
|
||||||
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerInventory;
|
||||||
|
import net.minecraft.inventory.CraftingResultInventory;
|
||||||
|
import net.minecraft.inventory.Inventory;
|
||||||
|
import net.minecraft.inventory.RecipeInputInventory;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.recipe.RecipeMatcher;
|
||||||
|
import net.minecraft.recipe.input.CraftingRecipeInput;
|
||||||
|
import net.minecraft.registry.RegistryWrapper;
|
||||||
|
import net.minecraft.screen.ScreenHandler;
|
||||||
|
import net.minecraft.screen.ScreenHandlerType;
|
||||||
|
import net.minecraft.screen.slot.CraftingResultSlot;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import net.minecraft.screen.slot.SlotActionType;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.text.Style;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Formatting;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.collection.DefaultedList;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.sys42.dakedres.grafting.Grafting;
|
||||||
|
import net.sys42.dakedres.grafting.PlacementUtils;
|
||||||
|
import net.sys42.dakedres.grafting.ui.Icons;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class WorkbenchBlockEntity extends OrderedContainer {
|
||||||
|
public static final String NBT_ORIENTATION_KEY = "CraftingOrientation";
|
||||||
|
public static final int GRID_AREA = 9;
|
||||||
|
public static final int GRID_SIZE = 3;
|
||||||
|
|
||||||
|
private int craftingOrientation = 0;
|
||||||
|
private final OrderedRecipeInventory recipeInventory = new OrderedRecipeInventory();
|
||||||
|
private final ArrayList<Gui> guis = new ArrayList<>();
|
||||||
|
public WorkbenchBlockEntity(BlockPos blockPos, BlockState blockState) {
|
||||||
|
super(Grafting.WORKBENCH_BLOCK_ENTITY_TYPE, blockPos, blockState, GRID_AREA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPlayerUse(PlayerEntity player) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public Text getDisplayName() {
|
||||||
|
// // TODO: Localize
|
||||||
|
// return Text.of("ASDF??");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// public @Nullable ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
|
||||||
|
// return new Gui((ServerPlayerEntity) player)
|
||||||
|
// .openAsScreenHandler(syncId, playerInventory, player);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void openMenu(ServerPlayerEntity player) {
|
||||||
|
Optional<AttachedInventory> attachedInventory = Optional.empty();
|
||||||
|
|
||||||
|
for(BlockPos p : new BlockPos[]{
|
||||||
|
pos.north(1),
|
||||||
|
pos.east(1),
|
||||||
|
pos.south(1),
|
||||||
|
pos.west(1)
|
||||||
|
}) {
|
||||||
|
assert world != null;
|
||||||
|
BlockEntity be = world.getBlockEntity(p);
|
||||||
|
if(
|
||||||
|
!(be instanceof WorkbenchBlockEntity) && // It gets annoying
|
||||||
|
be instanceof Inventory &&
|
||||||
|
AttachedInventory.valid((Inventory) be)
|
||||||
|
) {
|
||||||
|
attachedInventory = Optional.of(new AttachedInventory((Inventory) be));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Gui(player, attachedInventory).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDirty() {
|
||||||
|
for(Gui gui : guis) {
|
||||||
|
gui.updateResult();
|
||||||
|
}
|
||||||
|
super.markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
|
||||||
|
super.writeNbt(nbt, registries);
|
||||||
|
nbt.putByte(NBT_ORIENTATION_KEY, (byte) craftingOrientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
|
||||||
|
super.readNbt(nbt, registries);
|
||||||
|
craftingOrientation = nbt.getByte(NBT_ORIENTATION_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getResult(ServerPlayerEntity player) {
|
||||||
|
World world = player.getWorld();
|
||||||
|
CraftingRecipeInput craftingRecipeInput = recipeInventory.createRecipeInput();
|
||||||
|
return CrafterBlock.getCraftingRecipe(world, craftingRecipeInput)
|
||||||
|
.map(recipe -> recipe.value()
|
||||||
|
.craft(craftingRecipeInput, world.getRegistryManager())
|
||||||
|
)
|
||||||
|
.orElse(ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rotate(int amount) {
|
||||||
|
craftingOrientation = PlacementUtils.clampRotation(craftingOrientation + amount);
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrderedRecipeInventory implements RecipeInputInventory {
|
||||||
|
public int[] getRotatedPlacementIndexes(int slot) {
|
||||||
|
return PlacementUtils.asIndexes(
|
||||||
|
PlacementUtils.rotate(
|
||||||
|
PlacementUtils.getPlacements(WorkbenchBlockEntity.this.getStack(slot), (ServerWorld) world),
|
||||||
|
craftingOrientation
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ItemStack> getHeldStacks() {
|
||||||
|
DefaultedList<ItemStack> stacks = DefaultedList.ofSize(size(), ItemStack.EMPTY);
|
||||||
|
|
||||||
|
for(int i = 0; i < filledSlots(); i++) {
|
||||||
|
int[] placements = getRotatedPlacementIndexes(i);
|
||||||
|
|
||||||
|
for (int slot : placements) {
|
||||||
|
if(stacks.get(slot).isEmpty()) {
|
||||||
|
stacks.set(slot, WorkbenchBlockEntity.this.getStack(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getOrderedSlot(int positionalSlot) {
|
||||||
|
boolean[] filledSlotMatrix = new boolean[size()];
|
||||||
|
Arrays.fill(filledSlotMatrix, false);
|
||||||
|
|
||||||
|
for(int i = 0; i <= filledSlots(); i++) {
|
||||||
|
int[] placements = getRotatedPlacementIndexes(i);
|
||||||
|
|
||||||
|
for (int slot : placements) {
|
||||||
|
if (slot == positionalSlot) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filledSlotMatrix[slot]) {
|
||||||
|
filledSlotMatrix[slot] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return GRID_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return GRID_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return WorkbenchBlockEntity.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return WorkbenchBlockEntity.this.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getStack(int slot) {
|
||||||
|
return WorkbenchBlockEntity.this.getStack(getOrderedSlot(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeStack(int slot, int amount) {
|
||||||
|
return WorkbenchBlockEntity.this.removeStack(getOrderedSlot(slot), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeStack(int slot) {
|
||||||
|
return WorkbenchBlockEntity.this.removeStack(getOrderedSlot(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStack(int slot, ItemStack stack) {
|
||||||
|
int orderedSlot = getOrderedSlot(slot);
|
||||||
|
if(orderedSlot == -1) {
|
||||||
|
pushStack(stack);
|
||||||
|
} else {
|
||||||
|
WorkbenchBlockEntity.this.setStack(orderedSlot, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDirty() {
|
||||||
|
WorkbenchBlockEntity.this.markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPlayerUse(PlayerEntity player) {
|
||||||
|
return WorkbenchBlockEntity.this.canPlayerUse(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void provideRecipeInputs(RecipeMatcher finder) {
|
||||||
|
for(ItemStack itemStack : this.getHeldStacks()) {
|
||||||
|
finder.addUnenchantedInput(itemStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
WorkbenchBlockEntity.this.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Gui extends SimpleGui {
|
||||||
|
private static final int BUTTON_COLUMN = 1;
|
||||||
|
private static final int GRID_START_COLUMN = BUTTON_COLUMN + 1;
|
||||||
|
private static final int RESULT_COLUMN = GRID_START_COLUMN + GRID_SIZE + 2;
|
||||||
|
private static final int BASE_HEIGHT = 3;
|
||||||
|
|
||||||
|
private static final Identifier GUI_FONT_ID = Identifier.of(Grafting.MOD_ID, "workbench_gui");
|
||||||
|
|
||||||
|
private final CraftingResultInventory resultInventory = new CraftingResultInventory();
|
||||||
|
private boolean attachedInventory;
|
||||||
|
public Gui(ServerPlayerEntity player) {
|
||||||
|
this(player, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gui(ServerPlayerEntity player, Optional<AttachedInventory> attachedInventory) {
|
||||||
|
super(
|
||||||
|
attachedInventory.isEmpty() ?
|
||||||
|
ScreenHandlerType.GENERIC_9X3 :
|
||||||
|
attachedInventory.get().extendedScreenHandlerType(BASE_HEIGHT),
|
||||||
|
player,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
guis.add(this);
|
||||||
|
|
||||||
|
setTitle(
|
||||||
|
Text.empty()
|
||||||
|
.append(Text.literal(attachedInventory.isEmpty() ? "-0." : "-1.").setStyle(Style.EMPTY.withFont(GUI_FONT_ID).withColor(Formatting.WHITE)))
|
||||||
|
.append(Text.translatable("container.crafting"))
|
||||||
|
);
|
||||||
|
|
||||||
|
setSlot(BUTTON_COLUMN, new RotateGridButton(Icons.ROTATE_CLOCKWISE,1));
|
||||||
|
setSlot(BUTTON_COLUMN + 9, new RotateGridButton(Icons.ROTATE_COUNTER_CLOCKWISE, -1));
|
||||||
|
setSlotRedirect(
|
||||||
|
RESULT_COLUMN + 9,
|
||||||
|
new CraftingResultGridSlot(player, recipeInventory, resultInventory, 0, 0, 0)
|
||||||
|
);
|
||||||
|
for(int i = 0; i < GRID_AREA; i++) {
|
||||||
|
int row = (i / GRID_SIZE);
|
||||||
|
setSlotRedirect(
|
||||||
|
GRID_START_COLUMN + (i % GRID_SIZE) + (row * 9),
|
||||||
|
new GridSlot(i)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
attachedInventory.ifPresent(inventory -> {
|
||||||
|
inventory.redirectSlots(BASE_HEIGHT, this);
|
||||||
|
});
|
||||||
|
|
||||||
|
updateResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
guis.remove(this);
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateResult() {
|
||||||
|
resultInventory.setStack(0, getResult(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack quickMove(int index) {
|
||||||
|
ItemStack returnStack = ItemStack.EMPTY;
|
||||||
|
Slot slot = this.getSlotRedirectOrPlayer(index);
|
||||||
|
|
||||||
|
if(slot != null && slot.hasStack() && !(slot instanceof VirtualSlot)) {
|
||||||
|
ItemStack operatingStack;
|
||||||
|
|
||||||
|
if(slot instanceof GridSlot) {
|
||||||
|
operatingStack = topStack();
|
||||||
|
} else {
|
||||||
|
operatingStack = slot.getStack();
|
||||||
|
}
|
||||||
|
returnStack = operatingStack.copy();
|
||||||
|
|
||||||
|
if(index < this.getVirtualSize()) {
|
||||||
|
if(!this.insertItem(operatingStack, this.getVirtualSize(), this.getVirtualSize() + 36, true)) {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
} else if (!this.insertItem(operatingStack, 0, this.getVirtualSize(), false)) {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operatingStack.isEmpty()) {
|
||||||
|
slot.setStack(ItemStack.EMPTY);
|
||||||
|
} else {
|
||||||
|
slot.markDirty();
|
||||||
|
}
|
||||||
|
} else if (slot instanceof VirtualSlot) {
|
||||||
|
return slot.getStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Inserting single items changes the shape
|
||||||
|
// - Inserting a full stack will attempt to distribute the stack, or insert if that stack isn't in the workbench
|
||||||
|
public class GridSlot extends Slot {
|
||||||
|
public GridSlot(int index) {
|
||||||
|
super(WorkbenchBlockEntity.this.recipeInventory, index, index, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public ItemStack insertStack(ItemStack stack, int count) {
|
||||||
|
//
|
||||||
|
// if(stack.isEmpty()) {
|
||||||
|
// return stack;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// int allowedCount = Math.min(count, stack.getCount());
|
||||||
|
// if(
|
||||||
|
// (!hasItemsLike(stack) || count == 1) &&
|
||||||
|
// WorkbenchBlockEntity.this.hasRoom()
|
||||||
|
// ) {
|
||||||
|
// pushStack(stack.split(allowedCount));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return stack;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CraftingResultGridSlot extends CraftingResultSlot {
|
||||||
|
public CraftingResultGridSlot(PlayerEntity player, RecipeInputInventory input, Inventory inventory, int index, int x, int y) {
|
||||||
|
super(player, input, inventory, index, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTakeItem(PlayerEntity player, ItemStack stack) {
|
||||||
|
super.onTakeItem(player, stack);
|
||||||
|
WorkbenchBlockEntity.this.markDirty(); // Make sure the OrderedContainer gets cleaned so that everything is in its rightful place
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RotateGridButton extends GuiElement {
|
||||||
|
public RotateGridButton(PolymerModelData model, int bias) {
|
||||||
|
super(
|
||||||
|
Icons.getStack(model),
|
||||||
|
new ItemClickCallback() {
|
||||||
|
@Override
|
||||||
|
public void click(int i, ClickType clickType, SlotActionType slotActionType) {
|
||||||
|
switch(clickType) {
|
||||||
|
case ClickType.MOUSE_LEFT -> rotate(bias);
|
||||||
|
case ClickType.MOUSE_RIGHT -> rotate(bias * -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,67 +0,0 @@
|
|||||||
package net.sys42.dakedres.grafting.blocks;
|
|
||||||
|
|
||||||
import com.mojang.serialization.MapCodec;
|
|
||||||
import eu.pb4.polymer.core.api.block.PolymerBlock;
|
|
||||||
import eu.pb4.polymer.core.api.item.PolymerItem;
|
|
||||||
import net.minecraft.block.Block;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.block.BlockWithEntity;
|
|
||||||
import net.minecraft.block.Blocks;
|
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.item.BlockItem;
|
|
||||||
import net.minecraft.item.Item;
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.item.Items;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.util.ActionResult;
|
|
||||||
import net.minecraft.util.collection.DefaultedList;
|
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.sys42.dakedres.grafting.Grafting;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class Workbench extends BlockWithEntity implements PolymerBlock {
|
|
||||||
private final DefaultedList<ItemStack> stacks = DefaultedList.ofSize(9, ItemStack.EMPTY);
|
|
||||||
|
|
||||||
public Workbench(Settings settings) {
|
|
||||||
super(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MapCodec<? extends BlockWithEntity> getCodec() {
|
|
||||||
return createCodec(Workbench::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
|
||||||
return new WorkbenchBlockEntity(pos, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getPolymerBlockState(BlockState blockState) {
|
|
||||||
return Blocks.CRAFTING_TABLE.getDefaultState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) {
|
|
||||||
if(!player.isSneaking() && world.getBlockEntity(pos) instanceof WorkbenchBlockEntity blockEntity) {
|
|
||||||
blockEntity.openMenu((ServerPlayerEntity) player);
|
|
||||||
return ActionResult.SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onUse(state, world, pos, player, hit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class WorkbenchItem extends BlockItem implements PolymerItem {
|
|
||||||
public WorkbenchItem(Block block, Settings settings) {
|
|
||||||
super(block, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Item getPolymerItem(ItemStack itemStack, @Nullable ServerPlayerEntity serverPlayerEntity) {
|
|
||||||
return Items.CRAFTING_TABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,404 +0,0 @@
|
|||||||
package net.sys42.dakedres.grafting.blocks;
|
|
||||||
|
|
||||||
import eu.pb4.polymer.resourcepack.api.PolymerModelData;
|
|
||||||
import eu.pb4.sgui.api.ClickType;
|
|
||||||
import eu.pb4.sgui.api.elements.GuiElement;
|
|
||||||
import eu.pb4.sgui.api.elements.GuiElementInterface;
|
|
||||||
import eu.pb4.sgui.api.gui.SimpleGui;
|
|
||||||
import eu.pb4.sgui.virtual.inventory.VirtualSlot;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.block.CrafterBlock;
|
|
||||||
import net.minecraft.block.entity.LockableContainerBlockEntity;
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.entity.player.PlayerInventory;
|
|
||||||
import net.minecraft.inventory.CraftingResultInventory;
|
|
||||||
import net.minecraft.inventory.Inventories;
|
|
||||||
import net.minecraft.inventory.Inventory;
|
|
||||||
import net.minecraft.inventory.RecipeInputInventory;
|
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
|
||||||
import net.minecraft.recipe.RecipeMatcher;
|
|
||||||
import net.minecraft.recipe.input.CraftingRecipeInput;
|
|
||||||
import net.minecraft.registry.RegistryWrapper;
|
|
||||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
|
||||||
import net.minecraft.screen.ScreenHandler;
|
|
||||||
import net.minecraft.screen.ScreenHandlerType;
|
|
||||||
import net.minecraft.screen.slot.CraftingResultSlot;
|
|
||||||
import net.minecraft.screen.slot.Slot;
|
|
||||||
import net.minecraft.screen.slot.SlotActionType;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.collection.DefaultedList;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.sys42.dakedres.grafting.Grafting;
|
|
||||||
import net.sys42.dakedres.grafting.PlacementMatrixUtils;
|
|
||||||
import net.sys42.dakedres.grafting.ui.Icons;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class WorkbenchBlockEntity extends LockableContainerBlockEntity implements RecipeInputInventory {
|
|
||||||
|
|
||||||
public static final String NBT_GRID_ROTATION_KEY = "GridRotation";
|
|
||||||
public static final int GRID_SIZE = 9;
|
|
||||||
public static final int GRID_WIDTH = 3;
|
|
||||||
|
|
||||||
private DefaultedList<ItemStack> grid = DefaultedList.ofSize(GRID_SIZE, ItemStack.EMPTY);
|
|
||||||
private int gridRotation = 0;
|
|
||||||
private final ArrayList<Gui> guis = new ArrayList<>();
|
|
||||||
public WorkbenchBlockEntity(BlockPos blockPos, BlockState blockState) {
|
|
||||||
super(Grafting.WORKBENCH_BLOCK_ENTITY_TYPE, blockPos, blockState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openMenu(ServerPlayerEntity playerEntity) {
|
|
||||||
playerEntity.openHandledScreen(new NamedScreenHandlerFactory() {
|
|
||||||
@Override
|
|
||||||
public Text getDisplayName() {
|
|
||||||
return WorkbenchBlockEntity.this.getDisplayName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
|
|
||||||
return WorkbenchBlockEntity.this.createScreenHandler(syncId, playerInventory);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
|
|
||||||
super.readNbt(nbt, registryLookup);
|
|
||||||
this.gridRotation = nbt.getByte(NBT_GRID_ROTATION_KEY);
|
|
||||||
this.grid = DefaultedList.ofSize(this.size(), ItemStack.EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
|
|
||||||
super.writeNbt(nbt, registryLookup);
|
|
||||||
nbt.putByte(NBT_GRID_ROTATION_KEY, (byte) gridRotation);
|
|
||||||
Inventories.writeNbt(nbt, this.grid, registryLookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Text getContainerName() {
|
|
||||||
// TODO: localize
|
|
||||||
return Text.of("Workbench");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getWidth() {
|
|
||||||
return GRID_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeight() {
|
|
||||||
return GRID_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DefaultedList<ItemStack> getHeldStacks() {
|
|
||||||
return this.grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setHeldStacks(DefaultedList<ItemStack> inventory) {
|
|
||||||
this.grid = inventory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ScreenHandler createScreenHandler(int syncId, PlayerInventory playerInventory) {
|
|
||||||
return createScreenHandler(syncId, playerInventory, playerInventory.player);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ScreenHandler createScreenHandler(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
|
|
||||||
return new Gui((ServerPlayerEntity) player).openAsScreenHandler(syncId, playerInventory, player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return GRID_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasEmptySlots() {
|
|
||||||
for(ItemStack itemStack : grid) {
|
|
||||||
if (itemStack.isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int[] getPlacementMatrix() {
|
|
||||||
return PlacementMatrixUtils.rotate(PlacementMatrixUtils.getPlacements(), gridRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void placeStack(ItemStack stack) {
|
|
||||||
int[] placements = getPlacementMatrix();
|
|
||||||
|
|
||||||
for(int i = 0; i < GRID_SIZE; i++) {
|
|
||||||
int p = placements[i];
|
|
||||||
if(getStack(p).isEmpty()) {
|
|
||||||
setStack(p, stack);
|
|
||||||
distributeStack(stack, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void distributeStack(ItemStack stack) {
|
|
||||||
distributeStack(stack, stack.getCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void distributeStack(ItemStack stack, int count) {
|
|
||||||
ArrayList<ItemStack> candidates = new ArrayList<>();
|
|
||||||
int totalCount = count;
|
|
||||||
int maxTotalCount = 0;
|
|
||||||
int[] placements = getPlacementMatrix();
|
|
||||||
|
|
||||||
for(int i = 0; i < GRID_SIZE; i++) {
|
|
||||||
int p = placements[i];
|
|
||||||
ItemStack ps = getStack(p);
|
|
||||||
if(!ps.isEmpty() && ItemStack.areItemsAndComponentsEqual(ps, stack)) {
|
|
||||||
totalCount += ps.getCount();
|
|
||||||
maxTotalCount += ps.getMaxCount();
|
|
||||||
ps.setCount(0);
|
|
||||||
candidates.add(ps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int remainder = 0;
|
|
||||||
if(totalCount > maxTotalCount) {
|
|
||||||
remainder = totalCount - maxTotalCount;
|
|
||||||
totalCount = maxTotalCount;
|
|
||||||
}
|
|
||||||
int i = 0;
|
|
||||||
while(totalCount > 0) {
|
|
||||||
candidates.get(i % candidates.size()).increment(1);
|
|
||||||
totalCount--;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.decrement(count - remainder);
|
|
||||||
markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int lastPriorityIndexAlike(ItemStack stack) {
|
|
||||||
int[] placements = getPlacementMatrix();
|
|
||||||
|
|
||||||
for(int i = placements.length - 1; i >= 0; i--) {
|
|
||||||
int p = placements[i];
|
|
||||||
ItemStack ps = getStack(p);
|
|
||||||
if(ItemStack.areItemsAndComponentsEqual(ps, stack)) {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemStack removeStack(ItemStack stack, int amount) {
|
|
||||||
int index = lastPriorityIndexAlike(stack);
|
|
||||||
|
|
||||||
if(index >= 0) {
|
|
||||||
ItemStack o = removeStack(index, amount);
|
|
||||||
distributeStack(o, 0);
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ItemStack.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasItemsLike(ItemStack stack) {
|
|
||||||
for(ItemStack itemStack : grid) {
|
|
||||||
if(ItemStack.areItemsAndComponentsEqual(itemStack, stack)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on CrafterScreenHandler#updateResult
|
|
||||||
public ItemStack getResult(ServerPlayerEntity player) {
|
|
||||||
World world = player.getWorld();
|
|
||||||
CraftingRecipeInput craftingRecipeInput = createRecipeInput();
|
|
||||||
return CrafterBlock.getCraftingRecipe(world, craftingRecipeInput)
|
|
||||||
.map(recipe -> recipe.value()
|
|
||||||
.craft(craftingRecipeInput, world.getRegistryManager())
|
|
||||||
)
|
|
||||||
.orElse(ItemStack.EMPTY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void markDirty() {
|
|
||||||
for(Gui gui : guis) {
|
|
||||||
gui.updateResult();
|
|
||||||
}
|
|
||||||
super.markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void provideRecipeInputs(RecipeMatcher finder) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Reimplement. Need to prevent any possible desync between the orientation as manipulated here, and the one in getPlacements
|
|
||||||
public void rotate(int direction) {
|
|
||||||
gridRotation = PlacementMatrixUtils.clampRotation(gridRotation + direction);
|
|
||||||
int[] rotationMatrix = PlacementMatrixUtils.ROTATION_MATRICES[PlacementMatrixUtils.clampRotation(direction)];
|
|
||||||
DefaultedList<ItemStack> newGrid = DefaultedList.ofSize(grid.size(), ItemStack.EMPTY);
|
|
||||||
|
|
||||||
for(int i = 0; i < grid.size(); i++) {
|
|
||||||
newGrid.set(rotationMatrix[i], grid.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeldStacks(newGrid);
|
|
||||||
markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Gui extends SimpleGui {
|
|
||||||
private static final int BUTTONS_LEFT = 1;
|
|
||||||
private static final int GRID_LEFT = BUTTONS_LEFT + 1;
|
|
||||||
private static final int RESULT_LEFT = GRID_LEFT + GRID_WIDTH + 2;
|
|
||||||
|
|
||||||
private final CraftingResultInventory resultInventory = new CraftingResultInventory();
|
|
||||||
public Gui(ServerPlayerEntity player) {
|
|
||||||
super(ScreenHandlerType.GENERIC_9X3, player, false);
|
|
||||||
guis.add(this);
|
|
||||||
|
|
||||||
this.setTitle(WorkbenchBlockEntity.this.getDisplayName());
|
|
||||||
setSlot(BUTTONS_LEFT, new RotateGridButton(Icons.ROTATE_CLOCKWISE,1));
|
|
||||||
setSlot(BUTTONS_LEFT + 9, new RotateGridButton(Icons.ROTATE_COUNTER_CLOCKWISE, -1));
|
|
||||||
setSlotRedirect(
|
|
||||||
RESULT_LEFT + 9,
|
|
||||||
new CraftingResultSlot(player, WorkbenchBlockEntity.this, resultInventory, 0, 0, 0)
|
|
||||||
);
|
|
||||||
for(int i = 0; i < GRID_SIZE; i++) {
|
|
||||||
int row = (i / GRID_WIDTH);
|
|
||||||
setSlotRedirect(
|
|
||||||
GRID_LEFT + (i % GRID_WIDTH) + (row * 9),
|
|
||||||
new GridSlot(i)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
guis.remove(this);
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateResult() {
|
|
||||||
resultInventory.setStack(0, getResult(player));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
According to ScreenHandler#internalOnSlotClick, quickMove is continually called
|
|
||||||
while it returns a non-empty ItemStack that matches the original slot
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public ItemStack quickMove(int index) {
|
|
||||||
ItemStack returnStack = ItemStack.EMPTY;
|
|
||||||
Slot slot = this.getSlotRedirectOrPlayer(index);
|
|
||||||
|
|
||||||
if(slot != null && slot.hasStack() && !(slot instanceof VirtualSlot)) {
|
|
||||||
ItemStack operatingStack = slot.getStack();
|
|
||||||
returnStack = operatingStack.copy();
|
|
||||||
|
|
||||||
if(index < this.getVirtualSize()) {
|
|
||||||
int i = -1;
|
|
||||||
|
|
||||||
if(slot instanceof GridSlot) {
|
|
||||||
i = lastPriorityIndexAlike(operatingStack);
|
|
||||||
operatingStack = i == -1 ? ItemStack.EMPTY : removeStack(i);
|
|
||||||
|
|
||||||
if (!this.insertItem(operatingStack, this.getVirtualSize(), this.getVirtualSize() + 36, true)) {
|
|
||||||
return ItemStack.EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.insertItem(operatingStack, this.getVirtualSize(), this.getVirtualSize() + 36, true)) {
|
|
||||||
return ItemStack.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(slot.getIndex() == i) {
|
|
||||||
return ItemStack.EMPTY;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(hasItemsLike(operatingStack)) {
|
|
||||||
distributeStack(operatingStack);
|
|
||||||
return ItemStack.EMPTY;
|
|
||||||
} else if(hasEmptySlots()) {
|
|
||||||
placeStack(operatingStack);
|
|
||||||
slot.setStack(ItemStack.EMPTY);
|
|
||||||
} else {
|
|
||||||
return ItemStack.EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operatingStack.isEmpty()) {
|
|
||||||
slot.setStack(ItemStack.EMPTY);
|
|
||||||
} else {
|
|
||||||
slot.markDirty();
|
|
||||||
}
|
|
||||||
} else if (slot instanceof VirtualSlot) {
|
|
||||||
return slot.getStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Inserting single items changes the shape
|
|
||||||
// - Inserting a full stack will attempt to distribute the stack, or insert if that stack isn't in the workbench
|
|
||||||
public class GridSlot extends Slot {
|
|
||||||
public GridSlot(int index) {
|
|
||||||
super(WorkbenchBlockEntity.this, index, index, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ItemStack insertStack(ItemStack stack, int count) {
|
|
||||||
|
|
||||||
if(stack.isEmpty()) {
|
|
||||||
return stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
int allowedCount = Math.min(count, stack.getCount());
|
|
||||||
if(
|
|
||||||
(!hasItemsLike(stack) || count == 1) &&
|
|
||||||
WorkbenchBlockEntity.this.hasEmptySlots()
|
|
||||||
) {
|
|
||||||
placeStack(stack.split(allowedCount));
|
|
||||||
} else {
|
|
||||||
distributeStack(stack, allowedCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ItemStack takeStack(int amount) {
|
|
||||||
return removeStack(this.getStack(), amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RotateGridButton extends GuiElement {
|
|
||||||
public RotateGridButton(PolymerModelData model, int bias) {
|
|
||||||
super(
|
|
||||||
Icons.getStack(model),
|
|
||||||
new GuiElementInterface.ItemClickCallback() {
|
|
||||||
@Override
|
|
||||||
public void click(int i, ClickType clickType, SlotActionType slotActionType) {
|
|
||||||
switch(clickType) {
|
|
||||||
case ClickType.MOUSE_LEFT -> rotate(bias);
|
|
||||||
case ClickType.MOUSE_RIGHT -> rotate(bias * -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,66 @@
|
|||||||
|
package net.sys42.dakedres.grafting.data;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.DataResult;
|
||||||
|
import com.mojang.serialization.DynamicOps;
|
||||||
|
import com.mojang.serialization.codecs.PrimitiveCodec;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.sys42.dakedres.grafting.Grafting;
|
||||||
|
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public class PlacementOrder {
|
||||||
|
public static final PlacementOrder DEFAULT_FALLBACK = new PlacementOrder(new int[]{
|
||||||
|
0,1,2,
|
||||||
|
3,4,5,
|
||||||
|
6,7,8
|
||||||
|
});
|
||||||
|
public static final Identifier DEFAULT_ID = Identifier.of(Grafting.MOD_ID, "default");
|
||||||
|
|
||||||
|
int[] orderMatrix;
|
||||||
|
public PlacementOrder(int[] orderMatrix) {
|
||||||
|
this.orderMatrix = orderMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getOrderMatrix() {
|
||||||
|
return orderMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Codec<PlacementOrder> CODEC = new PrimitiveCodec<>() {
|
||||||
|
@Override
|
||||||
|
public <T> DataResult<PlacementOrder> read(DynamicOps<T> ops, T input) {
|
||||||
|
return ops.getIntStream(input).flatMap(intStream -> {
|
||||||
|
var om = intStream.toArray();
|
||||||
|
try {
|
||||||
|
PlacementOrder.validate(om);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return DataResult.error(e::getMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DataResult.success(new PlacementOrder(om));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T write(DynamicOps<T> ops, PlacementOrder value) {
|
||||||
|
return ops.createIntList(IntStream.of(value.getOrderMatrix()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PlacementOrder";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void validate(int[] orderMatrix) throws Exception {
|
||||||
|
if(orderMatrix.length != 9) {
|
||||||
|
throw new Exception("Not a complete matrix. Expected an array of at least 9 integers between 0 and 8.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < 9; i++) {
|
||||||
|
if(orderMatrix[i] >= 9 || orderMatrix[i] < 0) {
|
||||||
|
throw new Exception(String.format("At index {}, expected integer between 8 and 0", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package net.sys42.dakedres.grafting.data;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
|
import net.minecraft.registry.Registry;
|
||||||
|
import net.minecraft.resource.Resource;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.sys42.dakedres.grafting.Grafting;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PlacementOrderReloadListener {
|
||||||
|
static public void init() {
|
||||||
|
ServerLifecycleEvents.START_DATA_PACK_RELOAD.register((server, resourceManager) -> {
|
||||||
|
// Thank you tomalbrc! https://github.com/tomalbrc/filament/blob/main/src/main/java/de/tomalbrc/filament/util/FilamentSynchronousResourceReloadListener.java#L70
|
||||||
|
|
||||||
|
var resources = resourceManager.findResources(
|
||||||
|
"grafting/placement_order",
|
||||||
|
path -> path.getPath().endsWith(".json")
|
||||||
|
);
|
||||||
|
|
||||||
|
for(Map.Entry<Identifier, Resource> entry : resources.entrySet()) {
|
||||||
|
load(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grafting.LOGGER.info("Loaded {} placement orders.", Grafting.PLACEMENT_ORDER_REGISTRY.getEntrySet().size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static private void load(Identifier id, Resource resource) {
|
||||||
|
JsonObject json;
|
||||||
|
|
||||||
|
try(BufferedReader reader = resource.getReader()) {
|
||||||
|
load(id, reader);
|
||||||
|
} catch(IOException | IllegalStateException e) {
|
||||||
|
error(id, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private void load(Identifier id, BufferedReader reader) {
|
||||||
|
Gson gson = new GsonBuilder().create();
|
||||||
|
JsonElement rootElement = JsonParser.parseReader(reader);
|
||||||
|
|
||||||
|
try {
|
||||||
|
int[] orderMatrix = gson.fromJson(rootElement, int[].class);
|
||||||
|
|
||||||
|
PlacementOrder.validate(orderMatrix);
|
||||||
|
} catch(Exception e) {
|
||||||
|
error(id, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grafting.PLACEMENT_ORDER_REGISTRY
|
||||||
|
// PlacementOrder.Data = gson.fromJson(rootElement, PlacementOrder.Data.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
static private void error(Identifier id, Exception e) {
|
||||||
|
Grafting.LOGGER.error("Could not load placement order \"{}\": {}", id, e);
|
||||||
|
}
|
||||||
|
}
|
@ -1,36 +1,56 @@
|
|||||||
package net.sys42.dakedres.grafting.item;
|
package net.sys42.dakedres.grafting.item;
|
||||||
|
|
||||||
import eu.pb4.polymer.core.api.item.PolymerItem;
|
import eu.pb4.polymer.core.api.item.PolymerItem;
|
||||||
|
import eu.pb4.polymer.resourcepack.api.PolymerModelData;
|
||||||
|
import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.item.*;
|
import net.minecraft.item.*;
|
||||||
|
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.util.ActionResult;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.sys42.dakedres.grafting.Grafting;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class WorkbenchItem extends BlockItem implements PolymerItem {
|
public class WorkbenchItem extends BlockItem implements PolymerItem {
|
||||||
public WorkbenchItem(Block block, Settings settings, String modelId) {
|
public final PolymerModelData model = PolymerResourcePackUtils.requestModel(
|
||||||
|
Items.BREEZE_ROD,
|
||||||
|
Identifier.of(Grafting.MOD_ID, "item/workbench")
|
||||||
|
);
|
||||||
|
public WorkbenchItem(Block block, Settings settings) {
|
||||||
super(block, settings);
|
super(block, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Item getPolymerItem(ItemStack itemStack, @Nullable ServerPlayerEntity serverPlayerEntity) {
|
public Item getPolymerItem(ItemStack itemStack, @Nullable ServerPlayerEntity serverPlayerEntity) {
|
||||||
return Items.CRAFTING_TABLE;
|
return model.item();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPolymerCustomModelData(ItemStack itemStack, @Nullable ServerPlayerEntity player) {
|
||||||
|
return model.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thank you Mykhailo!
|
// Thank you Mykhailo!
|
||||||
// copied from:
|
// copied from:
|
||||||
// https://github.com/MykhailoOpryshok/Borukva-Food/blob/master/src/main/java/com/opryshok/block/TexturedPolyBlockItem.java#L18
|
// https://github.com/MykhailoOpryshok/Borukva-Food/blob/master/src/main/java/com/opryshok/block/TexturedPolyBlockItem.java#L18
|
||||||
// @Override
|
@Override
|
||||||
// public ActionResult useOnBlock(ItemUsageContext context) {
|
public ActionResult useOnBlock(ItemUsageContext context) {
|
||||||
// var x = super.useOnBlock(context);
|
var x = super.useOnBlock(context);
|
||||||
// if (x == ActionResult.CONSUME) {
|
if (x == ActionResult.CONSUME) {
|
||||||
// if (context.getPlayer() instanceof ServerPlayerEntity player) {
|
if (context.getPlayer() instanceof ServerPlayerEntity player) {
|
||||||
// var pos = Vec3d.ofCenter(context.getBlockPos().offset(context.getSide()));
|
var pos = Vec3d.ofCenter(context.getBlockPos().offset(context.getSide()));
|
||||||
// var blockSoundGroup = this.getBlock().getDefaultState().getSoundGroup();
|
var blockSoundGroup = this.getBlock().getDefaultState().getSoundGroup();
|
||||||
// player.networkHandler.sendPacket(new PlaySoundS2CPacket(Registries.SOUND_EVENT.getEntry(this.getPlaceSound(this.getBlock().getDefaultState())), SoundCategory.BLOCKS, pos.x, pos.y, pos.z, (blockSoundGroup.getVolume() + 1.0F) / 2.0F, blockSoundGroup.getPitch() * 0.8F, context.getPlayer().getRandom().nextLong()));
|
player.networkHandler.sendPacket(
|
||||||
// }
|
new PlaySoundS2CPacket(Registries.SOUND_EVENT.getEntry(this.getPlaceSound(this.getBlock().getDefaultState())), SoundCategory.BLOCKS, pos.x, pos.y, pos.z, (blockSoundGroup.getVolume() + 1.0F) / 2.0F, blockSoundGroup.getPitch() * 0.8F, context.getPlayer().getRandom().nextLong())
|
||||||
// return ActionResult.SUCCESS;
|
);
|
||||||
// }
|
}
|
||||||
// return x;
|
return ActionResult.SUCCESS;
|
||||||
// }
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,17 +14,24 @@ import net.sys42.dakedres.grafting.Grafting;
|
|||||||
public class Icons {
|
public class Icons {
|
||||||
public static final Item BASE_ITEM = Items.BREEZE_ROD;
|
public static final Item BASE_ITEM = Items.BREEZE_ROD;
|
||||||
|
|
||||||
public static final PolymerModelData ROTATE_CLOCKWISE = uiElementModel("icons/rotate_clockwise.png");
|
public static final PolymerModelData ROTATE_CLOCKWISE = uiElementModel("rotate_clockwise");
|
||||||
public static final PolymerModelData ROTATE_COUNTER_CLOCKWISE = uiElementModel("icons/rotate_counter_clockwise.png");
|
public static final PolymerModelData ROTATE_COUNTER_CLOCKWISE = uiElementModel("rotate_counter_clockwise");
|
||||||
|
|
||||||
private static PolymerModelData uiElementModel(String path) {
|
private static PolymerModelData uiElementModel(String name) {
|
||||||
return PolymerResourcePackUtils.requestModel(BASE_ITEM, Identifier.of(Grafting.MOD_ID + path));
|
return PolymerResourcePackUtils.requestModel(
|
||||||
|
BASE_ITEM,
|
||||||
|
Identifier.of(Grafting.MOD_ID, "icon/" + name)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemStack getStack(PolymerModelData model) {
|
public static ItemStack getStack(PolymerModelData model) {
|
||||||
ItemStack stack = Icons.ROTATE_CLOCKWISE.asStack();
|
ItemStack stack = model.asStack();
|
||||||
stack.set(DataComponentTypes.HIDE_TOOLTIP, Unit.INSTANCE);
|
stack.set(DataComponentTypes.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||||
|
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
29
src/main/resources/assets/grafting/font/workbench_gui.json
Normal file
29
src/main/resources/assets/grafting/font/workbench_gui.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"providers": [
|
||||||
|
{
|
||||||
|
"type": "space",
|
||||||
|
"advances": {
|
||||||
|
".": -134,
|
||||||
|
"-": -8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bitmap",
|
||||||
|
"file": "grafting:gui/workbench_gui.png",
|
||||||
|
"ascent": 34,
|
||||||
|
"height": 256,
|
||||||
|
"chars": [
|
||||||
|
"0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bitmap",
|
||||||
|
"file": "grafting:gui/workbench_gui_with_inventory.png",
|
||||||
|
"ascent": 34,
|
||||||
|
"height": 256,
|
||||||
|
"chars": [
|
||||||
|
"1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
src/main/resources/assets/grafting/lang/en_us.json
Normal file
3
src/main/resources/assets/grafting/lang/en_us.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"block.grafting.workbench": "Workbench"
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "grafting:icon/shifted",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "grafting:item/icon/rotate_clockwise"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "grafting:icon/shifted",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "grafting:item/icon/rotate_counter_clockwise"
|
||||||
|
}
|
||||||
|
}
|
10
src/main/resources/assets/grafting/models/icon/shifted.json
Normal file
10
src/main/resources/assets/grafting/models/icon/shifted.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"display": {
|
||||||
|
"gui": {
|
||||||
|
"rotation": [ 0, 0, 0 ],
|
||||||
|
"translation": [ -1, 0, 0 ],
|
||||||
|
"scale": [ 1, 1, 1 ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "grafting:block/workbench"
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 677 B |
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 670 B |
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
0,1,2,
|
||||||
|
3,4,5,
|
||||||
|
6,7,8
|
||||||
|
]
|
Loading…
x
Reference in New Issue
Block a user