diff --git a/build.gradle b/build.gradle index ed5499f..7281c47 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,8 @@ repositories { // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. // See https://docs.gradle.org/current/userguide/declaring_repositories.html // for more information about repositories. + + maven { url 'https://maven.nucleoid.xyz' } } loom { @@ -38,7 +40,11 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - + + modImplementation include("eu.pb4:polymer-core:${project.polymer_version}") + modImplementation include("eu.pb4:polymer-blocks:${project.polymer_version}") + modImplementation include("eu.pb4:polymer-resource-pack:${project.polymer_version}") + modImplementation include("eu.pb4:sgui:${project.sgui_version}") } processResources { diff --git a/gradle.properties b/gradle.properties index 6b16f86..7fa6ce6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,6 @@ maven_group=net.sys42.dakedres.grafting archives_base_name=grafting # Dependencies -fabric_version=0.115.4+1.21.1 \ No newline at end of file +fabric_version=0.115.4+1.21.1 +polymer_version=0.9.18+1.21.1 +sgui_version=1.6.1+1.21.1 \ No newline at end of file diff --git a/src/client/java/net/sys42/dakedres/grafting/GraftingClient.java b/src/client/java/net/sys42/dakedres/grafting/GraftingClient.java deleted file mode 100644 index d06816c..0000000 --- a/src/client/java/net/sys42/dakedres/grafting/GraftingClient.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.sys42.dakedres.grafting; - -import net.fabricmc.api.ClientModInitializer; - -public class GraftingClient implements ClientModInitializer { - @Override - public void onInitializeClient() { - // This entrypoint is suitable for setting up client-specific logic, such as rendering. - } -} \ No newline at end of file diff --git a/src/client/java/net/sys42/dakedres/grafting/mixin/client/ExampleClientMixin.java b/src/client/java/net/sys42/dakedres/grafting/mixin/client/ExampleClientMixin.java deleted file mode 100644 index 0e59c3a..0000000 --- a/src/client/java/net/sys42/dakedres/grafting/mixin/client/ExampleClientMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.sys42.dakedres.grafting.mixin.client; - -import net.minecraft.client.MinecraftClient; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(MinecraftClient.class) -public class ExampleClientMixin { - @Inject(at = @At("HEAD"), method = "run") - private void init(CallbackInfo info) { - // This code is injected into the start of MinecraftClient.run()V - } -} \ No newline at end of file diff --git a/src/client/resources/grafting.client.mixins.json b/src/client/resources/grafting.client.mixins.json deleted file mode 100644 index 97a0150..0000000 --- a/src/client/resources/grafting.client.mixins.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "required": true, - "package": "net.sys42.dakedres.grafting.mixin.client", - "compatibilityLevel": "JAVA_21", - "client": [ - "ExampleClientMixin" - ], - "injectors": { - "defaultRequire": 1 - } -} \ No newline at end of file diff --git a/src/main/java/net/sys42/dakedres/grafting/Grafting.java b/src/main/java/net/sys42/dakedres/grafting/Grafting.java index d19b699..0407226 100644 --- a/src/main/java/net/sys42/dakedres/grafting/Grafting.java +++ b/src/main/java/net/sys42/dakedres/grafting/Grafting.java @@ -1,7 +1,24 @@ package net.sys42.dakedres.grafting; +import eu.pb4.polymer.core.api.block.PolymerBlockUtils; +import eu.pb4.polymer.resourcepack.api.PolymerModelData; +import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.sound.BlockSoundGroup; +import net.minecraft.util.Identifier; +import net.sys42.dakedres.grafting.blocks.Workbench; +import net.sys42.dakedres.grafting.blocks.WorkbenchBlockEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +30,21 @@ public class Grafting implements ModInitializer { // That way, it's clear which mod wrote info, warnings, and errors. 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( + Registries.BLOCK, WORKBENCH_ID, + new Workbench(AbstractBlock.Settings.create().sounds(BlockSoundGroup.WOOD)) + ); + public static final BlockEntityType WORKBENCH_BLOCK_ENTITY_TYPE = Registry.register( + Registries.BLOCK_ENTITY_TYPE, + WORKBENCH_ID, + FabricBlockEntityTypeBuilder.create(WorkbenchBlockEntity::new, WORKBENCH).build() + ); + public static final BlockItem WORKBENCH_ITEM = Registry.register( + Registries.ITEM, WORKBENCH_ID, + new Workbench.WorkbenchItem(WORKBENCH, new Item.Settings()) + ); + @Override public void onInitialize() { // This code runs as soon as Minecraft is in a mod-load-ready state. @@ -20,5 +52,14 @@ public class Grafting implements ModInitializer { // Proceed with mild caution. LOGGER.info("Hello Fabric world!"); + + PolymerBlockUtils.registerBlockEntity(WORKBENCH_BLOCK_ENTITY_TYPE); + + if (PolymerResourcePackUtils.addModAssets(MOD_ID)) { + LOGGER.info("Successfully added mod assets for " + MOD_ID); + } else { + LOGGER.error("Failed to add mod assets for " + MOD_ID); + } + PolymerResourcePackUtils.markAsRequired(); } } \ No newline at end of file diff --git a/src/main/java/net/sys42/dakedres/grafting/PlacementMatrixUtils.java b/src/main/java/net/sys42/dakedres/grafting/PlacementMatrixUtils.java new file mode 100644 index 0000000..55c1a7a --- /dev/null +++ b/src/main/java/net/sys42/dakedres/grafting/PlacementMatrixUtils.java @@ -0,0 +1,60 @@ +package net.sys42.dakedres.grafting; + +public class PlacementMatrixUtils { + public static final int[][] ROTATION_MATRICES = { + { 0,1,2,3,4,5,6,7,8 }, + { 1,2,5,0,4,8,3,6,7 }, + { 2,5,8,1,4,7,0,3,6 }, + { 5,8,7,2,4,6,1,0,3 }, + { 8,7,6,5,4,3,2,1,0 }, + { 7,6,3,8,4,0,5,2,1 }, + { 6,3,0,7,4,1,8,5,2 }, + { 3,0,1,6,4,2,7,8,5 } + }; + + +// public static DefaultedList rotate(DefaultedList grid, int direction) { +// DefaultedList o = DefaultedList.ofSize(grid.size(), ItemStack.EMPTY); +// int[] rotationMatrix = ROTATION_MATRICES[(direction < 0 ? 8 - direction : direction) % 8]; +// +// for(int i = 0; i < grid.size(); i++) { +// o.set(rotationMatrix[i], grid.get(i)); +// } +// +// return o; +// } + + public static int clampRotation(int rotation) { + return (rotation < 0 ? 8 + rotation : rotation) % 8; + } + + public static int[] getRotationMatrix(int rotation) { + return ROTATION_MATRICES[clampRotation(rotation)]; + } + + public static int[] rotate(int[] matrix, int rotation) { + int[] o = new int[matrix.length]; + int[] rotationMatrix = ROTATION_MATRICES[rotation]; + + for(int i = 0; i < matrix.length; i++) { + o[rotationMatrix[i]] = matrix[i]; + } + + return o; + } + + public static int[] getPlacements() { + int[] placements = { + 2,0,8, + 1,3,4, + 7,6,5 + }; + int[] out = new int[9]; + + for(int i = 0; i < 9; i++) { + out[placements[i]] = i; + } + + return out; + } +} diff --git a/src/main/java/net/sys42/dakedres/grafting/blocks/Workbench.java b/src/main/java/net/sys42/dakedres/grafting/blocks/Workbench.java new file mode 100644 index 0000000..fd5487e --- /dev/null +++ b/src/main/java/net/sys42/dakedres/grafting/blocks/Workbench.java @@ -0,0 +1,67 @@ +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 stacks = DefaultedList.ofSize(9, ItemStack.EMPTY); + + public Workbench(Settings settings) { + super(settings); + } + + @Override + protected MapCodec 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; + } + } +} diff --git a/src/main/java/net/sys42/dakedres/grafting/blocks/WorkbenchBlockEntity.java b/src/main/java/net/sys42/dakedres/grafting/blocks/WorkbenchBlockEntity.java new file mode 100644 index 0000000..c4281b7 --- /dev/null +++ b/src/main/java/net/sys42/dakedres/grafting/blocks/WorkbenchBlockEntity.java @@ -0,0 +1,404 @@ +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 grid = DefaultedList.ofSize(GRID_SIZE, ItemStack.EMPTY); + private int gridRotation = 0; + private final ArrayList 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 getHeldStacks() { + return this.grid; + } + + @Override + protected void setHeldStacks(DefaultedList 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 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 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); + } + } + } + ); + } + } + } +} diff --git a/src/main/java/net/sys42/dakedres/grafting/item/WorkbenchItem.java b/src/main/java/net/sys42/dakedres/grafting/item/WorkbenchItem.java new file mode 100644 index 0000000..aac7b92 --- /dev/null +++ b/src/main/java/net/sys42/dakedres/grafting/item/WorkbenchItem.java @@ -0,0 +1,36 @@ +package net.sys42.dakedres.grafting.item; + +import eu.pb4.polymer.core.api.item.PolymerItem; +import net.minecraft.block.Block; +import net.minecraft.item.*; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Nullable; + +public class WorkbenchItem extends BlockItem implements PolymerItem { + public WorkbenchItem(Block block, Settings settings, String modelId) { + super(block, settings); + } + + @Override + public Item getPolymerItem(ItemStack itemStack, @Nullable ServerPlayerEntity serverPlayerEntity) { + return Items.CRAFTING_TABLE; + } + + // Thank you Mykhailo! + // copied from: + // https://github.com/MykhailoOpryshok/Borukva-Food/blob/master/src/main/java/com/opryshok/block/TexturedPolyBlockItem.java#L18 +// @Override +// public ActionResult useOnBlock(ItemUsageContext context) { +// var x = super.useOnBlock(context); +// if (x == ActionResult.CONSUME) { +// if (context.getPlayer() instanceof ServerPlayerEntity player) { +// var pos = Vec3d.ofCenter(context.getBlockPos().offset(context.getSide())); +// 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())); +// } +// return ActionResult.SUCCESS; +// } +// return x; +// } + +} diff --git a/src/main/java/net/sys42/dakedres/grafting/ui/Icons.java b/src/main/java/net/sys42/dakedres/grafting/ui/Icons.java new file mode 100644 index 0000000..0936021 --- /dev/null +++ b/src/main/java/net/sys42/dakedres/grafting/ui/Icons.java @@ -0,0 +1,30 @@ +package net.sys42.dakedres.grafting.ui; + +import eu.pb4.polymer.resourcepack.api.PolymerModelData; +import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils; +import net.minecraft.component.ComponentType; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.Identifier; +import net.minecraft.util.Unit; +import net.sys42.dakedres.grafting.Grafting; + +public class Icons { + 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_COUNTER_CLOCKWISE = uiElementModel("icons/rotate_counter_clockwise.png"); + + private static PolymerModelData uiElementModel(String path) { + return PolymerResourcePackUtils.requestModel(BASE_ITEM, Identifier.of(Grafting.MOD_ID + path)); + } + + public static ItemStack getStack(PolymerModelData model) { + ItemStack stack = Icons.ROTATE_CLOCKWISE.asStack(); + stack.set(DataComponentTypes.HIDE_TOOLTIP, Unit.INSTANCE); + + return stack; + } +} diff --git a/src/main/resources/assets/grafting/models/block/workbench.json b/src/main/resources/assets/grafting/models/block/workbench.json new file mode 100644 index 0000000..978fe17 --- /dev/null +++ b/src/main/resources/assets/grafting/models/block/workbench.json @@ -0,0 +1,121 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "4": "grafting:block/workbench_top", + "5": "grafting:block/workbench_front", + "6": "grafting:block/workbench_bottom", + "7": "grafting:block/workbench_side", + "particle": "grafting:block/workbench_top" + }, + "elements": [ + { + "from": [0, 0, 12], + "to": [4, 12, 16], + "faces": { + "north": {"uv": [12, 5, 16, 16], "texture": "#5"}, + "east": {"uv": [0, 5, 4, 16], "texture": "#7"}, + "south": {"uv": [0, 5, 4, 16], "texture": "#5"}, + "west": {"uv": [12, 5, 16, 16], "texture": "#7"}, + "up": {"uv": [0, 0, 4, 4], "texture": "#missing"}, + "down": {"uv": [12, 12, 16, 16], "rotation": 180, "texture": "#6"} + } + }, + { + "from": [0, 0, 0], + "to": [4, 12, 4], + "faces": { + "north": {"uv": [12, 5, 16, 16], "texture": "#5"}, + "east": {"uv": [12, 5, 16, 16], "texture": "#7"}, + "south": {"uv": [0, 5, 4, 16], "texture": "#5"}, + "west": {"uv": [0, 5, 4, 16], "texture": "#7"}, + "up": {"uv": [0, 0, 4, 4], "rotation": 90, "texture": "#missing"}, + "down": {"uv": [12, 0, 16, 4], "rotation": 180, "texture": "#6"} + } + }, + { + "from": [12, 0, 12], + "to": [16, 12, 16], + "faces": { + "north": {"uv": [0, 5, 4, 16], "texture": "#5"}, + "east": {"uv": [0, 5, 4, 16], "texture": "#7"}, + "south": {"uv": [12, 5, 16, 16], "texture": "#5"}, + "west": {"uv": [12, 5, 16, 16], "texture": "#7"}, + "up": {"uv": [0, 0, 4, 4], "rotation": 270, "texture": "#missing"}, + "down": {"uv": [0, 12, 4, 16], "rotation": 180, "texture": "#6"} + } + }, + { + "from": [12, 0, 0], + "to": [16, 12, 4], + "faces": { + "north": {"uv": [0, 5, 4, 16], "texture": "#5"}, + "east": {"uv": [12, 5, 16, 16], "texture": "#7"}, + "south": {"uv": [12, 5, 16, 16], "texture": "#5"}, + "west": {"uv": [0, 5, 4, 16], "texture": "#7"}, + "up": {"uv": [0, 0, 4, 4], "rotation": 180, "texture": "#missing"}, + "down": {"uv": [0, 0, 4, 4], "rotation": 180, "texture": "#6"} + } + }, + { + "from": [0, 12, 0], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 0, 16, 4], "texture": "#5"}, + "east": {"uv": [0, 0, 16, 4], "texture": "#7"}, + "south": {"uv": [0, 0, 16, 4], "texture": "#5"}, + "west": {"uv": [0, 0, 16, 4], "texture": "#7"}, + "up": {"uv": [0, 0, 16, 16], "texture": "#4"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#6"} + } + }, + { + "from": [4, 0, 16], + "to": [12, 12, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [7, 4, 6]}, + "faces": { + "north": {"uv": [4, 4, 12, 16], "texture": "#5"}, + "south": {"uv": [4, 4, 12, 16], "texture": "#5"} + } + }, + { + "from": [4, 0, 0], + "to": [12, 12, 0], + "rotation": {"angle": 0, "axis": "y", "origin": [7, 4, -10]}, + "faces": { + "north": {"uv": [4, 4, 12, 16], "texture": "#5"}, + "south": {"uv": [4, 4, 12, 16], "texture": "#5"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "thirdperson_lefthand": { + "rotation": [75, 45, 0], + "translation": [0, 2.5, 0], + "scale": [0.375, 0.375, 0.375] + }, + "firstperson_righthand": { + "rotation": [0, 45, 0], + "scale": [0.4, 0.4, 0.4] + }, + "firstperson_lefthand": { + "rotation": [0, 225, 0], + "scale": [0.4, 0.4, 0.4] + }, + "ground": { + "translation": [0, 3, 0], + "scale": [0.25, 0.25, 0.25] + }, + "gui": { + "rotation": [30, 225, 0], + "scale": [0.625, 0.625, 0.625] + }, + "fixed": { + "scale": [0.5, 0.5, 0.5] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/grafting/textures/block/workbench_bottom.png b/src/main/resources/assets/grafting/textures/block/workbench_bottom.png new file mode 100644 index 0000000..2ab90bc Binary files /dev/null and b/src/main/resources/assets/grafting/textures/block/workbench_bottom.png differ diff --git a/src/main/resources/assets/grafting/textures/block/workbench_front.png b/src/main/resources/assets/grafting/textures/block/workbench_front.png new file mode 100644 index 0000000..22f3a4c Binary files /dev/null and b/src/main/resources/assets/grafting/textures/block/workbench_front.png differ diff --git a/src/main/resources/assets/grafting/textures/block/workbench_front.xcf b/src/main/resources/assets/grafting/textures/block/workbench_front.xcf new file mode 100644 index 0000000..657d7a9 Binary files /dev/null and b/src/main/resources/assets/grafting/textures/block/workbench_front.xcf differ diff --git a/src/main/resources/assets/grafting/textures/block/workbench_side.png b/src/main/resources/assets/grafting/textures/block/workbench_side.png new file mode 100644 index 0000000..3a86315 Binary files /dev/null and b/src/main/resources/assets/grafting/textures/block/workbench_side.png differ diff --git a/src/main/resources/assets/grafting/textures/block/workbench_side.xcf b/src/main/resources/assets/grafting/textures/block/workbench_side.xcf new file mode 100644 index 0000000..c0a95c5 Binary files /dev/null and b/src/main/resources/assets/grafting/textures/block/workbench_side.xcf differ diff --git a/src/main/resources/assets/grafting/textures/block/workbench_top.png b/src/main/resources/assets/grafting/textures/block/workbench_top.png new file mode 100644 index 0000000..5c4c1f0 Binary files /dev/null and b/src/main/resources/assets/grafting/textures/block/workbench_top.png differ diff --git a/src/main/resources/assets/grafting/textures/gui/icons/rotate_clockwise.png b/src/main/resources/assets/grafting/textures/gui/icons/rotate_clockwise.png new file mode 100644 index 0000000..03862a5 Binary files /dev/null and b/src/main/resources/assets/grafting/textures/gui/icons/rotate_clockwise.png differ diff --git a/src/main/resources/assets/grafting/textures/gui/icons/rotate_counter_clockwise.png b/src/main/resources/assets/grafting/textures/gui/icons/rotate_counter_clockwise.png new file mode 100644 index 0000000..caf727c Binary files /dev/null and b/src/main/resources/assets/grafting/textures/gui/icons/rotate_counter_clockwise.png differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 80a47bb..8360783 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -17,17 +17,10 @@ "entrypoints": { "main": [ "net.sys42.dakedres.grafting.Grafting" - ], - "client": [ - "net.sys42.dakedres.grafting.GraftingClient" ] }, "mixins": [ - "grafting.mixins.json", - { - "config": "grafting.client.mixins.json", - "environment": "client" - } + "grafting.mixins.json" ], "depends": { "fabricloader": ">=0.16.13",