Basic grid functionality and rotation

This commit is contained in:
Dakedres 2025-04-14 15:09:55 -06:00
parent 13c489cda0
commit 2197c69f0f
21 changed files with 770 additions and 46 deletions

View File

@ -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 {

View File

@ -14,4 +14,6 @@ maven_group=net.sys42.dakedres.grafting
archives_base_name=grafting
# Dependencies
fabric_version=0.115.4+1.21.1
fabric_version=0.115.4+1.21.1
polymer_version=0.9.18+1.21.1
sgui_version=1.6.1+1.21.1

View File

@ -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.
}
}

View File

@ -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
}
}

View File

@ -1,11 +0,0 @@
{
"required": true,
"package": "net.sys42.dakedres.grafting.mixin.client",
"compatibilityLevel": "JAVA_21",
"client": [
"ExampleClientMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -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<WorkbenchBlockEntity> 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();
}
}

View File

@ -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<ItemStack> rotate(DefaultedList<ItemStack> grid, int direction) {
// DefaultedList<ItemStack> 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;
}
}

View File

@ -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<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;
}
}
}

View File

@ -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<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);
}
}
}
);
}
}
}
}

View File

@ -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;
// }
}

View File

@ -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;
}
}

View File

@ -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]
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

View File

@ -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",