From 65636ea49422ec2e9159a7db7c351ec384e4d32c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 25 Apr 2026 11:13:48 +0000 Subject: [PATCH 01/17] Convert LumberjackBlock to a storage container (64-item single slot) The block now acts as both the worker's home and its log storage: - LumberjackBlockEntity: implements Container (vanilla hopper compat) with a single slot capped at 64 items; exposes IItemHandler capability via RegisterCapabilitiesEvent so pipes from other mods can extract from any face - LumberjackBlock: now extends BaseEntityBlock (EntityBlock); right-click shows "Storage: N/64 " in the action bar; breaking the block drops stored logs before discarding the entity - ChopTreeGoal: instead of Block.dropResources(), harvested logs are inserted into the block entity; any overflow is dropped on the ground - FindTreeGoal: will not search for new trees while the storage is full, preventing logs from piling up on the ground - ModBlockEntities: new DeferredRegister wiring up the block entity type https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../hardworkers/hardworkers/HardWorkers.java | 2 + .../hardworkers/block/LumberjackBlock.java | 53 +++++- .../blockentity/LumberjackBlockEntity.java | 169 ++++++++++++++++++ .../hardworkers/entity/ai/ChopTreeGoal.java | 20 ++- .../hardworkers/entity/ai/FindTreeGoal.java | 9 + .../events/ModEventSubscriber.java | 17 ++ .../hardworkers/init/ModBlockEntities.java | 20 +++ 7 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java create mode 100644 src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java diff --git a/src/main/java/com/hardworkers/hardworkers/HardWorkers.java b/src/main/java/com/hardworkers/hardworkers/HardWorkers.java index 261fcb5..e54f2d2 100644 --- a/src/main/java/com/hardworkers/hardworkers/HardWorkers.java +++ b/src/main/java/com/hardworkers/hardworkers/HardWorkers.java @@ -1,5 +1,6 @@ package com.hardworkers.hardworkers; +import com.hardworkers.hardworkers.init.ModBlockEntities; import com.hardworkers.hardworkers.init.ModBlocks; import com.hardworkers.hardworkers.init.ModEntities; import com.hardworkers.hardworkers.init.ModItems; @@ -17,6 +18,7 @@ public HardWorkers(IEventBus modEventBus, ModContainer modContainer) { ModBlocks.BLOCKS.register(modEventBus); ModItems.ITEMS.register(modEventBus); ModEntities.ENTITY_TYPES.register(modEventBus); + ModBlockEntities.BLOCK_ENTITY_TYPES.register(modEventBus); modContainer.registerConfig(ModConfig.Type.COMMON, HardWorkersConfig.SPEC); } diff --git a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java index a4a6dae..6bcc8a3 100644 --- a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java +++ b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java @@ -1,17 +1,28 @@ package com.hardworkers.hardworkers.block; +import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; +import com.hardworkers.hardworkers.init.ModBlockEntities; import com.hardworkers.hardworkers.init.ModEntities; import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.MapColor; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; -public class LumberjackBlock extends Block { +import javax.annotation.Nullable; + +public class LumberjackBlock extends BaseEntityBlock { public LumberjackBlock() { super(BlockBehaviour.Properties.of() @@ -21,6 +32,26 @@ public LumberjackBlock() { ); } + // ------------------------------------------------------------------------- + // EntityBlock + // ------------------------------------------------------------------------- + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new LumberjackBlockEntity(pos, state); + } + + // Keep the normal cube model; BaseEntityBlock defaults to INVISIBLE. + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + // ------------------------------------------------------------------------- + // Lifecycle + // ------------------------------------------------------------------------- + @Override public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { super.onPlace(state, level, pos, oldState, movedByPiston); @@ -37,6 +68,11 @@ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldS @Override public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { if (!level.isClientSide() && !state.is(newState.getBlock())) { + // Drop stored logs + if (level.getBlockEntity(pos) instanceof LumberjackBlockEntity be) { + Containers.dropContents(level, pos, be); + } + // Discard the associated lumberjack entity AABB searchArea = new AABB(pos).inflate(3.0); level.getEntitiesOfClass(LumberjackEntity.class, searchArea) .stream() @@ -45,4 +81,17 @@ public void onRemove(BlockState state, Level level, BlockPos pos, BlockState new } super.onRemove(state, level, pos, newState, movedByPiston); } + + // ------------------------------------------------------------------------- + // Interaction — right-click shows storage status in the action bar + // ------------------------------------------------------------------------- + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, + Player player, BlockHitResult hitResult) { + if (!level.isClientSide() && level.getBlockEntity(pos) instanceof LumberjackBlockEntity be) { + player.displayClientMessage(be.getStorageStatus(), true); + } + return InteractionResult.sidedSuccess(level.isClientSide()); + } } diff --git a/src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java b/src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java new file mode 100644 index 0000000..03f53c4 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java @@ -0,0 +1,169 @@ +package com.hardworkers.hardworkers.blockentity; + +import com.hardworkers.hardworkers.init.ModBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.wrapper.InvWrapper; + +import java.util.function.Supplier; + +/** + * Single-slot (max 64 items) storage for logs harvested by the lumberjack. + * Implements {@link Container} for vanilla hopper compatibility. + * Exposes {@link IItemHandler} via capability for other mods' pipes. + */ +public class LumberjackBlockEntity extends BlockEntity implements Container { + + public static final int CAPACITY = 64; + + private ItemStack stored = ItemStack.EMPTY; + + // Cached wrapper — created once per block entity instance. + private final IItemHandler itemHandler = new InvWrapper(this); + + public LumberjackBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.LUMBERJACK_BLOCK_ENTITY.get(), pos, state); + } + + // ------------------------------------------------------------------------- + // Public API + // ------------------------------------------------------------------------- + + public IItemHandler getItemHandler() { + return itemHandler; + } + + /** + * Tries to insert {@code incoming} into the slot. + * + * @return the leftover stack (empty if everything was accepted). + */ + public ItemStack insertItem(ItemStack incoming) { + if (incoming.isEmpty()) return ItemStack.EMPTY; + + if (stored.isEmpty()) { + int accept = Math.min(incoming.getCount(), CAPACITY); + stored = incoming.copyWithCount(accept); + setChanged(); + int leftover = incoming.getCount() - accept; + return leftover > 0 ? incoming.copyWithCount(leftover) : ItemStack.EMPTY; + } + + if (stored.getItem() == incoming.getItem()) { + int space = CAPACITY - stored.getCount(); + if (space > 0) { + int accept = Math.min(space, incoming.getCount()); + stored.grow(accept); + setChanged(); + int leftover = incoming.getCount() - accept; + return leftover > 0 ? incoming.copyWithCount(leftover) : ItemStack.EMPTY; + } + } + + return incoming; // full or different item type + } + + public boolean isFull() { + return !stored.isEmpty() && stored.getCount() >= CAPACITY; + } + + public Component getStorageStatus() { + if (stored.isEmpty()) { + return Component.literal("Storage: empty"); + } + return Component.literal("Storage: " + stored.getCount() + "/" + CAPACITY + + " " + stored.getHoverName().getString()); + } + + // ------------------------------------------------------------------------- + // Container (vanilla hopper compatibility) + // ------------------------------------------------------------------------- + + @Override + public int getContainerSize() { + return 1; + } + + @Override + public boolean isEmpty() { + return stored.isEmpty(); + } + + @Override + public ItemStack getItem(int slot) { + return slot == 0 ? stored : ItemStack.EMPTY; + } + + @Override + public ItemStack removeItem(int slot, int amount) { + if (slot != 0 || stored.isEmpty()) return ItemStack.EMPTY; + ItemStack taken = stored.split(amount); + setChanged(); + return taken; + } + + @Override + public ItemStack removeItemNoUpdate(int slot) { + if (slot != 0) return ItemStack.EMPTY; + ItemStack was = stored; + stored = ItemStack.EMPTY; + return was; + } + + @Override + public void setItem(int slot, ItemStack stack) { + if (slot != 0) return; + if (!stack.isEmpty() && stack.getCount() > CAPACITY) { + stack = stack.copyWithCount(CAPACITY); + } + stored = stack; + setChanged(); + } + + @Override + public int getMaxStackSize() { + return CAPACITY; + } + + @Override + public boolean stillValid(Player player) { + return true; + } + + @Override + public void clearContent() { + stored = ItemStack.EMPTY; + setChanged(); + } + + // ------------------------------------------------------------------------- + // NBT + // ------------------------------------------------------------------------- + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + if (!stored.isEmpty()) { + tag.put("Stored", stored.save(registries)); + } + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + if (tag.contains("Stored")) { + stored = ItemStack.parseOptional(registries, tag.getCompound("Stored")); + } else { + stored = ItemStack.EMPTY; + } + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java index 4433779..d05b837 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java @@ -1,6 +1,7 @@ package com.hardworkers.hardworkers.entity.ai; import com.hardworkers.hardworkers.HardWorkersConfig; +import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; @@ -11,6 +12,7 @@ import net.minecraft.world.level.LevelEvent; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import java.util.*; @@ -118,9 +120,9 @@ private void chopNext() { ); if (level instanceof ServerLevel serverLevel) { - Block.dropResources(state, serverLevel, logPos, null, lumberjack, ItemStack.EMPTY); level.setBlock(logPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, logPos, Block.getId(state)); + depositLog(serverLevel, new ItemStack(state.getBlock().asItem(), 1)); } } @@ -137,6 +139,22 @@ private void plantSapling() { } } + /** + * Sends the harvested log to the block entity's storage. + * Falls back to dropping the item on the ground if the storage is full or missing. + */ + private void depositLog(ServerLevel level, ItemStack logItem) { + BlockEntity be = level.getBlockEntity(lumberjack.getHomePosition()); + if (be instanceof LumberjackBlockEntity storage) { + ItemStack remainder = storage.insertItem(logItem); + if (!remainder.isEmpty()) { + Block.popResource(level, lumberjack.blockPosition(), remainder); + } + } else { + Block.popResource(level, lumberjack.blockPosition(), logItem); + } + } + /** BFS from the base log to collect all connected log blocks. */ private void collectTreeLogs(Level level, BlockPos base) { logsToChop.clear(); diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java index 6dd94bc..84d297c 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java @@ -1,11 +1,13 @@ package com.hardworkers.hardworkers.entity.ai; import com.hardworkers.hardworkers.HardWorkersConfig; +import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; import net.minecraft.core.BlockPos; import net.minecraft.tags.BlockTags; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import java.util.EnumSet; @@ -30,6 +32,7 @@ public FindTreeGoal(LumberjackEntity lumberjack) { @Override public boolean canUse() { if (lumberjack.getTargetTree() != null) return false; + if (isStorageFull()) return false; if (searchCooldown > 0) { searchCooldown--; return false; @@ -37,6 +40,12 @@ public boolean canUse() { return true; } + private boolean isStorageFull() { + Level level = lumberjack.level(); + BlockEntity be = level.getBlockEntity(lumberjack.getHomePosition()); + return be instanceof LumberjackBlockEntity storage && storage.isFull(); + } + @Override public boolean canContinueToUse() { return false; // one-shot; ChopTreeGoal picks up from here diff --git a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java index aa3d176..1b911fc 100644 --- a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java +++ b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java @@ -2,13 +2,17 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.entity.LumberjackEntity; +import com.hardworkers.hardworkers.init.ModBlockEntities; import com.hardworkers.hardworkers.init.ModEntities; import com.hardworkers.hardworkers.init.ModItems; import net.minecraft.world.item.CreativeModeTabs; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent; +import net.neoforged.neoforge.items.wrapper.InvWrapper; @EventBusSubscriber(modid = HardWorkers.MODID, bus = EventBusSubscriber.Bus.MOD) public class ModEventSubscriber { @@ -24,4 +28,17 @@ public static void onBuildCreativeTab(BuildCreativeModeTabContentsEvent event) { event.accept(ModItems.LUMBERJACK_BLOCK_ITEM); } } + + /** + * Exposes the block entity's inventory as IItemHandler so that hoppers and + * pipes from other mods (e.g. Mekanism, Pipez) can extract logs from it. + */ + @SubscribeEvent + public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + ModBlockEntities.LUMBERJACK_BLOCK_ENTITY.get(), + (be, side) -> be.getItemHandler() + ); + } } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java new file mode 100644 index 0000000..59134b9 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java @@ -0,0 +1,20 @@ +package com.hardworkers.hardworkers.init; + +import com.hardworkers.hardworkers.HardWorkers; +import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.neoforged.neoforge.registries.DeferredHolder; +import net.neoforged.neoforge.registries.DeferredRegister; + +public class ModBlockEntities { + + public static final DeferredRegister> BLOCK_ENTITY_TYPES = + DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, HardWorkers.MODID); + + public static final DeferredHolder, BlockEntityType> LUMBERJACK_BLOCK_ENTITY = + BLOCK_ENTITY_TYPES.register("lumberjack_block", () -> + BlockEntityType.Builder.of(LumberjackBlockEntity::new, ModBlocks.LUMBERJACK_BLOCK.get()) + .build(null) + ); +} From f0bd9d49b0e6da3d41a72ceff129e221af88b79e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 03:33:59 +0000 Subject: [PATCH 02/17] Expand storage to 27 slots and collect full tree drops (leaves included) LumberjackBlockEntity: - Replaced single ItemStack slot with NonNullList of 27 slots (same as a vanilla chest) - insertItem() does two-pass insertion: merge into existing matching stacks first, then fill the next empty slot; handles different log types, saplings, apples and any other item in separate slots - isFull() now checks all 27 slots; getStorageStatus() shows slots used and total item count - NBT uses ContainerHelper.saveAllItems / loadAllItems for slot-indexed serialization ChopTreeGoal: - collectTreeBlocks() now calls collectLeafBlocks() after the log BFS; collectLeafBlocks() scans a bounding box around the log columns (+ 3-block margin) and records every leaf block - After the last log is broken, clearLeaves() runs in the same tick: each leaf is evaluated through Block.getDrops() so loot-table chances for saplings, apples, and sticks are respected, then removed from the world - chopNext() also switched from hardcoded new ItemStack(...) to Block.getDrops() for consistency with modded logs that may have custom loot tables - depositItems() handles a full List and falls back to Block.popResource() for any overflow the storage can't accept https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../blockentity/LumberjackBlockEntity.java | 122 ++++++++++-------- .../hardworkers/entity/ai/ChopTreeGoal.java | 107 ++++++++++++--- 2 files changed, 152 insertions(+), 77 deletions(-) diff --git a/src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java b/src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java index 03f53c4..1666899 100644 --- a/src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java +++ b/src/main/java/com/hardworkers/hardworkers/blockentity/LumberjackBlockEntity.java @@ -3,6 +3,7 @@ import com.hardworkers.hardworkers.init.ModBlockEntities; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; +import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.Container; @@ -14,20 +15,19 @@ import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.wrapper.InvWrapper; -import java.util.function.Supplier; - /** - * Single-slot (max 64 items) storage for logs harvested by the lumberjack. + * Chest-sized (27-slot) storage for everything the lumberjack harvests: + * logs, saplings, apples, sticks, etc. + *

* Implements {@link Container} for vanilla hopper compatibility. - * Exposes {@link IItemHandler} via capability for other mods' pipes. + * Exposes {@link IItemHandler} via capability for mod pipes. */ public class LumberjackBlockEntity extends BlockEntity implements Container { - public static final int CAPACITY = 64; + public static final int SLOT_COUNT = 27; - private ItemStack stored = ItemStack.EMPTY; + private NonNullList items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); - // Cached wrapper — created once per block entity instance. private final IItemHandler itemHandler = new InvWrapper(this); public LumberjackBlockEntity(BlockPos pos, BlockState state) { @@ -43,45 +43,66 @@ public IItemHandler getItemHandler() { } /** - * Tries to insert {@code incoming} into the slot. + * Inserts {@code incoming} into the best available slot. + * Merges with existing stacks of the same item first, then fills empty slots. * - * @return the leftover stack (empty if everything was accepted). + * @return the leftover that could not be stored (empty if fully accepted). */ public ItemStack insertItem(ItemStack incoming) { if (incoming.isEmpty()) return ItemStack.EMPTY; - if (stored.isEmpty()) { - int accept = Math.min(incoming.getCount(), CAPACITY); - stored = incoming.copyWithCount(accept); - setChanged(); - int leftover = incoming.getCount() - accept; - return leftover > 0 ? incoming.copyWithCount(leftover) : ItemStack.EMPTY; + // Pass 1 — merge into existing matching stacks + for (int i = 0; i < SLOT_COUNT; i++) { + ItemStack slot = items.get(i); + if (!slot.isEmpty() + && slot.getItem() == incoming.getItem() + && ItemStack.isSameItemSameComponents(slot, incoming)) { + int space = slot.getMaxStackSize() - slot.getCount(); + if (space > 0) { + int toAdd = Math.min(space, incoming.getCount()); + slot.grow(toAdd); + setChanged(); + incoming = incoming.copyWithCount(incoming.getCount() - toAdd); + if (incoming.isEmpty()) return ItemStack.EMPTY; + } + } } - if (stored.getItem() == incoming.getItem()) { - int space = CAPACITY - stored.getCount(); - if (space > 0) { - int accept = Math.min(space, incoming.getCount()); - stored.grow(accept); + // Pass 2 — fill the first empty slot + for (int i = 0; i < SLOT_COUNT; i++) { + if (items.get(i).isEmpty()) { + int toStore = Math.min(incoming.getCount(), incoming.getMaxStackSize()); + items.set(i, incoming.copyWithCount(toStore)); setChanged(); - int leftover = incoming.getCount() - accept; + int leftover = incoming.getCount() - toStore; return leftover > 0 ? incoming.copyWithCount(leftover) : ItemStack.EMPTY; } } - return incoming; // full or different item type + return incoming; // no space } + /** Returns true when every slot is occupied with a full stack. */ public boolean isFull() { - return !stored.isEmpty() && stored.getCount() >= CAPACITY; + for (ItemStack stack : items) { + if (stack.isEmpty() || stack.getCount() < stack.getMaxStackSize()) return false; + } + return true; } public Component getStorageStatus() { - if (stored.isEmpty()) { - return Component.literal("Storage: empty"); + int usedSlots = 0; + int totalItems = 0; + for (ItemStack stack : items) { + if (!stack.isEmpty()) { + usedSlots++; + totalItems += stack.getCount(); + } } - return Component.literal("Storage: " + stored.getCount() + "/" + CAPACITY - + " " + stored.getHoverName().getString()); + if (usedSlots == 0) return Component.literal("Storage: empty"); + return Component.literal( + "Storage: " + usedSlots + "/" + SLOT_COUNT + " slots (" + totalItems + " items)" + ); } // ------------------------------------------------------------------------- @@ -90,48 +111,40 @@ public Component getStorageStatus() { @Override public int getContainerSize() { - return 1; + return SLOT_COUNT; } @Override public boolean isEmpty() { - return stored.isEmpty(); + for (ItemStack stack : items) { + if (!stack.isEmpty()) return false; + } + return true; } @Override public ItemStack getItem(int slot) { - return slot == 0 ? stored : ItemStack.EMPTY; + return slot >= 0 && slot < SLOT_COUNT ? items.get(slot) : ItemStack.EMPTY; } @Override public ItemStack removeItem(int slot, int amount) { - if (slot != 0 || stored.isEmpty()) return ItemStack.EMPTY; - ItemStack taken = stored.split(amount); - setChanged(); - return taken; + ItemStack result = ContainerHelper.removeItem(items, slot, amount); + if (!result.isEmpty()) setChanged(); + return result; } @Override public ItemStack removeItemNoUpdate(int slot) { - if (slot != 0) return ItemStack.EMPTY; - ItemStack was = stored; - stored = ItemStack.EMPTY; - return was; + return ContainerHelper.takeItem(items, slot); } @Override public void setItem(int slot, ItemStack stack) { - if (slot != 0) return; - if (!stack.isEmpty() && stack.getCount() > CAPACITY) { - stack = stack.copyWithCount(CAPACITY); + if (slot >= 0 && slot < SLOT_COUNT) { + items.set(slot, stack); + setChanged(); } - stored = stack; - setChanged(); - } - - @Override - public int getMaxStackSize() { - return CAPACITY; } @Override @@ -141,7 +154,7 @@ public boolean stillValid(Player player) { @Override public void clearContent() { - stored = ItemStack.EMPTY; + items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); setChanged(); } @@ -152,18 +165,13 @@ public void clearContent() { @Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.saveAdditional(tag, registries); - if (!stored.isEmpty()) { - tag.put("Stored", stored.save(registries)); - } + ContainerHelper.saveAllItems(tag, items, registries); } @Override public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.loadAdditional(tag, registries); - if (tag.contains("Stored")) { - stored = ItemStack.parseOptional(registries, tag.getCompound("Stored")); - } else { - stored = ItemStack.EMPTY; - } + items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); + ContainerHelper.loadAllItems(tag, items, registries); } } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java index d05b837..477a14d 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java @@ -18,18 +18,23 @@ import java.util.*; /** - * Navigates to the tree set by FindTreeGoal, breaks every connected log - * (BFS-collected, chopped bottom-to-top) at the configured interval, then - * plants the matching sapling at the base position. + * Navigates to the tree set by FindTreeGoal, breaks every connected log at the + * configured interval, then instantly clears all associated leaves (collecting + * their drops — saplings, apples, sticks), and finally plants a sapling. + * + * All drops go into the LumberjackBlockEntity; overflow lands on the ground. */ public class ChopTreeGoal extends Goal { private static final double REACH_DIST_SQ = 16.0; // 4-block reach private static final int MAX_LOGS = 256; + /** Margin around the log bounding box to scan for leaf blocks. */ + private static final int LEAF_MARGIN = 3; private final LumberjackEntity lumberjack; private int chopTimer = 0; private final List logsToChop = new ArrayList<>(); + private final List leavesToClear = new ArrayList<>(); private Block saplingType = null; private BlockPos plantPos = null; @@ -58,7 +63,7 @@ public void start() { saplingType = saplingFor(level.getBlockState(treeBase).getBlock()); plantPos = treeBase; - collectTreeLogs(level, treeBase); + collectTreeBlocks(level, treeBase); navigateTo(treeBase); } @@ -83,15 +88,16 @@ public void tick() { } if (logsToChop.isEmpty()) { + clearLeaves(); plantSapling(); lumberjack.setTargetTree(null); - // canContinueToUse() will return false next tick, triggering stop() } } @Override public void stop() { logsToChop.clear(); + leavesToClear.clear(); saplingType = null; plantPos = null; chopTimer = 0; @@ -113,19 +119,36 @@ private void chopNext() { BlockPos logPos = logsToChop.remove(0); BlockState state = level.getBlockState(logPos); - if (!state.is(BlockTags.LOGS)) return; // already broken by something else + if (!state.is(BlockTags.LOGS)) return; lumberjack.getLookControl().setLookAt( logPos.getX() + 0.5, logPos.getY() + 0.5, logPos.getZ() + 0.5 ); if (level instanceof ServerLevel serverLevel) { - level.setBlock(logPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); - level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, logPos, Block.getId(state)); - depositLog(serverLevel, new ItemStack(state.getBlock().asItem(), 1)); + List drops = Block.getDrops(state, serverLevel, logPos, null); + serverLevel.setBlock(logPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + serverLevel.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, logPos, Block.getId(state)); + depositItems(serverLevel, drops); } } + /** Instantly removes all leaf blocks that were part of this tree, collecting their drops. */ + private void clearLeaves() { + Level level = lumberjack.level(); + if (!(level instanceof ServerLevel serverLevel)) return; + + for (BlockPos leafPos : leavesToClear) { + BlockState leafState = serverLevel.getBlockState(leafPos); + if (!leafState.is(BlockTags.LEAVES)) continue; // may have already decayed + + List drops = Block.getDrops(leafState, serverLevel, leafPos, null); + serverLevel.setBlock(leafPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + depositItems(serverLevel, drops); + } + leavesToClear.clear(); + } + private void plantSapling() { if (plantPos == null || saplingType == null) return; @@ -140,24 +163,38 @@ private void plantSapling() { } /** - * Sends the harvested log to the block entity's storage. - * Falls back to dropping the item on the ground if the storage is full or missing. + * Sends all {@code drops} to the block entity's storage. + * Items that don't fit are popped as entities near the lumberjack. */ - private void depositLog(ServerLevel level, ItemStack logItem) { + private void depositItems(ServerLevel level, List drops) { BlockEntity be = level.getBlockEntity(lumberjack.getHomePosition()); if (be instanceof LumberjackBlockEntity storage) { - ItemStack remainder = storage.insertItem(logItem); - if (!remainder.isEmpty()) { - Block.popResource(level, lumberjack.blockPosition(), remainder); + for (ItemStack drop : drops) { + ItemStack remainder = storage.insertItem(drop); + if (!remainder.isEmpty()) { + Block.popResource(level, lumberjack.blockPosition(), remainder); + } } } else { - Block.popResource(level, lumberjack.blockPosition(), logItem); + for (ItemStack drop : drops) { + Block.popResource(level, lumberjack.blockPosition(), drop); + } } } - /** BFS from the base log to collect all connected log blocks. */ - private void collectTreeLogs(Level level, BlockPos base) { + // ------------------------------------------------------------------------- + // Tree scanning + // ------------------------------------------------------------------------- + + /** + * BFS from {@code base} to collect all connected log blocks into + * {@link #logsToChop}, then scans the log bounding box for associated + * leaf blocks into {@link #leavesToClear}. + */ + private void collectTreeBlocks(Level level, BlockPos base) { logsToChop.clear(); + leavesToClear.clear(); + Set visited = new HashSet<>(); Queue queue = new ArrayDeque<>(); queue.add(base); @@ -169,7 +206,6 @@ private void collectTreeLogs(Level level, BlockPos base) { logsToChop.add(current); - // Logs connect upward, diagonally-up, and horizontally (for large oak etc.) BlockPos[] neighbours = { current.above(), current.north(), current.south(), current.east(), current.west(), @@ -186,8 +222,39 @@ private void collectTreeLogs(Level level, BlockPos base) { } } - // Bottom logs first so the tree "falls" from the base logsToChop.sort(Comparator.comparingInt(BlockPos::getY)); + collectLeafBlocks(level); + } + + /** + * Scans a box around the collected logs (+ {@link #LEAF_MARGIN}) for + * leaf blocks and populates {@link #leavesToClear}. + */ + private void collectLeafBlocks(Level level) { + if (logsToChop.isEmpty()) return; + + int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, minZ = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE, maxZ = Integer.MIN_VALUE; + for (BlockPos p : logsToChop) { + if (p.getX() < minX) minX = p.getX(); + if (p.getY() < minY) minY = p.getY(); + if (p.getZ() < minZ) minZ = p.getZ(); + if (p.getX() > maxX) maxX = p.getX(); + if (p.getY() > maxY) maxY = p.getY(); + if (p.getZ() > maxZ) maxZ = p.getZ(); + } + + Set logSet = new HashSet<>(logsToChop); + for (int x = minX - LEAF_MARGIN; x <= maxX + LEAF_MARGIN; x++) { + for (int y = minY - 1; y <= maxY + LEAF_MARGIN; y++) { + for (int z = minZ - LEAF_MARGIN; z <= maxZ + LEAF_MARGIN; z++) { + BlockPos p = new BlockPos(x, y, z); + if (!logSet.contains(p) && level.getBlockState(p).is(BlockTags.LEAVES)) { + leavesToClear.add(p); + } + } + } + } } private Block saplingFor(Block log) { From 954b007aa2a6bad0c09ebb0c8cceb29cc458830d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 03:39:02 +0000 Subject: [PATCH 03/17] Add gradle-wrapper.jar (Gradle 8.8) Required for ./gradlew / gradlew.bat to work without a global Gradle install. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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

NnX)EH+Nua)3Y(c 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 Date: Fri, 1 May 2026 03:39:37 +0000 Subject: [PATCH 04/17] Add .gitignore Excludes Gradle cache, build output, and IDE-specific files. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .gitignore | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..43a350a --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Gradle +.gradle/ +build/ +out/ + +# Generated sources +src/generated/ + +# IntelliJ IDEA +.idea/ +*.iml +*.ipr +*.iws + +# Eclipse +.classpath +.project +.settings/ + +# VS Code +.vscode/ + +# macOS +.DS_Store + +# Windows +Thumbs.db From 92df34244e654b6b941a72bb957ecc98e83b659f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 04:31:25 +0000 Subject: [PATCH 05/17] Fix 1.21.1 compilation errors - ChopTreeGoal: remove LevelEvent import (class absent in 1.21.1), use raw int 2001 for block-break particle event; remove PALE_OAK_* constants which were only added in 1.21.2 - LumberjackBlock: add required codec() override (BaseEntityBlock made it abstract in 1.21.1); update constructor to accept BlockBehaviour.Properties as required by simpleCodec - ModBlocks: pass Properties explicitly to LumberjackBlock constructor https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../hardworkers/block/LumberjackBlock.java | 16 ++++++++++------ .../hardworkers/entity/ai/ChopTreeGoal.java | 4 +--- .../hardworkers/hardworkers/init/ModBlocks.java | 10 +++++++++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java index 6bcc8a3..e4ee5a2 100644 --- a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java +++ b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java @@ -4,6 +4,7 @@ import com.hardworkers.hardworkers.entity.LumberjackEntity; import com.hardworkers.hardworkers.init.ModBlockEntities; import com.hardworkers.hardworkers.init.ModEntities; +import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.Containers; @@ -24,12 +25,15 @@ public class LumberjackBlock extends BaseEntityBlock { - public LumberjackBlock() { - super(BlockBehaviour.Properties.of() - .mapColor(MapColor.WOOD) - .sound(SoundType.WOOD) - .strength(2.0f) - ); + public static final MapCodec CODEC = simpleCodec(LumberjackBlock::new); + + public LumberjackBlock(BlockBehaviour.Properties properties) { + super(properties); + } + + @Override + public MapCodec codec() { + return CODEC; } // ------------------------------------------------------------------------- diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java index 477a14d..638aa30 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java @@ -9,7 +9,6 @@ import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelEvent; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -128,7 +127,7 @@ private void chopNext() { if (level instanceof ServerLevel serverLevel) { List drops = Block.getDrops(state, serverLevel, logPos, null); serverLevel.setBlock(logPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); - serverLevel.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, logPos, Block.getId(state)); + serverLevel.levelEvent(2001, logPos, Block.getId(state)); // 2001 = block break particles + sound depositItems(serverLevel, drops); } } @@ -266,7 +265,6 @@ private Block saplingFor(Block log) { if (log == Blocks.DARK_OAK_LOG || log == Blocks.STRIPPED_DARK_OAK_LOG) return Blocks.DARK_OAK_SAPLING; if (log == Blocks.MANGROVE_LOG || log == Blocks.STRIPPED_MANGROVE_LOG) return Blocks.MANGROVE_PROPAGULE; if (log == Blocks.CHERRY_LOG || log == Blocks.STRIPPED_CHERRY_LOG) return Blocks.CHERRY_SAPLING; - if (log == Blocks.PALE_OAK_LOG || log == Blocks.STRIPPED_PALE_OAK_LOG) return Blocks.PALE_OAK_SAPLING; return Blocks.OAK_SAPLING; } } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java index 8fa6482..0db0ab7 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java @@ -2,6 +2,9 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.block.LumberjackBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.material.MapColor; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; @@ -10,5 +13,10 @@ public class ModBlocks { public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(HardWorkers.MODID); public static final DeferredBlock LUMBERJACK_BLOCK = - BLOCKS.register("lumberjack_block", LumberjackBlock::new); + BLOCKS.register("lumberjack_block", () -> new LumberjackBlock( + BlockBehaviour.Properties.of() + .mapColor(MapColor.WOOD) + .sound(SoundType.WOOD) + .strength(2.0f) + )); } From 304e5e800b98753a2964096beacc0691c23fed93 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 04:43:35 +0000 Subject: [PATCH 06/17] Force ASM 9.7 across all configurations to fix BootstrapLauncher crash NeoForge 21.1.172 puts asm-analysis-9.7 on the JVM module path while NeoGradle 7.0.145 drags in asm-analysis-9.5 as a transitive dependency on the classpath. BootstrapLauncher 2.0.2 treats this as a fatal duplicate- module error and exits with code 1 before Minecraft starts. Forcing all org.ow2.asm artifacts to 9.7 via resolutionStrategy eliminates the 9.5 jar from the classpath so there is no conflict. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- build.gradle | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/build.gradle b/build.gradle index c121d3d..7bbdd3e 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,19 @@ repositories { mavenLocal() } +// NeoForge 21.1.x puts ASM 9.7 on the JVM module path. The NeoGradle plugin +// pulls in a transitive ASM 9.5 which then also lands on the classpath, +// causing BootstrapLauncher to refuse to start. Force everything to 9.7. +configurations.all { + resolutionStrategy { + force 'org.ow2.asm:asm:9.7' + force 'org.ow2.asm:asm-analysis:9.7' + force 'org.ow2.asm:asm-commons:9.7' + force 'org.ow2.asm:asm-tree:9.7' + force 'org.ow2.asm:asm-util:9.7' + } +} + dependencies { implementation "net.neoforged:neoforge:${project.neo_version}" } From c3cb3c4c68890dc2c0d68522074d1520d954bf2d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 05:20:07 +0000 Subject: [PATCH 07/17] =?UTF-8?q?Add=205-tier=20lumberjack=20system=20(Woo?= =?UTF-8?q?d=20=E2=86=92=20Stone=20=E2=86=92=20Iron=20=E2=86=92=20Diamond?= =?UTF-8?q?=20=E2=86=92=20Netherite)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LumberjackTier enum defines chop intervals: Wood=40t Stone=30t Iron=20t Diamond=12t Netherite=6t (ticks/log) LumberjackBlock now accepts a LumberjackTier at construction time. Each tier is a separately registered block sharing the same LumberjackBlockEntity type. A per-tier MapCodec is declared via simpleCodec to satisfy the abstract codec() requirement from BaseEntityBlock in 1.21.1. ChopTreeGoal.chopInterval() reads the tier off the home block at runtime so the speed is always consistent with the placed block, no config needed. LUMBERJACK_CHOP_INTERVAL removed from config (hardcoded per tier now). Recipes chain upgrades: each tier consumes the previous block + the matching axe tier + 7 planks. Pattern: WWW / WAW / WWW (wood — no previous block) WWW / WAW / WBW (stone/iron/diamond/netherite — B = previous tier) Resources: 5 blockstates, 5 block models, 5 item models, 5 recipe files, 5 loot tables, 5 placeholder textures (colour-coded per tier), updated lang. Old single-tier lumberjack_block resources removed. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../hardworkers/HardWorkersConfig.java | 5 --- .../hardworkers/block/LumberjackBlock.java | 42 ++++++++++++++---- .../hardworkers/block/LumberjackTier.java | 20 +++++++++ .../hardworkers/entity/ai/ChopTreeGoal.java | 13 +++++- .../events/ModEventSubscriber.java | 11 +++-- .../hardworkers/init/ModBlockEntities.java | 10 ++++- .../hardworkers/init/ModBlocks.java | 29 ++++++++---- .../hardworkers/init/ModItems.java | 7 ++- .../blockstates/lumberjack_block.json | 7 --- .../blockstates/lumberjack_diamond.json | 5 +++ .../blockstates/lumberjack_iron.json | 5 +++ .../blockstates/lumberjack_netherite.json | 5 +++ .../blockstates/lumberjack_stone.json | 5 +++ .../blockstates/lumberjack_wood.json | 5 +++ .../assets/hardworkers/lang/en_us.json | 8 +++- .../models/block/lumberjack_block.json | 6 --- .../models/block/lumberjack_diamond.json | 4 ++ .../models/block/lumberjack_iron.json | 4 ++ .../models/block/lumberjack_netherite.json | 4 ++ .../models/block/lumberjack_stone.json | 4 ++ .../models/block/lumberjack_wood.json | 4 ++ .../models/item/lumberjack_block.json | 3 -- .../models/item/lumberjack_diamond.json | 3 ++ .../models/item/lumberjack_iron.json | 3 ++ .../models/item/lumberjack_netherite.json | 3 ++ .../models/item/lumberjack_stone.json | 3 ++ .../models/item/lumberjack_wood.json | 3 ++ .../textures/block/lumberjack_diamond.png | Bin 0 -> 79 bytes .../textures/block/lumberjack_iron.png | Bin 0 -> 79 bytes .../textures/block/lumberjack_netherite.png | Bin 0 -> 79 bytes .../textures/block/lumberjack_stone.png | Bin 0 -> 77 bytes ...mberjack_block.png => lumberjack_wood.png} | Bin .../loot_tables/blocks/lumberjack_block.json | 18 -------- .../blocks/lumberjack_diamond.json | 9 ++++ .../loot_tables/blocks/lumberjack_iron.json | 9 ++++ .../blocks/lumberjack_netherite.json | 9 ++++ .../loot_tables/blocks/lumberjack_stone.json | 9 ++++ .../loot_tables/blocks/lumberjack_wood.json | 9 ++++ .../hardworkers/recipes/lumberjack_block.json | 17 ------- .../recipes/lumberjack_diamond.json | 11 +++++ .../hardworkers/recipes/lumberjack_iron.json | 11 +++++ .../recipes/lumberjack_netherite.json | 11 +++++ .../hardworkers/recipes/lumberjack_stone.json | 11 +++++ .../hardworkers/recipes/lumberjack_wood.json | 10 +++++ 44 files changed, 268 insertions(+), 87 deletions(-) create mode 100644 src/main/java/com/hardworkers/hardworkers/block/LumberjackTier.java delete mode 100644 src/main/resources/assets/hardworkers/blockstates/lumberjack_block.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json delete mode 100644 src/main/resources/assets/hardworkers/models/block/lumberjack_block.json create mode 100644 src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json delete mode 100644 src/main/resources/assets/hardworkers/models/item/lumberjack_block.json create mode 100644 src/main/resources/assets/hardworkers/models/item/lumberjack_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/item/lumberjack_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/item/lumberjack_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/item/lumberjack_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/item/lumberjack_wood.json create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_iron.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_stone.png rename src/main/resources/assets/hardworkers/textures/block/{lumberjack_block.png => lumberjack_wood.png} (100%) delete mode 100644 src/main/resources/data/hardworkers/loot_tables/blocks/lumberjack_block.json create mode 100644 src/main/resources/data/hardworkers/loot_tables/blocks/lumberjack_diamond.json create mode 100644 src/main/resources/data/hardworkers/loot_tables/blocks/lumberjack_iron.json create mode 100644 src/main/resources/data/hardworkers/loot_tables/blocks/lumberjack_netherite.json create mode 100644 src/main/resources/data/hardworkers/loot_tables/blocks/lumberjack_stone.json create mode 100644 src/main/resources/data/hardworkers/loot_tables/blocks/lumberjack_wood.json delete mode 100644 src/main/resources/data/hardworkers/recipes/lumberjack_block.json create mode 100644 src/main/resources/data/hardworkers/recipes/lumberjack_diamond.json create mode 100644 src/main/resources/data/hardworkers/recipes/lumberjack_iron.json create mode 100644 src/main/resources/data/hardworkers/recipes/lumberjack_netherite.json create mode 100644 src/main/resources/data/hardworkers/recipes/lumberjack_stone.json create mode 100644 src/main/resources/data/hardworkers/recipes/lumberjack_wood.json diff --git a/src/main/java/com/hardworkers/hardworkers/HardWorkersConfig.java b/src/main/java/com/hardworkers/hardworkers/HardWorkersConfig.java index 3b2a3b3..6df490e 100644 --- a/src/main/java/com/hardworkers/hardworkers/HardWorkersConfig.java +++ b/src/main/java/com/hardworkers/hardworkers/HardWorkersConfig.java @@ -9,7 +9,6 @@ public class HardWorkersConfig { public static final ModConfigSpec SPEC; public static final ModConfigSpec.IntValue LUMBERJACK_SEARCH_RADIUS; - public static final ModConfigSpec.IntValue LUMBERJACK_CHOP_INTERVAL; static { BUILDER.comment("Lumberjack Settings").push("lumberjack"); @@ -18,10 +17,6 @@ public class HardWorkersConfig { .comment("Radius in blocks the lumberjack searches for trees around its home block") .defineInRange("searchRadius", 16, 4, 64); - LUMBERJACK_CHOP_INTERVAL = BUILDER - .comment("Ticks between each log broken (20 ticks = 1 second)") - .defineInRange("chopInterval", 20, 5, 100); - BUILDER.pop(); SPEC = BUILDER.build(); } diff --git a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java index e4ee5a2..31a4ef1 100644 --- a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java +++ b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java @@ -2,7 +2,6 @@ import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; -import com.hardworkers.hardworkers.init.ModBlockEntities; import com.hardworkers.hardworkers.init.ModEntities; import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; @@ -25,15 +24,34 @@ public class LumberjackBlock extends BaseEntityBlock { - public static final MapCodec CODEC = simpleCodec(LumberjackBlock::new); + // One codec per tier — simpleCodec only encodes Properties, so we capture + // the tier via a lambda for each variant. + public static final MapCodec WOOD_CODEC = simpleCodec(p -> new LumberjackBlock(LumberjackTier.WOOD, p)); + public static final MapCodec STONE_CODEC = simpleCodec(p -> new LumberjackBlock(LumberjackTier.STONE, p)); + public static final MapCodec IRON_CODEC = simpleCodec(p -> new LumberjackBlock(LumberjackTier.IRON, p)); + public static final MapCodec DIAMOND_CODEC = simpleCodec(p -> new LumberjackBlock(LumberjackTier.DIAMOND, p)); + public static final MapCodec NETHERITE_CODEC = simpleCodec(p -> new LumberjackBlock(LumberjackTier.NETHERITE, p)); - public LumberjackBlock(BlockBehaviour.Properties properties) { + private final LumberjackTier tier; + + public LumberjackBlock(LumberjackTier tier, BlockBehaviour.Properties properties) { super(properties); + this.tier = tier; + } + + public LumberjackTier getTier() { + return tier; } @Override public MapCodec codec() { - return CODEC; + return switch (tier) { + case WOOD -> WOOD_CODEC; + case STONE -> STONE_CODEC; + case IRON -> IRON_CODEC; + case DIAMOND -> DIAMOND_CODEC; + case NETHERITE -> NETHERITE_CODEC; + }; } // ------------------------------------------------------------------------- @@ -46,7 +64,6 @@ public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new LumberjackBlockEntity(pos, state); } - // Keep the normal cube model; BaseEntityBlock defaults to INVISIBLE. @Override public RenderShape getRenderShape(BlockState state) { return RenderShape.MODEL; @@ -72,11 +89,9 @@ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldS @Override public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { if (!level.isClientSide() && !state.is(newState.getBlock())) { - // Drop stored logs if (level.getBlockEntity(pos) instanceof LumberjackBlockEntity be) { Containers.dropContents(level, pos, be); } - // Discard the associated lumberjack entity AABB searchArea = new AABB(pos).inflate(3.0); level.getEntitiesOfClass(LumberjackEntity.class, searchArea) .stream() @@ -87,7 +102,7 @@ public void onRemove(BlockState state, Level level, BlockPos pos, BlockState new } // ------------------------------------------------------------------------- - // Interaction — right-click shows storage status in the action bar + // Interaction // ------------------------------------------------------------------------- @Override @@ -98,4 +113,15 @@ protected InteractionResult useWithoutItem(BlockState state, Level level, BlockP } return InteractionResult.sidedSuccess(level.isClientSide()); } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + public static BlockBehaviour.Properties baseProperties(MapColor color) { + return BlockBehaviour.Properties.of() + .mapColor(color) + .sound(SoundType.WOOD) + .strength(2.0f); + } } diff --git a/src/main/java/com/hardworkers/hardworkers/block/LumberjackTier.java b/src/main/java/com/hardworkers/hardworkers/block/LumberjackTier.java new file mode 100644 index 0000000..0a820f2 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/block/LumberjackTier.java @@ -0,0 +1,20 @@ +package com.hardworkers.hardworkers.block; + +public enum LumberjackTier { + + WOOD ("wood", 40), + STONE ("stone", 30), + IRON ("iron", 20), + DIAMOND ("diamond", 12), + NETHERITE("netherite", 6); + + /** Used in block/item registry names, e.g. "lumberjack_wood". */ + public final String id; + /** Ticks between breaking each log. Lower = faster. */ + public final int chopInterval; + + LumberjackTier(String id, int chopInterval) { + this.id = id; + this.chopInterval = chopInterval; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java index 638aa30..8dc0ada 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/ChopTreeGoal.java @@ -1,6 +1,6 @@ package com.hardworkers.hardworkers.entity.ai; -import com.hardworkers.hardworkers.HardWorkersConfig; +import com.hardworkers.hardworkers.block.LumberjackBlock; import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; import net.minecraft.core.BlockPos; @@ -81,7 +81,7 @@ public void tick() { } chopTimer++; - if (chopTimer >= HardWorkersConfig.LUMBERJACK_CHOP_INTERVAL.get()) { + if (chopTimer >= chopInterval()) { chopTimer = 0; chopNext(); } @@ -105,6 +105,15 @@ public void stop() { // ------------------------------------------------------------------------- + /** Returns the chop interval from the home block's tier, with a safe fallback. */ + private int chopInterval() { + BlockState home = lumberjack.level().getBlockState(lumberjack.getHomePosition()); + if (home.getBlock() instanceof LumberjackBlock lb) { + return lb.getTier().chopInterval; + } + return 20; + } + private void navigateTo(BlockPos pos) { lumberjack.getNavigation().moveTo( pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 1.0 diff --git a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java index 1b911fc..b863e78 100644 --- a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java +++ b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java @@ -12,7 +12,6 @@ import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent; -import net.neoforged.neoforge.items.wrapper.InvWrapper; @EventBusSubscriber(modid = HardWorkers.MODID, bus = EventBusSubscriber.Bus.MOD) public class ModEventSubscriber { @@ -25,14 +24,14 @@ public static void onEntityAttributeCreation(EntityAttributeCreationEvent event) @SubscribeEvent public static void onBuildCreativeTab(BuildCreativeModeTabContentsEvent event) { if (event.getTabKey() == CreativeModeTabs.FUNCTIONAL_BLOCKS) { - event.accept(ModItems.LUMBERJACK_BLOCK_ITEM); + event.accept(ModItems.LUMBERJACK_WOOD_ITEM); + event.accept(ModItems.LUMBERJACK_STONE_ITEM); + event.accept(ModItems.LUMBERJACK_IRON_ITEM); + event.accept(ModItems.LUMBERJACK_DIAMOND_ITEM); + event.accept(ModItems.LUMBERJACK_NETHERITE_ITEM); } } - /** - * Exposes the block entity's inventory as IItemHandler so that hoppers and - * pipes from other mods (e.g. Mekanism, Pipez) can extract logs from it. - */ @SubscribeEvent public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { event.registerBlockEntity( diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java index 59134b9..89e2496 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java @@ -14,7 +14,13 @@ public class ModBlockEntities { public static final DeferredHolder, BlockEntityType> LUMBERJACK_BLOCK_ENTITY = BLOCK_ENTITY_TYPES.register("lumberjack_block", () -> - BlockEntityType.Builder.of(LumberjackBlockEntity::new, ModBlocks.LUMBERJACK_BLOCK.get()) - .build(null) + BlockEntityType.Builder.of( + LumberjackBlockEntity::new, + ModBlocks.LUMBERJACK_WOOD.get(), + ModBlocks.LUMBERJACK_STONE.get(), + ModBlocks.LUMBERJACK_IRON.get(), + ModBlocks.LUMBERJACK_DIAMOND.get(), + ModBlocks.LUMBERJACK_NETHERITE.get() + ).build(null) ); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java index 0db0ab7..5e9ad48 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java @@ -2,8 +2,7 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.block.LumberjackBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.state.BlockBehaviour; +import com.hardworkers.hardworkers.block.LumberjackTier; import net.minecraft.world.level.material.MapColor; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; @@ -12,11 +11,23 @@ public class ModBlocks { public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(HardWorkers.MODID); - public static final DeferredBlock LUMBERJACK_BLOCK = - BLOCKS.register("lumberjack_block", () -> new LumberjackBlock( - BlockBehaviour.Properties.of() - .mapColor(MapColor.WOOD) - .sound(SoundType.WOOD) - .strength(2.0f) - )); + public static final DeferredBlock LUMBERJACK_WOOD = + BLOCKS.register("lumberjack_wood", + () -> new LumberjackBlock(LumberjackTier.WOOD, LumberjackBlock.baseProperties(MapColor.WOOD))); + + public static final DeferredBlock LUMBERJACK_STONE = + BLOCKS.register("lumberjack_stone", + () -> new LumberjackBlock(LumberjackTier.STONE, LumberjackBlock.baseProperties(MapColor.STONE))); + + public static final DeferredBlock LUMBERJACK_IRON = + BLOCKS.register("lumberjack_iron", + () -> new LumberjackBlock(LumberjackTier.IRON, LumberjackBlock.baseProperties(MapColor.METAL))); + + public static final DeferredBlock LUMBERJACK_DIAMOND = + BLOCKS.register("lumberjack_diamond", + () -> new LumberjackBlock(LumberjackTier.DIAMOND, LumberjackBlock.baseProperties(MapColor.DIAMOND))); + + public static final DeferredBlock LUMBERJACK_NETHERITE = + BLOCKS.register("lumberjack_netherite", + () -> new LumberjackBlock(LumberjackTier.NETHERITE, LumberjackBlock.baseProperties(MapColor.COLOR_BLACK))); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java index e96d0e9..e610350 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java @@ -9,6 +9,9 @@ public class ModItems { public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(HardWorkers.MODID); - public static final DeferredItem LUMBERJACK_BLOCK_ITEM = - ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_BLOCK); + public static final DeferredItem LUMBERJACK_WOOD_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_WOOD); + public static final DeferredItem LUMBERJACK_STONE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_STONE); + public static final DeferredItem LUMBERJACK_IRON_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_IRON); + public static final DeferredItem LUMBERJACK_DIAMOND_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_DIAMOND); + public static final DeferredItem LUMBERJACK_NETHERITE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_NETHERITE); } diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_block.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_block.json deleted file mode 100644 index a1d4a9a..0000000 --- a/src/main/resources/assets/hardworkers/blockstates/lumberjack_block.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "variants": { - "": { - "model": "hardworkers:block/lumberjack_block" - } - } -} diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json new file mode 100644 index 0000000..c87c7b2 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "hardworkers:block/lumberjack_diamond" } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json new file mode 100644 index 0000000..7fca2e1 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "hardworkers:block/lumberjack_iron" } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json new file mode 100644 index 0000000..6e6ea8b --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "hardworkers:block/lumberjack_netherite" } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json new file mode 100644 index 0000000..a15e40c --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "hardworkers:block/lumberjack_stone" } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json new file mode 100644 index 0000000..88b9597 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "hardworkers:block/lumberjack_wood" } + } +} diff --git a/src/main/resources/assets/hardworkers/lang/en_us.json b/src/main/resources/assets/hardworkers/lang/en_us.json index 2170903..dcc2d6f 100644 --- a/src/main/resources/assets/hardworkers/lang/en_us.json +++ b/src/main/resources/assets/hardworkers/lang/en_us.json @@ -1,4 +1,8 @@ { - "block.hardworkers.lumberjack_block": "Lumberjack Block", - "entity.hardworkers.lumberjack": "Lumberjack" + "block.hardworkers.lumberjack_wood": "Lumberjack Block (Wood)", + "block.hardworkers.lumberjack_stone": "Lumberjack Block (Stone)", + "block.hardworkers.lumberjack_iron": "Lumberjack Block (Iron)", + "block.hardworkers.lumberjack_diamond": "Lumberjack Block (Diamond)", + "block.hardworkers.lumberjack_netherite": "Lumberjack Block (Netherite)", + "entity.hardworkers.lumberjack": "Lumberjack" } diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_block.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_block.json deleted file mode 100644 index 56c1209..0000000 --- a/src/main/resources/assets/hardworkers/models/block/lumberjack_block.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "hardworkers:block/lumberjack_block" - } -} diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json new file mode 100644 index 0000000..c76034f --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { "all": "hardworkers:block/lumberjack_diamond" } +} diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json new file mode 100644 index 0000000..7df97c4 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { "all": "hardworkers:block/lumberjack_iron" } +} diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json new file mode 100644 index 0000000..53610d9 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { "all": "hardworkers:block/lumberjack_netherite" } +} diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json new file mode 100644 index 0000000..9fe1f64 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { "all": "hardworkers:block/lumberjack_stone" } +} diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json new file mode 100644 index 0000000..e2d8749 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json @@ -0,0 +1,4 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { "all": "hardworkers:block/lumberjack_wood" } +} diff --git a/src/main/resources/assets/hardworkers/models/item/lumberjack_block.json b/src/main/resources/assets/hardworkers/models/item/lumberjack_block.json deleted file mode 100644 index 0384ee3..0000000 --- a/src/main/resources/assets/hardworkers/models/item/lumberjack_block.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "hardworkers:block/lumberjack_block" -} diff --git a/src/main/resources/assets/hardworkers/models/item/lumberjack_diamond.json b/src/main/resources/assets/hardworkers/models/item/lumberjack_diamond.json new file mode 100644 index 0000000..d25bdea --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/lumberjack_diamond.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/lumberjack_diamond" +} diff --git a/src/main/resources/assets/hardworkers/models/item/lumberjack_iron.json b/src/main/resources/assets/hardworkers/models/item/lumberjack_iron.json new file mode 100644 index 0000000..cbbcbdf --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/lumberjack_iron.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/lumberjack_iron" +} diff --git a/src/main/resources/assets/hardworkers/models/item/lumberjack_netherite.json b/src/main/resources/assets/hardworkers/models/item/lumberjack_netherite.json new file mode 100644 index 0000000..fae465d --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/lumberjack_netherite.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/lumberjack_netherite" +} diff --git a/src/main/resources/assets/hardworkers/models/item/lumberjack_stone.json b/src/main/resources/assets/hardworkers/models/item/lumberjack_stone.json new file mode 100644 index 0000000..527a40d --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/lumberjack_stone.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/lumberjack_stone" +} diff --git a/src/main/resources/assets/hardworkers/models/item/lumberjack_wood.json b/src/main/resources/assets/hardworkers/models/item/lumberjack_wood.json new file mode 100644 index 0000000..9de19ac --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/lumberjack_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/lumberjack_wood" +} diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..777b74b3920264ab0e1a89913fc9dd14621c5c66 GIT binary patch literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|VxBIJAr*6yId+t^s~j;{l^AmP c!&L@`DM$VZaGm6_0jgl|boFyt=akR{0Gmh@qW}N^ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_iron.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_iron.png new file mode 100644 index 0000000000000000000000000000000000000000..947e696834e7d73cf4f2c6223d1b4b17fc1f8e8e GIT binary patch literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|VxBIJAr*6yZ)|#M;MtKFa(G4C c!_|xof6r^n--(>I8mNN7)78&qol`;+0Lt?gmjD0& literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite.png new file mode 100644 index 0000000000000000000000000000000000000000..b55aab4161f2f80c27cfdab4332047c015ee7a13 GIT binary patch literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|VxBIJAr*6y4X&4}a2obxNnACk bF=b#_e`P77)a{L|Kotz0u6{1-oD!M Date: Fri, 1 May 2026 05:52:16 +0000 Subject: [PATCH 08/17] Prevent multiple lumberjacks from targeting the same tree FindTreeGoal now queries all other LumberjackEntity instances in the search area and skips any tree-base position they already have claimed, so each worker independently selects a distinct tree. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../hardworkers/entity/ai/FindTreeGoal.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java index 84d297c..4ba45c3 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/FindTreeGoal.java @@ -9,8 +9,11 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; import java.util.EnumSet; +import java.util.HashSet; +import java.util.Set; /** * Scans the configurable area around the lumberjack's home block for the base @@ -66,6 +69,8 @@ private BlockPos findNearestTreeBase() { BlockPos home = lumberjack.getHomePosition(); int radius = HardWorkersConfig.LUMBERJACK_SEARCH_RADIUS.get(); + Set claimed = getClaimedTrees(level, home, radius); + BlockPos nearest = null; double nearestDistSq = Double.MAX_VALUE; @@ -73,8 +78,9 @@ private BlockPos findNearestTreeBase() { for (int z = -radius; z <= radius; z++) { for (int y = -radius / 2; y <= radius / 2; y++) { BlockPos candidate = home.offset(x, y, z); - BlockState state = level.getBlockState(candidate); + if (claimed.contains(candidate)) continue; + BlockState state = level.getBlockState(candidate); if (state.is(BlockTags.LOGS) && isBaseLog(level, candidate)) { double distSq = home.distSqr(candidate); if (distSq < nearestDistSq) { @@ -88,6 +94,18 @@ private BlockPos findNearestTreeBase() { return nearest; } + /** Returns the set of tree-base positions already claimed by other lumberjacks in the area. */ + private Set getClaimedTrees(Level level, BlockPos home, int radius) { + AABB searchBox = new AABB( + home.getX() - radius, home.getY() - radius / 2.0, home.getZ() - radius, + home.getX() + radius, home.getY() + radius / 2.0, home.getZ() + radius + ); + Set claimed = new HashSet<>(); + level.getEntitiesOfClass(LumberjackEntity.class, searchBox, e -> e != lumberjack) + .forEach(e -> { if (e.getTargetTree() != null) claimed.add(e.getTargetTree()); }); + return claimed; + } + /** A base log is a log block that does not have another log directly below it. */ private boolean isBaseLog(Level level, BlockPos pos) { return !level.getBlockState(pos.below()).is(BlockTags.LOGS); From e946d7c8cd615ff08c9d73b437f95d400a4fba1f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 06:13:42 +0000 Subject: [PATCH 09/17] Add directional block facing and per-face texture templates - LumberjackBlock gains a FACING (horizontal) block state so it orients toward the player when placed, and supports rotate/mirror for structure blocks and data packs - Blockstate JSONs updated to 4 facing variants (0/90/180/270 y-rotation) - Block models changed from cube_all to cube with distinct textures for front, back, top, bottom, and side faces - Generated 25 block face templates (16x16 per face per tier) and a 64x64 entity skin template following the standard Minecraft UV layout; edit these in any pixel editor (Aseprite, GIMP, Pixelorama, etc.) - gen_textures.py included at project root to regenerate templates https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- gen_textures.py | 255 ++++++++++++++++++ .../hardworkers/block/LumberjackBlock.java | 32 ++- .../blockstates/lumberjack_diamond.json | 5 +- .../blockstates/lumberjack_iron.json | 5 +- .../blockstates/lumberjack_netherite.json | 5 +- .../blockstates/lumberjack_stone.json | 5 +- .../blockstates/lumberjack_wood.json | 5 +- .../models/block/lumberjack_diamond.json | 12 +- .../models/block/lumberjack_iron.json | 12 +- .../models/block/lumberjack_netherite.json | 12 +- .../models/block/lumberjack_stone.json | 12 +- .../models/block/lumberjack_wood.json | 12 +- .../textures/block/lumberjack_diamond.png | Bin 79 -> 0 bytes .../block/lumberjack_diamond_back.png | Bin 0 -> 121 bytes .../block/lumberjack_diamond_bottom.png | Bin 0 -> 90 bytes .../block/lumberjack_diamond_front.png | Bin 0 -> 109 bytes .../block/lumberjack_diamond_side.png | Bin 0 -> 90 bytes .../textures/block/lumberjack_diamond_top.png | Bin 0 -> 102 bytes .../textures/block/lumberjack_iron.png | Bin 79 -> 0 bytes .../textures/block/lumberjack_iron_back.png | Bin 0 -> 122 bytes .../textures/block/lumberjack_iron_bottom.png | Bin 0 -> 91 bytes .../textures/block/lumberjack_iron_front.png | Bin 0 -> 110 bytes .../textures/block/lumberjack_iron_side.png | Bin 0 -> 91 bytes .../textures/block/lumberjack_iron_top.png | Bin 0 -> 102 bytes .../textures/block/lumberjack_netherite.png | Bin 79 -> 0 bytes .../block/lumberjack_netherite_back.png | Bin 0 -> 121 bytes .../block/lumberjack_netherite_bottom.png | Bin 0 -> 89 bytes .../block/lumberjack_netherite_front.png | Bin 0 -> 110 bytes .../block/lumberjack_netherite_side.png | Bin 0 -> 90 bytes .../block/lumberjack_netherite_top.png | Bin 0 -> 101 bytes .../textures/block/lumberjack_stone.png | Bin 77 -> 0 bytes .../textures/block/lumberjack_stone_back.png | Bin 0 -> 121 bytes .../block/lumberjack_stone_bottom.png | Bin 0 -> 90 bytes .../textures/block/lumberjack_stone_front.png | Bin 0 -> 110 bytes .../textures/block/lumberjack_stone_side.png | Bin 0 -> 90 bytes .../textures/block/lumberjack_stone_top.png | Bin 0 -> 102 bytes .../textures/block/lumberjack_wood.png | Bin 79 -> 0 bytes .../textures/block/lumberjack_wood_back.png | Bin 0 -> 121 bytes .../textures/block/lumberjack_wood_bottom.png | Bin 0 -> 89 bytes .../textures/block/lumberjack_wood_front.png | Bin 0 -> 110 bytes .../textures/block/lumberjack_wood_side.png | Bin 0 -> 90 bytes .../textures/block/lumberjack_wood_top.png | Bin 0 -> 102 bytes .../textures/entity/lumberjack.png | Bin 180 -> 331 bytes 43 files changed, 356 insertions(+), 16 deletions(-) create mode 100644 gen_textures.py delete mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_top.png delete mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_iron.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_top.png delete mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_top.png delete mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_stone.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_top.png delete mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_wood.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_top.png diff --git a/gen_textures.py b/gen_textures.py new file mode 100644 index 0000000..00fceb9 --- /dev/null +++ b/gen_textures.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +""" +Generate template textures for the Hard Workers mod. + +Block textures: 16x16, one per face per tier (front / back / top / bottom / side) +Entity texture: 64x64 standard Minecraft skin layout (edit this in any pixel editor) + +Run from the project root: python3 gen_textures.py +""" + +import struct, zlib, os + +# --------------------------------------------------------------------------- +# Minimal PNG writer (no Pillow required) +# --------------------------------------------------------------------------- + +def _chunk(tag, data): + crc = zlib.crc32(tag + data) & 0xFFFFFFFF + return struct.pack(">I", len(data)) + tag + data + struct.pack(">I", crc) + +def write_png(path, w, h, pixels): + """pixels: flat list of (r,g,b,a) tuples, row-major.""" + raw = b"".join( + b"\x00" + b"".join(bytes(pixels[y * w + x]) for x in range(w)) + for y in range(h) + ) + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "wb") as f: + f.write(b"\x89PNG\r\n\x1a\n") + f.write(_chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 6, 0, 0, 0))) + f.write(_chunk(b"IDAT", zlib.compress(raw))) + f.write(_chunk(b"IEND", b"")) + print(f" wrote {path}") + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def clamp(v): return max(0, min(255, v)) +def shade(c, d): return tuple(clamp(v + d) for v in c) + +def fill(pixels, w, x, y, fw, fh, color): + for py in range(y, y + fh): + for px in range(x, x + fw): + if 0 <= px < w and 0 <= py < len(pixels) // w: + pixels[py * w + px] = color + +# --------------------------------------------------------------------------- +# Block face templates (16x16) +# --------------------------------------------------------------------------- + +TIERS = { + "wood": (139, 100, 40), + "stone": (115, 115, 115), + "iron": (175, 180, 190), + "diamond": ( 45, 195, 210), + "netherite": ( 58, 28, 75), +} + +def block_face(base, variant): + r, g, b = base + pix = [] + for y in range(16): + for x in range(16): + border = x == 0 or x == 15 or y == 0 or y == 15 + dark = shade(base, -45) + if variant == "front": + # two glowing "eye" pixels, dark border, small mouth line + left_eye = (x in (4, 5) and y in (5, 6)) + right_eye = (x in (10, 11) and y in (5, 6)) + mouth = (5 <= x <= 10 and y == 11) + if border: + pix.append(dark + (255,)) + elif left_eye or right_eye: + pix.append((220, 220, 20, 255)) # yellow "eyes" + elif mouth: + pix.append(dark + (255,)) + else: + pix.append((r, g, b, 255)) + elif variant == "top": + light = shade(base, +35) + # cross marker so you can tell which way is up + cross = (x == 8 or y == 8) + if border: + pix.append(shade(base, -20) + (255,)) + elif cross: + pix.append(shade(base, +55) + (255,)) + else: + pix.append(light + (255,)) + elif variant == "bottom": + if border: + pix.append(shade(base, -65) + (255,)) + else: + pix.append(shade(base, -40) + (255,)) + elif variant == "back": + # diagonal stripe to distinguish back from side + stripe = ((x + y) % 6 == 0) + if border: + pix.append(dark + (255,)) + elif stripe: + pix.append(shade(base, -25) + (255,)) + else: + pix.append(shade(base, +10) + (255,)) + else: # side + if border: + pix.append(dark + (255,)) + else: + pix.append((r, g, b, 255)) + return pix + +BLOCK_TEX = "src/main/resources/assets/hardworkers/textures/block" + +for tier, color in TIERS.items(): + for face in ("front", "back", "top", "bottom", "side"): + write_png(f"{BLOCK_TEX}/lumberjack_{tier}_{face}.png", 16, 16, + block_face(color, face)) + +# --------------------------------------------------------------------------- +# Entity skin template (64x64, standard Minecraft player UV layout) +# +# Edit lumberjack.png in any pixel editor. UV regions: +# +# ┌─────────────────────────────────────────────────────────────────────────┐ +# │ HEAD │ HEAD OVERLAY (hat / hair) │ +# │ top (8,0 8×8) │ top (40,0 8×8) │ +# │ bot (16,0 8×8) │ bot (48,0 8×8) │ +# │ right (0,8 8×8) │ right (32,8 8×8) │ +# │ front (8,8 8×8) ← face │ front (40,8 8×8) ← hat brim │ +# │ left (16,8 8×8) │ left (48,8 8×8) │ +# │ back (24,8 8×8) │ back (56,8 8×8) │ +# ├─────────────────────────────────────────────────────────────────────────┤ +# │ RIGHT LEG │ BODY │ RIGHT ARM │ +# │ top (4,16 4×4) │ top (20,16 8×4) │ top (44,16 4×4) │ +# │ bot (8,16 4×4) │ bot (28,16 8×4) │ bot (48,16 4×4) │ +# │ r (0,20 4×12)│ r (16,20 4×12) │ r (40,20 4×12) │ +# │ f (4,20 4×12)│ f (20,20 8×12) ←shirt│ f (44,20 4×12) │ +# │ l (8,20 4×12)│ l (28,20 4×12) │ l (48,20 4×12) │ +# │ b (12,20 4×12)│ b (32,20 8×12) │ b (52,20 4×12) │ +# ├─────────────────────────────────────────────────────────────────────────┤ +# │ LEFT LEG (new-format 64px skin) │ LEFT ARM │ +# │ top (20,48 4×4) │ top (36,48 4×4) │ +# │ bot (24,48 4×4) │ bot (40,48 4×4) │ +# │ r (16,52 4×12) │ r (32,52 4×12) │ +# │ f (20,52 4×12) │ f (36,52 4×12) │ +# │ l (24,52 4×12) │ l (40,52 4×12) │ +# │ b (28,52 4×12) │ b (44,52 4×12) │ +# └─────────────────────────────────────────────────────────────────────────┘ +# --------------------------------------------------------------------------- + +SKIN = (200, 150, 100, 255) # face / hands +SKIN2 = (185, 135, 88, 255) # face side / darker skin +BEARD = ( 90, 55, 20, 255) # beard / hair +EYE = ( 40, 30, 20, 255) +HAT_F = ( 55, 90, 35, 255) # green logger hat – front +HAT_S = ( 45, 75, 28, 255) # hat sides +HAT_T = ( 65, 105, 42, 255) # hat top +SHIRT_F = ( 70, 90, 150, 255) # blue shirt front +SHIRT_S = ( 55, 72, 125, 255) # shirt sides / back +SUSP = ( 95, 65, 25, 255) # brown suspenders +PANTS_F = ( 45, 60, 100, 255) # dark trousers +PANTS_S = ( 38, 50, 85, 255) +BOOT = ( 48, 34, 18, 255) # leather boots +BOOT2 = ( 38, 26, 12, 255) +TRANS = ( 0, 0, 0, 0) # transparent (unused skin area) + +W = H = 64 +pixels = [TRANS] * (W * H) + +def f(x, y, fw, fh, color): + fill(pixels, W, x, y, fw, fh, color) + +# ── HEAD ──────────────────────────────────────────────────────────────────── +f( 8, 0, 8, 8, SKIN) # head top +f(16, 0, 8, 8, SKIN2) # head bottom +f( 0, 8, 8, 8, SKIN2) # head right +f( 8, 8, 8, 8, SKIN) # head front ← paint the face here +f(16, 8, 8, 8, SKIN2) # head left +f(24, 8, 8, 8, SKIN2) # head back +# face details on front (8,8) +f(10,10, 2, 2, EYE) # left eye +f(13,10, 2, 2, EYE) # right eye +f( 9,13, 6, 2, BEARD) # beard + +# ── HAT (head overlay) ────────────────────────────────────────────────────── +f(40, 0, 8, 8, HAT_T) # hat top +f(48, 0, 8, 8, HAT_S) # hat bottom inside +f(32, 8, 8, 8, HAT_S) # hat right +f(40, 8, 8, 8, HAT_F) # hat front ← wide brim suggestion +f(48, 8, 8, 8, HAT_S) # hat left +f(56, 8, 8, 8, HAT_S) # hat back + +# ── BODY ──────────────────────────────────────────────────────────────────── +f(20,16, 8, 4, SHIRT_S) # body top +f(28,16, 8, 4, SHIRT_S) # body bottom +f(16,20, 4,12, SHIRT_S) # body right +f(20,20, 8,12, SHIRT_F) # body front ← shirt +f(28,20, 4,12, SHIRT_S) # body left +f(32,20, 8,12, SHIRT_S) # body back +# suspenders drawn over shirt front +f(21,20, 1,12, SUSP) +f(26,20, 1,12, SUSP) + +# ── RIGHT LEG ─────────────────────────────────────────────────────────────── +f( 4,16, 4, 4, PANTS_S) # top +f( 8,16, 4, 4, PANTS_S) # bottom +f( 0,20, 4,12, PANTS_S) # right +f( 4,20, 4,12, PANTS_F) # front +f( 8,20, 4,12, PANTS_S) # left +f(12,20, 4,12, PANTS_S) # back +f( 0,28, 4, 4, BOOT2) # boot right +f( 4,28, 4, 4, BOOT) # boot front +f( 8,28, 4, 4, BOOT2) # boot left +f(12,28, 4, 4, BOOT2) # boot back + +# ── LEFT LEG ──────────────────────────────────────────────────────────────── +f(20,48, 4, 4, PANTS_S) +f(24,48, 4, 4, PANTS_S) +f(16,52, 4,12, PANTS_S) +f(20,52, 4,12, PANTS_F) +f(24,52, 4,12, PANTS_S) +f(28,52, 4,12, PANTS_S) +f(16,60, 4, 4, BOOT2) +f(20,60, 4, 4, BOOT) +f(24,60, 4, 4, BOOT2) +f(28,60, 4, 4, BOOT2) + +# ── RIGHT ARM ─────────────────────────────────────────────────────────────── +f(44,16, 4, 4, SHIRT_S) # top +f(48,16, 4, 4, SHIRT_S) # bottom +f(40,20, 4,12, SHIRT_S) # right +f(44,20, 4,12, SHIRT_F) # front +f(48,20, 4,12, SHIRT_S) # left +f(52,20, 4,12, SHIRT_S) # back +f(40,28, 4, 4, SKIN2) # hand right +f(44,28, 4, 4, SKIN) # hand front +f(48,28, 4, 4, SKIN2) # hand left +f(52,28, 4, 4, SKIN2) # hand back + +# ── LEFT ARM ──────────────────────────────────────────────────────────────── +f(36,48, 4, 4, SHIRT_S) +f(40,48, 4, 4, SHIRT_S) +f(32,52, 4,12, SHIRT_S) +f(36,52, 4,12, SHIRT_F) +f(40,52, 4,12, SHIRT_S) +f(44,52, 4,12, SHIRT_S) +f(32,60, 4, 4, SKIN2) +f(36,60, 4, 4, SKIN) +f(40,60, 4, 4, SKIN2) +f(44,60, 4, 4, SKIN2) + +write_png("src/main/resources/assets/hardworkers/textures/entity/lumberjack.png", + 64, 64, pixels) + +print("\nDone! Edit the PNGs in any pixel editor (e.g. Aseprite, GIMP, Pixelorama).") +print("Block textures are 16x16; the entity skin is 64x64 (standard Minecraft layout).") diff --git a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java index 31a4ef1..fe4c535 100644 --- a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java +++ b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java @@ -5,17 +5,24 @@ import com.hardworkers.hardworkers.init.ModEntities; import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; +import net.minecraft.core.Direction; import net.minecraft.world.Containers; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.level.material.MapColor; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; @@ -24,6 +31,8 @@ public class LumberjackBlock extends BaseEntityBlock { + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + // One codec per tier — simpleCodec only encodes Properties, so we capture // the tier via a lambda for each variant. public static final MapCodec WOOD_CODEC = simpleCodec(p -> new LumberjackBlock(LumberjackTier.WOOD, p)); @@ -37,6 +46,27 @@ public class LumberjackBlock extends BaseEntityBlock { public LumberjackBlock(LumberjackTier tier, BlockBehaviour.Properties properties) { super(properties); this.tier = tier; + registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); } public LumberjackTier getTier() { diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json index c87c7b2..395444a 100644 --- a/src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_diamond.json @@ -1,5 +1,8 @@ { "variants": { - "": { "model": "hardworkers:block/lumberjack_diamond" } + "facing=north": { "model": "hardworkers:block/lumberjack_diamond" }, + "facing=east": { "model": "hardworkers:block/lumberjack_diamond", "y": 90 }, + "facing=south": { "model": "hardworkers:block/lumberjack_diamond", "y": 180 }, + "facing=west": { "model": "hardworkers:block/lumberjack_diamond", "y": 270 } } } diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json index 7fca2e1..6d98702 100644 --- a/src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_iron.json @@ -1,5 +1,8 @@ { "variants": { - "": { "model": "hardworkers:block/lumberjack_iron" } + "facing=north": { "model": "hardworkers:block/lumberjack_iron" }, + "facing=east": { "model": "hardworkers:block/lumberjack_iron", "y": 90 }, + "facing=south": { "model": "hardworkers:block/lumberjack_iron", "y": 180 }, + "facing=west": { "model": "hardworkers:block/lumberjack_iron", "y": 270 } } } diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json index 6e6ea8b..e9f0116 100644 --- a/src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_netherite.json @@ -1,5 +1,8 @@ { "variants": { - "": { "model": "hardworkers:block/lumberjack_netherite" } + "facing=north": { "model": "hardworkers:block/lumberjack_netherite" }, + "facing=east": { "model": "hardworkers:block/lumberjack_netherite", "y": 90 }, + "facing=south": { "model": "hardworkers:block/lumberjack_netherite", "y": 180 }, + "facing=west": { "model": "hardworkers:block/lumberjack_netherite", "y": 270 } } } diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json index a15e40c..3993453 100644 --- a/src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_stone.json @@ -1,5 +1,8 @@ { "variants": { - "": { "model": "hardworkers:block/lumberjack_stone" } + "facing=north": { "model": "hardworkers:block/lumberjack_stone" }, + "facing=east": { "model": "hardworkers:block/lumberjack_stone", "y": 90 }, + "facing=south": { "model": "hardworkers:block/lumberjack_stone", "y": 180 }, + "facing=west": { "model": "hardworkers:block/lumberjack_stone", "y": 270 } } } diff --git a/src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json b/src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json index 88b9597..0380024 100644 --- a/src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json +++ b/src/main/resources/assets/hardworkers/blockstates/lumberjack_wood.json @@ -1,5 +1,8 @@ { "variants": { - "": { "model": "hardworkers:block/lumberjack_wood" } + "facing=north": { "model": "hardworkers:block/lumberjack_wood" }, + "facing=east": { "model": "hardworkers:block/lumberjack_wood", "y": 90 }, + "facing=south": { "model": "hardworkers:block/lumberjack_wood", "y": 180 }, + "facing=west": { "model": "hardworkers:block/lumberjack_wood", "y": 270 } } } diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json index c76034f..75a3d49 100644 --- a/src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_diamond.json @@ -1,4 +1,12 @@ { - "parent": "minecraft:block/cube_all", - "textures": { "all": "hardworkers:block/lumberjack_diamond" } + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/lumberjack_diamond_front", + "up": "hardworkers:block/lumberjack_diamond_top", + "down": "hardworkers:block/lumberjack_diamond_bottom", + "north": "hardworkers:block/lumberjack_diamond_front", + "south": "hardworkers:block/lumberjack_diamond_back", + "east": "hardworkers:block/lumberjack_diamond_side", + "west": "hardworkers:block/lumberjack_diamond_side" + } } diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json index 7df97c4..8af24bd 100644 --- a/src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_iron.json @@ -1,4 +1,12 @@ { - "parent": "minecraft:block/cube_all", - "textures": { "all": "hardworkers:block/lumberjack_iron" } + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/lumberjack_iron_front", + "up": "hardworkers:block/lumberjack_iron_top", + "down": "hardworkers:block/lumberjack_iron_bottom", + "north": "hardworkers:block/lumberjack_iron_front", + "south": "hardworkers:block/lumberjack_iron_back", + "east": "hardworkers:block/lumberjack_iron_side", + "west": "hardworkers:block/lumberjack_iron_side" + } } diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json index 53610d9..f9108c9 100644 --- a/src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_netherite.json @@ -1,4 +1,12 @@ { - "parent": "minecraft:block/cube_all", - "textures": { "all": "hardworkers:block/lumberjack_netherite" } + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/lumberjack_netherite_front", + "up": "hardworkers:block/lumberjack_netherite_top", + "down": "hardworkers:block/lumberjack_netherite_bottom", + "north": "hardworkers:block/lumberjack_netherite_front", + "south": "hardworkers:block/lumberjack_netherite_back", + "east": "hardworkers:block/lumberjack_netherite_side", + "west": "hardworkers:block/lumberjack_netherite_side" + } } diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json index 9fe1f64..dc6abdd 100644 --- a/src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_stone.json @@ -1,4 +1,12 @@ { - "parent": "minecraft:block/cube_all", - "textures": { "all": "hardworkers:block/lumberjack_stone" } + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/lumberjack_stone_front", + "up": "hardworkers:block/lumberjack_stone_top", + "down": "hardworkers:block/lumberjack_stone_bottom", + "north": "hardworkers:block/lumberjack_stone_front", + "south": "hardworkers:block/lumberjack_stone_back", + "east": "hardworkers:block/lumberjack_stone_side", + "west": "hardworkers:block/lumberjack_stone_side" + } } diff --git a/src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json b/src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json index e2d8749..97dc63d 100644 --- a/src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json +++ b/src/main/resources/assets/hardworkers/models/block/lumberjack_wood.json @@ -1,4 +1,12 @@ { - "parent": "minecraft:block/cube_all", - "textures": { "all": "hardworkers:block/lumberjack_wood" } + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/lumberjack_wood_front", + "up": "hardworkers:block/lumberjack_wood_top", + "down": "hardworkers:block/lumberjack_wood_bottom", + "north": "hardworkers:block/lumberjack_wood_front", + "south": "hardworkers:block/lumberjack_wood_back", + "east": "hardworkers:block/lumberjack_wood_side", + "west": "hardworkers:block/lumberjack_wood_side" + } } diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond.png deleted file mode 100644 index 777b74b3920264ab0e1a89913fc9dd14621c5c66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|VxBIJAr*6yId+t^s~j;{l^AmP c!&L@`DM$VZaGm6_0jgl|boFyt=akR{0Gmh@qW}N^ diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_back.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_back.png new file mode 100644 index 0000000000000000000000000000000000000000..eb0b1a1427150fb255462fdfe3d0726fdd7bd63c GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`4xTQKAr*6y6J~7tV(%*9GGqSU z<|fy$w?_mHtMy%FNHR?}Z4fyt!8T!)qza#TgUnfr!wn+udyXB-&{-hiz{$pNvSD>_ U&5M)lK(iP;UHx3vIVCg!02-(!=Kufz literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_bottom.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..bb3518f66cf7eea2b207efac8fa3c30602cb3aee GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6y6EbG~n6Jd66p_AO niMwCQAnU*i#w%tGLOcu)mf2Xl`%Rq$RLtP%>gTe~DWM4fp%WMz literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_front.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_diamond_front.png new file mode 100644 index 0000000000000000000000000000000000000000..bf7fea8f0f479a6ddefdfe8eafa8d34e922cfa24 GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`CY~;iAr*6y6J~7tV(%*9@@dmJ_~PDx<+BROxOt=LURpne8VS3j3^ HP6cxAr*6y6J~7tV(%*9@I8mNN7)78&qol`;+0Lt?gmjD0& diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_back.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8dcf9ca61685dc9d1b22454aef30ee5e3e82457b GIT binary patch literal 122 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`j-D=#Ar*6yGqUFYm@jmOL8(80SAo$ky o@`*!>*?Oc5vKY4VE?{A1kSU$k?UQ4g22{=9>FVdQ&MBb@0MAVrb^rhX literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_front.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_iron_front.png new file mode 100644 index 0000000000000000000000000000000000000000..bf17d649035d30533c8008803a46bec3ac101a17 GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yGqUFYm@jmOLFrol zO?NT5b!>NbDl;8>(Y3Mj^09_S{^o;;4C_*?TlP3?Vw{q|V6y4g%I&e;oFVdQ&MBb@0F_@L A=l}o! literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite.png deleted file mode 100644 index b55aab4161f2f80c27cfdab4332047c015ee7a13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|VxBIJAr*6y4X&4}a2obxNnACk bF=b#_e`P77)a{L|Kotz0u6{1-oD!MGAr*6y6A~W$@|R>2a^g9a m@-UFujaS07;TBs!6XUgb%|HJ+g`NP_GI+ZBxvX2TnGI;3r*wCdKUf4>W+m)78&q Iol`;+08X+Y?EnA( literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_side.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_side.png new file mode 100644 index 0000000000000000000000000000000000000000..99b8afe6a9bf13cbbd596c3313e023c5a86e09ec GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6yAEZwB?a!IUAQ)zJ lFXdq%vm393X~QkHfF{Nrk}Vlxd3+$n44$rjF6*2UngE%D7fS#D literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_top.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_netherite_top.png new file mode 100644 index 0000000000000000000000000000000000000000..fc47ef420d1e9a7630810c838d83733e47e1cadf GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`I-V|$Ar*6y10Eav_vcJw5Zw5! y(xNc2K-h0*AT!%kMg`X=4;-Jg1~O}$KF8oc{lyM5fxodpeGHzielF{r5}E)3+a4GI literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone.png deleted file mode 100644 index 56c8e45ca4b6c7151bf0d00f8cc5dd897178c80b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|BAzaeAr*6yGZX|3da@+08q}CF ZFsQaP2;}S*`vR0_@O1TaS?83{1OO_N5nTWP diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_back.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_back.png new file mode 100644 index 0000000000000000000000000000000000000000..dd1b0b1eefab738e6e54df7c1da221a2343d4e93 GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`4xTQKAr*6y3yO<>`*Wr-2yWfF zRl`C{i_4j3wnZ3o$JL&z3_&Kz5)P}!yL57Sc-e`#)c#YRSAZttxmxX TFPEkP&0_F$^>bP0l+XkKO;;lr literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_bottom.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1fa389d583277d7b0f978bfbf5816aac604ece GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6y4UCL_`E#Z*2;RGQ nFXdq%vm393X~QkHfF{QLIp=B^PUTnu6*G9c`njxgN@xNAo+}tq literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_front.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_front.png new file mode 100644 index 0000000000000000000000000000000000000000..961f8b3de0e2750621cb9b9f86d1687576b8facf GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6y3yO<>`*Wr-2p&Cp zH09yLtOIkcl@fZ^NzAs$<&pSdAR)u{z-ycH32qf>2TnGI%#Ri?Qx-|&0}WvCboFyt I=akR{08UmQ`v3p{ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_side.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_side.png new file mode 100644 index 0000000000000000000000000000000000000000..f744aea826847e465e8f7e3abc71d271a8281ae7 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6y3yO<>`*Wr-2p&Cp nH05C+vm393X~QkHfF{NVYb~m6AM2k1DrWF>^>bP0l+XkKzPK4+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_top.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_stone_top.png new file mode 100644 index 0000000000000000000000000000000000000000..5fa995b0039ee5391f657f1c171db37b5efaad47 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`x}GkMAr*6yJNo(hR=9>uV726G#w8iY3>retDirQue3EfYl5I&vx`~)uGUHJOF9U{xMU3@7 T|Gw1(n#JJh>gTe~DWM4fizg(s literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_bottom.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..3e60a95c60e11573f18551f80a95bf0448f1c53a GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3Z5>GAr*6yKLn-y@aIfp5Ikx2 m%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_side.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_side.png new file mode 100644 index 0000000000000000000000000000000000000000..a56e95341f6a0ee9303100ef3a6aede1f73e0503 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6yJB-tR_;aQ)2;S9{ nN_iN_?8Yl$+Hi|4poua2)b|;Z{|+?(6*G9c`njxgN@xNAmp>T1 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_top.png b/src/main/resources/assets/hardworkers/textures/block/lumberjack_wood_top.png new file mode 100644 index 0000000000000000000000000000000000000000..cb3124fc063698f47b7b96565cd769fadc0cee4f GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`x}GkMAr*6yHTVU7`g5i+2)@3x zxA2kk=EsGndZi3j@Gxj9vGXhG_eeQJma#GHSsr(Mk@&kPphgByS3j3^P6OTYs-Ps|}BBbUy#peCg&tD6zD_5^w zzjVi%IqPpGK3eZxqIYY*Rf}@eH|R4?yS!!HA3g^ihQRww zsq^Y|7&bC|`6V_XGN|jV@li-ip{QBmG(8@S7lZJ0a%l76l0_9o+oH!K0M51ujj$i$@ zGng0{OeQk6NUZr&@&Tj*3|g2C_` Date: Fri, 1 May 2026 13:58:57 +0000 Subject: [PATCH 10/17] Add 5-tier Miner worker block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Places a MinerEntity that digs a 3-block-tall tunnel (Y, Y+1, Y+2) in the block's facing direction, up to 64 blocks deep. All mined blocks are deposited into the block's 27-slot storage; hoppers and mod pipes can extract from it. Tier restrictions (ore blocks left in place if tier is too low): Wood – skips NEEDS_STONE_TOOL, NEEDS_IRON_TOOL, NEEDS_DIAMOND_TOOL Stone – skips NEEDS_IRON_TOOL, NEEDS_DIAMOND_TOOL Iron – skips NEEDS_DIAMOND_TOOL Diamond / Netherite – mines everything except bedrock Mining intervals match lumberjack: 40 / 30 / 20 / 12 / 6 ticks per block. Progress (currentDepth) is persisted in entity NBT across world restarts. Recipes use pickaxes instead of axes, chained through previous tier: Wood: planks + wooden_pickaxe Stone–Netherite: planks + pickaxe + previous miner block Includes 26 block texture templates (16x16, 5 tiers × 5 faces + entity skin 64x64) and full blockstate/model/recipe/loot-table resource files. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- gen_textures.py | 167 +++++++++++++++++- .../hardworkers/block/MinerBlock.java | 137 ++++++++++++++ .../hardworkers/block/MinerTier.java | 17 ++ .../blockentity/MinerBlockEntity.java | 97 ++++++++++ .../hardworkers/client/ClientEvents.java | 2 + .../hardworkers/entity/MinerEntity.java | 74 ++++++++ .../entity/ai/MineForwardGoal.java | 165 +++++++++++++++++ .../entity/client/MinerRenderer.java | 23 +++ .../events/ModEventSubscriber.java | 12 ++ .../hardworkers/init/ModBlockEntities.java | 14 ++ .../hardworkers/init/ModBlocks.java | 23 +++ .../hardworkers/init/ModEntities.java | 10 ++ .../hardworkers/init/ModItems.java | 7 + .../blockstates/miner_diamond.json | 8 + .../hardworkers/blockstates/miner_iron.json | 8 + .../blockstates/miner_netherite.json | 8 + .../hardworkers/blockstates/miner_stone.json | 8 + .../hardworkers/blockstates/miner_wood.json | 8 + .../assets/hardworkers/lang/en_us.json | 9 +- .../models/block/miner_diamond.json | 12 ++ .../hardworkers/models/block/miner_iron.json | 12 ++ .../models/block/miner_netherite.json | 12 ++ .../hardworkers/models/block/miner_stone.json | 12 ++ .../hardworkers/models/block/miner_wood.json | 12 ++ .../models/item/miner_diamond.json | 1 + .../hardworkers/models/item/miner_iron.json | 1 + .../models/item/miner_netherite.json | 1 + .../hardworkers/models/item/miner_stone.json | 1 + .../hardworkers/models/item/miner_wood.json | 1 + .../textures/block/miner_diamond_back.png | Bin 0 -> 121 bytes .../textures/block/miner_diamond_bottom.png | Bin 0 -> 89 bytes .../textures/block/miner_diamond_front.png | Bin 0 -> 111 bytes .../textures/block/miner_diamond_side.png | Bin 0 -> 90 bytes .../textures/block/miner_diamond_top.png | Bin 0 -> 102 bytes .../textures/block/miner_iron_back.png | Bin 0 -> 121 bytes .../textures/block/miner_iron_bottom.png | Bin 0 -> 90 bytes .../textures/block/miner_iron_front.png | Bin 0 -> 112 bytes .../textures/block/miner_iron_side.png | Bin 0 -> 91 bytes .../textures/block/miner_iron_top.png | Bin 0 -> 102 bytes .../textures/block/miner_netherite_back.png | Bin 0 -> 120 bytes .../textures/block/miner_netherite_bottom.png | Bin 0 -> 87 bytes .../textures/block/miner_netherite_front.png | Bin 0 -> 111 bytes .../textures/block/miner_netherite_side.png | Bin 0 -> 89 bytes .../textures/block/miner_netherite_top.png | Bin 0 -> 101 bytes .../textures/block/miner_stone_back.png | Bin 0 -> 121 bytes .../textures/block/miner_stone_bottom.png | Bin 0 -> 90 bytes .../textures/block/miner_stone_front.png | Bin 0 -> 112 bytes .../textures/block/miner_stone_side.png | Bin 0 -> 90 bytes .../textures/block/miner_stone_top.png | Bin 0 -> 102 bytes .../textures/block/miner_wood_back.png | Bin 0 -> 121 bytes .../textures/block/miner_wood_bottom.png | Bin 0 -> 89 bytes .../textures/block/miner_wood_front.png | Bin 0 -> 112 bytes .../textures/block/miner_wood_side.png | Bin 0 -> 90 bytes .../textures/block/miner_wood_top.png | Bin 0 -> 102 bytes .../hardworkers/textures/entity/miner.png | Bin 0 -> 323 bytes .../loot_table/blocks/miner_diamond.json | 8 + .../loot_table/blocks/miner_iron.json | 8 + .../loot_table/blocks/miner_netherite.json | 8 + .../loot_table/blocks/miner_stone.json | 8 + .../loot_table/blocks/miner_wood.json | 8 + .../hardworkers/recipes/miner_diamond.json | 11 ++ .../data/hardworkers/recipes/miner_iron.json | 11 ++ .../hardworkers/recipes/miner_netherite.json | 11 ++ .../data/hardworkers/recipes/miner_stone.json | 11 ++ .../data/hardworkers/recipes/miner_wood.json | 10 ++ 65 files changed, 954 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java create mode 100644 src/main/java/com/hardworkers/hardworkers/block/MinerTier.java create mode 100644 src/main/java/com/hardworkers/hardworkers/blockentity/MinerBlockEntity.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java create mode 100644 src/main/resources/assets/hardworkers/blockstates/miner_diamond.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/miner_iron.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/miner_netherite.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/miner_stone.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/miner_wood.json create mode 100644 src/main/resources/assets/hardworkers/models/block/miner_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/block/miner_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/block/miner_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/block/miner_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/block/miner_wood.json create mode 100644 src/main/resources/assets/hardworkers/models/item/miner_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/item/miner_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/item/miner_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/item/miner_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/item/miner_wood.json create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_diamond_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_diamond_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_diamond_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_diamond_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_diamond_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_iron_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_iron_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_iron_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_iron_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_iron_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_netherite_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_netherite_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_netherite_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_netherite_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_netherite_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_stone_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_stone_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_stone_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_stone_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_stone_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_wood_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_wood_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_wood_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_wood_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/miner_wood_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/entity/miner.png create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/miner_diamond.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/miner_iron.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/miner_netherite.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/miner_stone.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/miner_wood.json create mode 100644 src/main/resources/data/hardworkers/recipes/miner_diamond.json create mode 100644 src/main/resources/data/hardworkers/recipes/miner_iron.json create mode 100644 src/main/resources/data/hardworkers/recipes/miner_netherite.json create mode 100644 src/main/resources/data/hardworkers/recipes/miner_stone.json create mode 100644 src/main/resources/data/hardworkers/recipes/miner_wood.json diff --git a/gen_textures.py b/gen_textures.py index 00fceb9..ada78a2 100644 --- a/gen_textures.py +++ b/gen_textures.py @@ -115,6 +115,69 @@ def block_face(base, variant): write_png(f"{BLOCK_TEX}/lumberjack_{tier}_{face}.png", 16, 16, block_face(color, face)) +# Miner block textures — same colour palette, front shows a pickaxe-hint +# (two small bright dots for drill bits instead of eyes) +MINER_TIERS = { + "wood": (120, 85, 40), # warm brown, darker than lumberjack + "stone": ( 90, 90, 95), # darker grey + "iron": (155, 160, 175), # steely blue-grey + "diamond": ( 30, 170, 190), # deeper cyan + "netherite": ( 40, 18, 55), # deep purple +} + +def miner_face(base, variant): + r, g, b = base + pix = [] + for y in range(16): + for x in range(16): + border = x == 0 or x == 15 or y == 0 or y == 15 + dark = shade(base, -45) + if variant == "front": + # Two drill-bit dots + lower vent grill + bit_l = (x in (4, 5) and y in (4, 5, 6)) + bit_r = (x in (10, 11) and y in (4, 5, 6)) + grill = (4 <= x <= 11 and y in (10, 12) and x % 2 == 0) + if border: + pix.append(dark + (255,)) + elif bit_l or bit_r: + pix.append((210, 210, 255, 255)) # bright tip + elif grill: + pix.append(shade(base, -30) + (255,)) + else: + pix.append((r, g, b, 255)) + elif variant == "top": + cross = (x == 8 or y == 8) + if border: + pix.append(shade(base, -20) + (255,)) + elif cross: + pix.append(shade(base, +55) + (255,)) + else: + pix.append(shade(base, +30) + (255,)) + elif variant == "bottom": + if border: + pix.append(shade(base, -65) + (255,)) + else: + pix.append(shade(base, -40) + (255,)) + elif variant == "back": + stripe = ((x + y) % 6 == 0) + if border: + pix.append(dark + (255,)) + elif stripe: + pix.append(shade(base, -25) + (255,)) + else: + pix.append(shade(base, +10) + (255,)) + else: # side + if border: + pix.append(dark + (255,)) + else: + pix.append((r, g, b, 255)) + return pix + +for tier, color in MINER_TIERS.items(): + for face in ("front", "back", "top", "bottom", "side"): + write_png(f"{BLOCK_TEX}/miner_{tier}_{face}.png", 16, 16, + miner_face(color, face)) + # --------------------------------------------------------------------------- # Entity skin template (64x64, standard Minecraft player UV layout) # @@ -251,5 +314,107 @@ def f(x, y, fw, fh, color): write_png("src/main/resources/assets/hardworkers/textures/entity/lumberjack.png", 64, 64, pixels) +# --------------------------------------------------------------------------- +# Miner entity skin (64x64) +# Colour scheme: grey hard-hat, brown leather jacket, dark trousers, boots +# Same UV layout as the lumberjack skin — edit in any pixel editor. +# --------------------------------------------------------------------------- + +MHAT_F = ( 50, 50, 55, 255) # hard hat front (dark grey) +MHAT_S = ( 40, 40, 45, 255) # hard hat sides +MHAT_T = ( 60, 60, 65, 255) # hard hat top +MJACK_F = ( 90, 65, 35, 255) # leather jacket front (brown) +MJACK_S = ( 70, 50, 25, 255) # jacket sides +MPANTS_F = ( 55, 55, 60, 255) # dark grey trousers +MPANTS_S = ( 45, 45, 50, 255) +MBOOT = ( 35, 25, 15, 255) +MBOOT2 = ( 25, 18, 10, 255) +LAMP = (220, 200, 50, 255) # lamp dot on hard hat + +mp = [TRANS] * (64 * 64) + +def mf(x, y, fw, fh, color): + fill(mp, 64, x, y, fw, fh, color) + +# ── HEAD ──────────────────────────────────────────────────────────────────── +mf( 8, 0, 8, 8, SKIN) +mf(16, 0, 8, 8, SKIN2) +mf( 0, 8, 8, 8, SKIN2) +mf( 8, 8, 8, 8, SKIN) # face +mf(16, 8, 8, 8, SKIN2) +mf(24, 8, 8, 8, SKIN2) +mf(10,10, 2, 2, EYE) +mf(13,10, 2, 2, EYE) +mf( 9,13, 6, 2, BEARD) + +# ── HARD HAT (head overlay) ────────────────────────────────────────────────── +mf(40, 0, 8, 8, MHAT_T) # top — with lamp dot +mf(48, 0, 8, 8, MHAT_S) # bottom inside +mf(32, 8, 8, 8, MHAT_S) # right +mf(40, 8, 8, 8, MHAT_F) # front +mf(48, 8, 8, 8, MHAT_S) # left +mf(56, 8, 8, 8, MHAT_S) # back +# headlamp on hat top (centre-ish) +mf(43, 2, 2, 2, LAMP) + +# ── BODY — leather jacket ──────────────────────────────────────────────────── +mf(20,16, 8, 4, MJACK_S) +mf(28,16, 8, 4, MJACK_S) +mf(16,20, 4,12, MJACK_S) +mf(20,20, 8,12, MJACK_F) +mf(28,20, 4,12, MJACK_S) +mf(32,20, 8,12, MJACK_S) + +# ── RIGHT LEG ─────────────────────────────────────────────────────────────── +mf( 4,16, 4, 4, MPANTS_S) +mf( 8,16, 4, 4, MPANTS_S) +mf( 0,20, 4,12, MPANTS_S) +mf( 4,20, 4,12, MPANTS_F) +mf( 8,20, 4,12, MPANTS_S) +mf(12,20, 4,12, MPANTS_S) +mf( 0,28, 4, 4, MBOOT2) +mf( 4,28, 4, 4, MBOOT) +mf( 8,28, 4, 4, MBOOT2) +mf(12,28, 4, 4, MBOOT2) + +# ── LEFT LEG ──────────────────────────────────────────────────────────────── +mf(20,48, 4, 4, MPANTS_S) +mf(24,48, 4, 4, MPANTS_S) +mf(16,52, 4,12, MPANTS_S) +mf(20,52, 4,12, MPANTS_F) +mf(24,52, 4,12, MPANTS_S) +mf(28,52, 4,12, MPANTS_S) +mf(16,60, 4, 4, MBOOT2) +mf(20,60, 4, 4, MBOOT) +mf(24,60, 4, 4, MBOOT2) +mf(28,60, 4, 4, MBOOT2) + +# ── RIGHT ARM ─────────────────────────────────────────────────────────────── +mf(44,16, 4, 4, MJACK_S) +mf(48,16, 4, 4, MJACK_S) +mf(40,20, 4,12, MJACK_S) +mf(44,20, 4,12, MJACK_F) +mf(48,20, 4,12, MJACK_S) +mf(52,20, 4,12, MJACK_S) +mf(40,28, 4, 4, SKIN2) +mf(44,28, 4, 4, SKIN) +mf(48,28, 4, 4, SKIN2) +mf(52,28, 4, 4, SKIN2) + +# ── LEFT ARM ──────────────────────────────────────────────────────────────── +mf(36,48, 4, 4, MJACK_S) +mf(40,48, 4, 4, MJACK_S) +mf(32,52, 4,12, MJACK_S) +mf(36,52, 4,12, MJACK_F) +mf(40,52, 4,12, MJACK_S) +mf(44,52, 4,12, MJACK_S) +mf(32,60, 4, 4, SKIN2) +mf(36,60, 4, 4, SKIN) +mf(40,60, 4, 4, SKIN2) +mf(44,60, 4, 4, SKIN2) + +write_png("src/main/resources/assets/hardworkers/textures/entity/miner.png", + 64, 64, mp) + print("\nDone! Edit the PNGs in any pixel editor (e.g. Aseprite, GIMP, Pixelorama).") -print("Block textures are 16x16; the entity skin is 64x64 (standard Minecraft layout).") +print("Block textures are 16x16; entity skins are 64x64 (standard Minecraft layout).") diff --git a/src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java b/src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java new file mode 100644 index 0000000..fe4f405 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java @@ -0,0 +1,137 @@ +package com.hardworkers.hardworkers.block; + +import com.hardworkers.hardworkers.blockentity.MinerBlockEntity; +import com.hardworkers.hardworkers.entity.MinerEntity; +import com.hardworkers.hardworkers.init.ModEntities; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.MapColor; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; + +import javax.annotation.Nullable; + +public class MinerBlock extends BaseEntityBlock { + + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + + public static final MapCodec WOOD_CODEC = simpleCodec(p -> new MinerBlock(MinerTier.WOOD, p)); + public static final MapCodec STONE_CODEC = simpleCodec(p -> new MinerBlock(MinerTier.STONE, p)); + public static final MapCodec IRON_CODEC = simpleCodec(p -> new MinerBlock(MinerTier.IRON, p)); + public static final MapCodec DIAMOND_CODEC = simpleCodec(p -> new MinerBlock(MinerTier.DIAMOND, p)); + public static final MapCodec NETHERITE_CODEC = simpleCodec(p -> new MinerBlock(MinerTier.NETHERITE, p)); + + private final MinerTier tier; + + public MinerBlock(MinerTier tier, BlockBehaviour.Properties properties) { + super(properties); + this.tier = tier; + registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH)); + } + + public MinerTier getTier() { return tier; } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); + } + + @Override + public MapCodec codec() { + return switch (tier) { + case WOOD -> WOOD_CODEC; + case STONE -> STONE_CODEC; + case IRON -> IRON_CODEC; + case DIAMOND -> DIAMOND_CODEC; + case NETHERITE -> NETHERITE_CODEC; + }; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new MinerBlockEntity(pos, state); + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + super.onPlace(state, level, pos, oldState, movedByPiston); + if (!level.isClientSide()) { + MinerEntity miner = ModEntities.MINER.get().create(level); + if (miner != null) { + miner.setHomePosition(pos); + miner.moveTo(pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, 0f, 0f); + level.addFreshEntity(miner); + } + } + } + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!level.isClientSide() && !state.is(newState.getBlock())) { + if (level.getBlockEntity(pos) instanceof MinerBlockEntity be) { + Containers.dropContents(level, pos, be); + } + AABB searchArea = new AABB(pos).inflate(3.0); + level.getEntitiesOfClass(MinerEntity.class, searchArea) + .stream() + .filter(e -> pos.equals(e.getHomePosition())) + .forEach(MinerEntity::discard); + } + super.onRemove(state, level, pos, newState, movedByPiston); + } + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, + Player player, BlockHitResult hitResult) { + if (!level.isClientSide() && level.getBlockEntity(pos) instanceof MinerBlockEntity be) { + player.displayClientMessage(be.getStorageStatus(), true); + } + return InteractionResult.sidedSuccess(level.isClientSide()); + } + + public static BlockBehaviour.Properties baseProperties(MapColor color) { + return BlockBehaviour.Properties.of() + .mapColor(color) + .sound(SoundType.STONE) + .strength(2.5f); + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/block/MinerTier.java b/src/main/java/com/hardworkers/hardworkers/block/MinerTier.java new file mode 100644 index 0000000..29e0c95 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/block/MinerTier.java @@ -0,0 +1,17 @@ +package com.hardworkers.hardworkers.block; + +public enum MinerTier { + WOOD ("wood", 40), + STONE ("stone", 30), + IRON ("iron", 20), + DIAMOND ("diamond", 12), + NETHERITE("netherite", 6); + + public final String id; + public final int mineInterval; + + MinerTier(String id, int mineInterval) { + this.id = id; + this.mineInterval = mineInterval; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/blockentity/MinerBlockEntity.java b/src/main/java/com/hardworkers/hardworkers/blockentity/MinerBlockEntity.java new file mode 100644 index 0000000..737e6e3 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/blockentity/MinerBlockEntity.java @@ -0,0 +1,97 @@ +package com.hardworkers.hardworkers.blockentity; + +import com.hardworkers.hardworkers.init.ModBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.wrapper.InvWrapper; + +public class MinerBlockEntity extends BlockEntity implements Container { + + public static final int SLOT_COUNT = 27; + + private NonNullList items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); + private final IItemHandler itemHandler = new InvWrapper(this); + + public MinerBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.MINER_BLOCK_ENTITY.get(), pos, state); + } + + public IItemHandler getItemHandler() { return itemHandler; } + + public ItemStack insertItem(ItemStack incoming) { + if (incoming.isEmpty()) return ItemStack.EMPTY; + for (int i = 0; i < SLOT_COUNT; i++) { + ItemStack slot = items.get(i); + if (!slot.isEmpty() + && slot.getItem() == incoming.getItem() + && ItemStack.isSameItemSameComponents(slot, incoming)) { + int space = slot.getMaxStackSize() - slot.getCount(); + if (space > 0) { + int toAdd = Math.min(space, incoming.getCount()); + slot.grow(toAdd); + setChanged(); + incoming = incoming.copyWithCount(incoming.getCount() - toAdd); + if (incoming.isEmpty()) return ItemStack.EMPTY; + } + } + } + for (int i = 0; i < SLOT_COUNT; i++) { + if (items.get(i).isEmpty()) { + int toStore = Math.min(incoming.getCount(), incoming.getMaxStackSize()); + items.set(i, incoming.copyWithCount(toStore)); + setChanged(); + int leftover = incoming.getCount() - toStore; + return leftover > 0 ? incoming.copyWithCount(leftover) : ItemStack.EMPTY; + } + } + return incoming; + } + + public boolean isFull() { + for (ItemStack stack : items) { + if (stack.isEmpty() || stack.getCount() < stack.getMaxStackSize()) return false; + } + return true; + } + + public Component getStorageStatus() { + int usedSlots = 0, totalItems = 0; + for (ItemStack stack : items) { + if (!stack.isEmpty()) { usedSlots++; totalItems += stack.getCount(); } + } + if (usedSlots == 0) return Component.literal("Storage: empty"); + return Component.literal("Storage: " + usedSlots + "/" + SLOT_COUNT + " slots (" + totalItems + " items)"); + } + + @Override public int getContainerSize() { return SLOT_COUNT; } + @Override public boolean isEmpty() { for (ItemStack s : items) { if (!s.isEmpty()) return false; } return true; } + @Override public ItemStack getItem(int slot) { return slot >= 0 && slot < SLOT_COUNT ? items.get(slot) : ItemStack.EMPTY; } + @Override public ItemStack removeItem(int slot, int amount) { ItemStack r = ContainerHelper.removeItem(items, slot, amount); if (!r.isEmpty()) setChanged(); return r; } + @Override public ItemStack removeItemNoUpdate(int slot) { return ContainerHelper.takeItem(items, slot); } + @Override public void setItem(int slot, ItemStack stack) { if (slot >= 0 && slot < SLOT_COUNT) { items.set(slot, stack); setChanged(); } } + @Override public boolean stillValid(Player player) { return true; } + @Override public void clearContent() { items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); setChanged(); } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + ContainerHelper.saveAllItems(tag, items, registries); + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); + ContainerHelper.loadAllItems(tag, items, registries); + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java b/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java index 4fc46cf..ec1ca76 100644 --- a/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java +++ b/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java @@ -2,6 +2,7 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.entity.client.LumberjackRenderer; +import com.hardworkers.hardworkers.entity.client.MinerRenderer; import com.hardworkers.hardworkers.init.ModEntities; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; @@ -14,5 +15,6 @@ public class ClientEvents { @SubscribeEvent public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerEntityRenderer(ModEntities.LUMBERJACK.get(), LumberjackRenderer::new); + event.registerEntityRenderer(ModEntities.MINER.get(), MinerRenderer::new); } } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java b/src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java new file mode 100644 index 0000000..facd91a --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java @@ -0,0 +1,74 @@ +package com.hardworkers.hardworkers.entity; + +import com.hardworkers.hardworkers.entity.ai.MineForwardGoal; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.goal.FloatGoal; +import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; +import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; +import net.minecraft.world.level.Level; + +public class MinerEntity extends PathfinderMob { + + private BlockPos homePosition = BlockPos.ZERO; + private int currentDepth = 1; + + public MinerEntity(EntityType type, Level level) { + super(type, level); + } + + public static AttributeSupplier.Builder createAttributes() { + return PathfinderMob.createMobAttributes() + .add(Attributes.MAX_HEALTH, 20.0) + .add(Attributes.MOVEMENT_SPEED, 0.3) + .add(Attributes.FOLLOW_RANGE, 80.0); + } + + @Override + protected void registerGoals() { + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new MineForwardGoal(this)); + goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.5)); + goalSelector.addGoal(3, new RandomLookAroundGoal(this)); + } + + @Override + public void tick() { + super.tick(); + if (!level().isClientSide() && tickCount % 100 == 0 && !homePosition.equals(BlockPos.ZERO)) { + if (level().getBlockState(homePosition).isAir()) { + discard(); + } + } + } + + @Override + public void addAdditionalSaveData(CompoundTag tag) { + super.addAdditionalSaveData(tag); + tag.putInt("HomeX", homePosition.getX()); + tag.putInt("HomeY", homePosition.getY()); + tag.putInt("HomeZ", homePosition.getZ()); + tag.putInt("CurrentDepth", currentDepth); + } + + @Override + public void readAdditionalSaveData(CompoundTag tag) { + super.readAdditionalSaveData(tag); + if (tag.contains("HomeX")) { + homePosition = new BlockPos(tag.getInt("HomeX"), tag.getInt("HomeY"), tag.getInt("HomeZ")); + } + if (tag.contains("CurrentDepth")) { + currentDepth = tag.getInt("CurrentDepth"); + } + } + + public BlockPos getHomePosition() { return homePosition; } + public void setHomePosition(BlockPos pos) { this.homePosition = pos; } + + public int getCurrentDepth() { return currentDepth; } + public void setCurrentDepth(int depth) { this.currentDepth = depth; } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java new file mode 100644 index 0000000..23ba7e9 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java @@ -0,0 +1,165 @@ +package com.hardworkers.hardworkers.entity.ai; + +import com.hardworkers.hardworkers.block.MinerBlock; +import com.hardworkers.hardworkers.block.MinerTier; +import com.hardworkers.hardworkers.blockentity.MinerBlockEntity; +import com.hardworkers.hardworkers.entity.MinerEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.EnumSet; +import java.util.List; + +/** + * Mines a 3-block-tall (Y, Y+1, Y+2) tunnel in the direction the miner block + * faces, up to 64 blocks deep. One block is mined per interval; the current + * depth is saved in the entity so progress survives world restarts. + * + * Tier capability (blocks in the path that need a higher tier are skipped): + * WOOD – no NEEDS_STONE_TOOL, NEEDS_IRON_TOOL, or NEEDS_DIAMOND_TOOL blocks + * STONE – no NEEDS_IRON_TOOL or NEEDS_DIAMOND_TOOL blocks + * IRON – no NEEDS_DIAMOND_TOOL blocks + * DIAMOND / NETHERITE – mines everything (except bedrock) + */ +public class MineForwardGoal extends Goal { + + public static final int MAX_DEPTH = 64; + public static final int TUNNEL_HEIGHT = 3; + + private final MinerEntity miner; + private int mineTimer = 0; + + public MineForwardGoal(MinerEntity miner) { + this.miner = miner; + setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + return miner.getCurrentDepth() <= MAX_DEPTH && !isStorageFull(); + } + + @Override + public boolean canContinueToUse() { + return miner.getCurrentDepth() <= MAX_DEPTH; + } + + @Override + public void stop() { + mineTimer = 0; + miner.getNavigation().stop(); + } + + @Override + public void tick() { + if (!(miner.level() instanceof ServerLevel serverLevel)) return; + + int depth = miner.getCurrentDepth(); + if (depth > MAX_DEPTH) return; + + Direction facing = getHomeFacing(); + if (facing == null) return; + + BlockPos homePos = miner.getHomePosition(); + BlockPos facePos = homePos.relative(facing, depth); + + // Walk toward the current mining face and look at it + miner.getNavigation().moveTo(facePos.getX() + 0.5, facePos.getY(), facePos.getZ() + 0.5, 0.85); + miner.getLookControl().setLookAt(facePos.getX() + 0.5, facePos.getY() + 1.0, facePos.getZ() + 0.5); + + if (isStorageFull()) return; + + mineTimer++; + if (mineTimer < mineInterval()) return; + mineTimer = 0; + + // Find and mine the first block at this depth that needs mining + for (int h = 0; h < TUNNEL_HEIGHT; h++) { + BlockPos minePos = facePos.above(h); + BlockState state = serverLevel.getBlockState(minePos); + + if (!needsMining(state)) continue; + + miner.getLookControl().setLookAt(minePos.getX() + 0.5, minePos.getY() + 0.5, minePos.getZ() + 0.5); + List drops = Block.getDrops(state, serverLevel, minePos, null); + serverLevel.setBlock(minePos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + serverLevel.levelEvent(2001, minePos, Block.getId(state)); + depositItems(serverLevel, drops); + return; // one block per interval + } + + // All three heights at this depth are clear (air, bedrock, or tier-restricted) → advance + miner.setCurrentDepth(depth + 1); + } + + // ------------------------------------------------------------------------- + + /** + * Returns true if this block should be mined (not air, not bedrock, not a + * liquid, and within the tier's capability). + */ + private boolean needsMining(BlockState state) { + if (state.isAir()) return false; + if (state.is(Blocks.BEDROCK)) return false; + if (state.is(Blocks.WATER) || state.is(Blocks.LAVA)) return false; + return canMine(state); + } + + /** Returns true if the current tier can break this block. */ + private boolean canMine(BlockState state) { + return switch (getHomeTier()) { + case WOOD -> !state.is(BlockTags.NEEDS_STONE_TOOL) + && !state.is(BlockTags.NEEDS_IRON_TOOL) + && !state.is(BlockTags.NEEDS_DIAMOND_TOOL); + case STONE -> !state.is(BlockTags.NEEDS_IRON_TOOL) + && !state.is(BlockTags.NEEDS_DIAMOND_TOOL); + case IRON -> !state.is(BlockTags.NEEDS_DIAMOND_TOOL); + case DIAMOND, NETHERITE -> true; + }; + } + + private MinerTier getHomeTier() { + BlockState homeState = miner.level().getBlockState(miner.getHomePosition()); + if (homeState.getBlock() instanceof MinerBlock mb) return mb.getTier(); + return MinerTier.NETHERITE; + } + + private Direction getHomeFacing() { + BlockState homeState = miner.level().getBlockState(miner.getHomePosition()); + if (homeState.hasProperty(MinerBlock.FACING)) return homeState.getValue(MinerBlock.FACING); + return null; + } + + private int mineInterval() { + return getHomeTier().mineInterval; + } + + private boolean isStorageFull() { + BlockEntity be = miner.level().getBlockEntity(miner.getHomePosition()); + return be instanceof MinerBlockEntity storage && storage.isFull(); + } + + private void depositItems(ServerLevel level, List drops) { + BlockEntity be = level.getBlockEntity(miner.getHomePosition()); + if (be instanceof MinerBlockEntity storage) { + for (ItemStack drop : drops) { + ItemStack remainder = storage.insertItem(drop); + if (!remainder.isEmpty()) { + Block.popResource(level, miner.blockPosition(), remainder); + } + } + } else { + for (ItemStack drop : drops) { + Block.popResource(level, miner.blockPosition(), drop); + } + } + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java new file mode 100644 index 0000000..6574eed --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java @@ -0,0 +1,23 @@ +package com.hardworkers.hardworkers.entity.client; + +import com.hardworkers.hardworkers.entity.MinerEntity; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.HumanoidMobRenderer; +import net.minecraft.resources.ResourceLocation; + +public class MinerRenderer extends HumanoidMobRenderer> { + + private static final ResourceLocation TEXTURE = + ResourceLocation.fromNamespaceAndPath("hardworkers", "textures/entity/miner.png"); + + public MinerRenderer(EntityRendererProvider.Context ctx) { + super(ctx, new HumanoidModel<>(ctx.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)), 0.5f); + } + + @Override + public ResourceLocation getTextureLocation(MinerEntity entity) { + return TEXTURE; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java index b863e78..70d8d58 100644 --- a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java +++ b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java @@ -2,6 +2,7 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.entity.LumberjackEntity; +import com.hardworkers.hardworkers.entity.MinerEntity; import com.hardworkers.hardworkers.init.ModBlockEntities; import com.hardworkers.hardworkers.init.ModEntities; import com.hardworkers.hardworkers.init.ModItems; @@ -19,6 +20,7 @@ public class ModEventSubscriber { @SubscribeEvent public static void onEntityAttributeCreation(EntityAttributeCreationEvent event) { event.put(ModEntities.LUMBERJACK.get(), LumberjackEntity.createAttributes().build()); + event.put(ModEntities.MINER.get(), MinerEntity.createAttributes().build()); } @SubscribeEvent @@ -29,6 +31,11 @@ public static void onBuildCreativeTab(BuildCreativeModeTabContentsEvent event) { event.accept(ModItems.LUMBERJACK_IRON_ITEM); event.accept(ModItems.LUMBERJACK_DIAMOND_ITEM); event.accept(ModItems.LUMBERJACK_NETHERITE_ITEM); + event.accept(ModItems.MINER_WOOD_ITEM); + event.accept(ModItems.MINER_STONE_ITEM); + event.accept(ModItems.MINER_IRON_ITEM); + event.accept(ModItems.MINER_DIAMOND_ITEM); + event.accept(ModItems.MINER_NETHERITE_ITEM); } } @@ -39,5 +46,10 @@ public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { ModBlockEntities.LUMBERJACK_BLOCK_ENTITY.get(), (be, side) -> be.getItemHandler() ); + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + ModBlockEntities.MINER_BLOCK_ENTITY.get(), + (be, side) -> be.getItemHandler() + ); } } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java index 89e2496..96b7564 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java @@ -2,6 +2,7 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; +import com.hardworkers.hardworkers.blockentity.MinerBlockEntity; import net.minecraft.core.registries.Registries; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.neoforge.registries.DeferredHolder; @@ -23,4 +24,17 @@ public class ModBlockEntities { ModBlocks.LUMBERJACK_NETHERITE.get() ).build(null) ); + + public static final DeferredHolder, BlockEntityType> MINER_BLOCK_ENTITY = + BLOCK_ENTITY_TYPES.register("miner_block", () -> + BlockEntityType.Builder.of( + MinerBlockEntity::new, + ModBlocks.MINER_WOOD.get(), + ModBlocks.MINER_STONE.get(), + ModBlocks.MINER_IRON.get(), + ModBlocks.MINER_DIAMOND.get(), + ModBlocks.MINER_NETHERITE.get() + ).build(null) + ); } + diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java index 5e9ad48..9ffe9a2 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java @@ -3,6 +3,8 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.block.LumberjackBlock; import com.hardworkers.hardworkers.block.LumberjackTier; +import com.hardworkers.hardworkers.block.MinerBlock; +import com.hardworkers.hardworkers.block.MinerTier; import net.minecraft.world.level.material.MapColor; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; @@ -30,4 +32,25 @@ public class ModBlocks { public static final DeferredBlock LUMBERJACK_NETHERITE = BLOCKS.register("lumberjack_netherite", () -> new LumberjackBlock(LumberjackTier.NETHERITE, LumberjackBlock.baseProperties(MapColor.COLOR_BLACK))); + + public static final DeferredBlock MINER_WOOD = + BLOCKS.register("miner_wood", + () -> new MinerBlock(MinerTier.WOOD, MinerBlock.baseProperties(MapColor.STONE))); + + public static final DeferredBlock MINER_STONE = + BLOCKS.register("miner_stone", + () -> new MinerBlock(MinerTier.STONE, MinerBlock.baseProperties(MapColor.STONE))); + + public static final DeferredBlock MINER_IRON = + BLOCKS.register("miner_iron", + () -> new MinerBlock(MinerTier.IRON, MinerBlock.baseProperties(MapColor.METAL))); + + public static final DeferredBlock MINER_DIAMOND = + BLOCKS.register("miner_diamond", + () -> new MinerBlock(MinerTier.DIAMOND, MinerBlock.baseProperties(MapColor.DIAMOND))); + + public static final DeferredBlock MINER_NETHERITE = + BLOCKS.register("miner_netherite", + () -> new MinerBlock(MinerTier.NETHERITE, MinerBlock.baseProperties(MapColor.COLOR_BLACK))); } + diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java index 2639da7..8170676 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java @@ -2,6 +2,7 @@ import com.hardworkers.hardworkers.HardWorkers; import com.hardworkers.hardworkers.entity.LumberjackEntity; +import com.hardworkers.hardworkers.entity.MinerEntity; import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; @@ -20,4 +21,13 @@ public class ModEntities { .clientTrackingRange(10) .build("lumberjack") ); + + public static final DeferredHolder, EntityType> MINER = + ENTITY_TYPES.register("miner", () -> + EntityType.Builder.of(MinerEntity::new, MobCategory.MISC) + .sized(0.6f, 1.95f) + .clientTrackingRange(80) + .build("miner") + ); } + diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java index e610350..3bf5ae6 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java @@ -14,4 +14,11 @@ public class ModItems { public static final DeferredItem LUMBERJACK_IRON_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_IRON); public static final DeferredItem LUMBERJACK_DIAMOND_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_DIAMOND); public static final DeferredItem LUMBERJACK_NETHERITE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.LUMBERJACK_NETHERITE); + + public static final DeferredItem MINER_WOOD_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_WOOD); + public static final DeferredItem MINER_STONE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_STONE); + public static final DeferredItem MINER_IRON_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_IRON); + public static final DeferredItem MINER_DIAMOND_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_DIAMOND); + public static final DeferredItem MINER_NETHERITE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_NETHERITE); } + diff --git a/src/main/resources/assets/hardworkers/blockstates/miner_diamond.json b/src/main/resources/assets/hardworkers/blockstates/miner_diamond.json new file mode 100644 index 0000000..95ffa8a --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/miner_diamond.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/miner_diamond" }, + "facing=east": { "model": "hardworkers:block/miner_diamond", "y": 90 }, + "facing=south": { "model": "hardworkers:block/miner_diamond", "y": 180 }, + "facing=west": { "model": "hardworkers:block/miner_diamond", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/miner_iron.json b/src/main/resources/assets/hardworkers/blockstates/miner_iron.json new file mode 100644 index 0000000..df45356 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/miner_iron.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/miner_iron" }, + "facing=east": { "model": "hardworkers:block/miner_iron", "y": 90 }, + "facing=south": { "model": "hardworkers:block/miner_iron", "y": 180 }, + "facing=west": { "model": "hardworkers:block/miner_iron", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/miner_netherite.json b/src/main/resources/assets/hardworkers/blockstates/miner_netherite.json new file mode 100644 index 0000000..f723b11 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/miner_netherite.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/miner_netherite" }, + "facing=east": { "model": "hardworkers:block/miner_netherite", "y": 90 }, + "facing=south": { "model": "hardworkers:block/miner_netherite", "y": 180 }, + "facing=west": { "model": "hardworkers:block/miner_netherite", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/miner_stone.json b/src/main/resources/assets/hardworkers/blockstates/miner_stone.json new file mode 100644 index 0000000..b6c9587 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/miner_stone.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/miner_stone" }, + "facing=east": { "model": "hardworkers:block/miner_stone", "y": 90 }, + "facing=south": { "model": "hardworkers:block/miner_stone", "y": 180 }, + "facing=west": { "model": "hardworkers:block/miner_stone", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/miner_wood.json b/src/main/resources/assets/hardworkers/blockstates/miner_wood.json new file mode 100644 index 0000000..fbcc002 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/miner_wood.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/miner_wood" }, + "facing=east": { "model": "hardworkers:block/miner_wood", "y": 90 }, + "facing=south": { "model": "hardworkers:block/miner_wood", "y": 180 }, + "facing=west": { "model": "hardworkers:block/miner_wood", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/lang/en_us.json b/src/main/resources/assets/hardworkers/lang/en_us.json index dcc2d6f..298c4e6 100644 --- a/src/main/resources/assets/hardworkers/lang/en_us.json +++ b/src/main/resources/assets/hardworkers/lang/en_us.json @@ -4,5 +4,12 @@ "block.hardworkers.lumberjack_iron": "Lumberjack Block (Iron)", "block.hardworkers.lumberjack_diamond": "Lumberjack Block (Diamond)", "block.hardworkers.lumberjack_netherite": "Lumberjack Block (Netherite)", - "entity.hardworkers.lumberjack": "Lumberjack" + "entity.hardworkers.lumberjack": "Lumberjack", + + "block.hardworkers.miner_wood": "Miner Block (Wood)", + "block.hardworkers.miner_stone": "Miner Block (Stone)", + "block.hardworkers.miner_iron": "Miner Block (Iron)", + "block.hardworkers.miner_diamond": "Miner Block (Diamond)", + "block.hardworkers.miner_netherite": "Miner Block (Netherite)", + "entity.hardworkers.miner": "Miner" } diff --git a/src/main/resources/assets/hardworkers/models/block/miner_diamond.json b/src/main/resources/assets/hardworkers/models/block/miner_diamond.json new file mode 100644 index 0000000..754fc72 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/miner_diamond.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/miner_diamond_front", + "up": "hardworkers:block/miner_diamond_top", + "down": "hardworkers:block/miner_diamond_bottom", + "north": "hardworkers:block/miner_diamond_front", + "south": "hardworkers:block/miner_diamond_back", + "east": "hardworkers:block/miner_diamond_side", + "west": "hardworkers:block/miner_diamond_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/miner_iron.json b/src/main/resources/assets/hardworkers/models/block/miner_iron.json new file mode 100644 index 0000000..0ecef80 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/miner_iron.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/miner_iron_front", + "up": "hardworkers:block/miner_iron_top", + "down": "hardworkers:block/miner_iron_bottom", + "north": "hardworkers:block/miner_iron_front", + "south": "hardworkers:block/miner_iron_back", + "east": "hardworkers:block/miner_iron_side", + "west": "hardworkers:block/miner_iron_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/miner_netherite.json b/src/main/resources/assets/hardworkers/models/block/miner_netherite.json new file mode 100644 index 0000000..59d3b59 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/miner_netherite.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/miner_netherite_front", + "up": "hardworkers:block/miner_netherite_top", + "down": "hardworkers:block/miner_netherite_bottom", + "north": "hardworkers:block/miner_netherite_front", + "south": "hardworkers:block/miner_netherite_back", + "east": "hardworkers:block/miner_netherite_side", + "west": "hardworkers:block/miner_netherite_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/miner_stone.json b/src/main/resources/assets/hardworkers/models/block/miner_stone.json new file mode 100644 index 0000000..ad06e3e --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/miner_stone.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/miner_stone_front", + "up": "hardworkers:block/miner_stone_top", + "down": "hardworkers:block/miner_stone_bottom", + "north": "hardworkers:block/miner_stone_front", + "south": "hardworkers:block/miner_stone_back", + "east": "hardworkers:block/miner_stone_side", + "west": "hardworkers:block/miner_stone_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/miner_wood.json b/src/main/resources/assets/hardworkers/models/block/miner_wood.json new file mode 100644 index 0000000..906ae98 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/miner_wood.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/miner_wood_front", + "up": "hardworkers:block/miner_wood_top", + "down": "hardworkers:block/miner_wood_bottom", + "north": "hardworkers:block/miner_wood_front", + "south": "hardworkers:block/miner_wood_back", + "east": "hardworkers:block/miner_wood_side", + "west": "hardworkers:block/miner_wood_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/item/miner_diamond.json b/src/main/resources/assets/hardworkers/models/item/miner_diamond.json new file mode 100644 index 0000000..91cb814 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/miner_diamond.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/miner_diamond" } diff --git a/src/main/resources/assets/hardworkers/models/item/miner_iron.json b/src/main/resources/assets/hardworkers/models/item/miner_iron.json new file mode 100644 index 0000000..568bd1e --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/miner_iron.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/miner_iron" } diff --git a/src/main/resources/assets/hardworkers/models/item/miner_netherite.json b/src/main/resources/assets/hardworkers/models/item/miner_netherite.json new file mode 100644 index 0000000..73f4fa9 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/miner_netherite.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/miner_netherite" } diff --git a/src/main/resources/assets/hardworkers/models/item/miner_stone.json b/src/main/resources/assets/hardworkers/models/item/miner_stone.json new file mode 100644 index 0000000..42fd84d --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/miner_stone.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/miner_stone" } diff --git a/src/main/resources/assets/hardworkers/models/item/miner_wood.json b/src/main/resources/assets/hardworkers/models/item/miner_wood.json new file mode 100644 index 0000000..a80d909 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/miner_wood.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/miner_wood" } diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_diamond_back.png b/src/main/resources/assets/hardworkers/textures/block/miner_diamond_back.png new file mode 100644 index 0000000000000000000000000000000000000000..1adeb0f223849989f8812cc30ae72be55ff7d55a GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`4xTQKAr*6y6IRUqF<*&C$zgI0)z4*}Q$iB}zX>F1 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_diamond_bottom.png b/src/main/resources/assets/hardworkers/textures/block/miner_diamond_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..74620ba5f0e36266c58abd7b9dd0bc493f725904 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3Z5>GAr*6y6Hcu8>o3SA?0fmd m;l*q{QU+NJTX`2SFf%Ov&7Iyo^M@x;ErX}4pUXO@geCx~@)+R& literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_diamond_front.png b/src/main/resources/assets/hardworkers/textures/block/miner_diamond_front.png new file mode 100644 index 0000000000000000000000000000000000000000..3d4e9b006280c7091990843298b08b22902dd0c6 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`W}YsNAr*6y6IRUqF<*&C=}FjI zWp4f;X1&;7{KsB&ZR`ZH+3W-~%^DV_Gsab^>}b$o);P!zlTqAVQ)o9GXaa+$tDnm{ Hr-UW|D7hc{ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_diamond_side.png b/src/main/resources/assets/hardworkers/textures/block/miner_diamond_side.png new file mode 100644 index 0000000000000000000000000000000000000000..912a36c31ffbe86c813d2880ef9f67a27d16a59d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6y6IRUqF<*&C=}FjI nW$u0{gRBE97_XQ$2=Oqy3(WYh6`jrmRLtP%>gTe~DWM4f$O#zp literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_diamond_top.png b/src/main/resources/assets/hardworkers/textures/block/miner_diamond_top.png new file mode 100644 index 0000000000000000000000000000000000000000..cba297ca27e6ca0dd06da2f5fe1231e9eb6552b8 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`x}GkMAr*6yAI#eJ%3gH_qsxss zJDP>t?f!HH=Ur)VOl9y8w5$-QOuWKyYLhX8#rDGwCI;t~0W~ssy85}Sb4q9e0DwCn ADgXcg literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_iron_back.png b/src/main/resources/assets/hardworkers/textures/block/miner_iron_back.png new file mode 100644 index 0000000000000000000000000000000000000000..934ac3220ec0cbf20fb8926b0df9bb778d4bcfd1 GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`4xTQKAr*6yPiW`;^5;xr5cCdz z>vGWd?PY@_8#Y&OqU)_2P{NQrsGB0ZojX{VoS@ TgI0)z4*}Q$iB}#d;*B literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_iron_bottom.png b/src/main/resources/assets/hardworkers/textures/block/miner_iron_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba76a8215a78a71f3f7cce3d55bba560afc6720 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6yJG##Q_UBAv5IidS nHsxU;vm393X~QkHfF?%U)c9_$GbfsXiWxjz{an^LB{Ts5wRIS^ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_iron_front.png b/src/main/resources/assets/hardworkers/textures/block/miner_iron_front.png new file mode 100644 index 0000000000000000000000000000000000000000..ccf1f14ec4039a3a0ad1aa49886ae595bc09c061 GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`=AJH&Ar*6yPiW`;^5;xr5Zo-j zMxC2Kh*>Z87yq#rT^l=rY&JUqO|u2tGL_=0RCYAzFl!uSn7rzIczopr01Rj!&i}6-`;0z3slYE>FVdQ&MBb@0Jgdq!Tu%xrUH^EN-&w)fe2QU$Qqc#6Plr}8PDxE?P+G12{{Fqk4M2?yp00i_>zopr0K6t5 Aj{pDw literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_netherite_back.png b/src/main/resources/assets/hardworkers/textures/block/miner_netherite_back.png new file mode 100644 index 0000000000000000000000000000000000000000..b12af5a3ae464c09d6c14d73f956d243e7ce7894 GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`_MR?|Ar*6y6A~W$@|R>2ax?1F zm@(gx&zWboMHq9()t;*iK__5mK7OyfGQb0UHx3vIVCg!08;iBGynhq literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_netherite_front.png b/src/main/resources/assets/hardworkers/textures/block/miner_netherite_front.png new file mode 100644 index 0000000000000000000000000000000000000000..615a267ba22f20fd38db64c205578ccbca9cfa61 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`W}YsNAr*6y6A~W$@|R>2l5%>M z^3XBsz*6u32PI++%{~Iz2iTR4vK1J5JX-4B#C^i3L5PRpfL#6+)!GLj6Bs;Q{an^L HB{Ts5kJci` literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_netherite_side.png b/src/main/resources/assets/hardworkers/textures/block/miner_netherite_side.png new file mode 100644 index 0000000000000000000000000000000000000000..be427d0cbd9985632b395eb6f10389ca655ea5a7 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3Z5>GAr*6y6A~W$@|R>2l5%>M m@-UFujaS07;TBs!6QlFP@Hc7C{AGb^89ZJ6T-G@yGywpKlo${I literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_netherite_top.png b/src/main/resources/assets/hardworkers/textures/block/miner_netherite_top.png new file mode 100644 index 0000000000000000000000000000000000000000..9d0285f42352b97912a2f20067f308db75099281 GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`I-V|$Ar*6y1rh`P`E#Z*2gI0)z4*}Q$iB}klQ1R literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_stone_bottom.png b/src/main/resources/assets/hardworkers/textures/block/miner_stone_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..82852abf26e4e87391e17ffad7a6213b67292949 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6yCrp|0+n+OyLD0?Z nS<1seW;b35(}r7Y0ZokjUL7^_b^>bP0l+XkKqjnf# literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_stone_front.png b/src/main/resources/assets/hardworkers/textures/block/miner_stone_front.png new file mode 100644 index 0000000000000000000000000000000000000000..a6a4bf2e3bfb0dd5937e64b443825ed48c0e9df5 GIT binary patch literal 112 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`=AJH&Ar*6yFI+YK<|BOBj^AC literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_stone_side.png b/src/main/resources/assets/hardworkers/textures/block/miner_stone_side.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7dcc9a7e6e834c905a06feb953c64a0c020a87 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6yFI+YK<N zYVq+%=jDP^y;24%co;O5*!h+8d!!s9%h(vcGzL6aII}qisFA_b)z4*}Q$iB}1TGx` literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_wood_back.png b/src/main/resources/assets/hardworkers/textures/block/miner_wood_back.png new file mode 100644 index 0000000000000000000000000000000000000000..1820a9cb1d904760b13a2bfbc5c3c0b385a7adf5 GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`4xTQKAr*6yKU_=r;m?`IAn5zi zO~c||o4hm6Y>P1Fj;lRa8G=lbB^*|zO|ZDbP-OC$hdF4Eu@v{>jSWc*suBzq)9)9U T9MR_kn#JJh>gTe~DWM4foXI1P literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_wood_bottom.png b/src/main/resources/assets/hardworkers/textures/block/miner_wood_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..da26b5fc5f84567b61b492efd3dbbffe93db8ac4 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3Z5>GAr*6y4a5_F_;aQ)2r}+^ l@-UFujaS07;TBs!6Qf&fLBvOyWow;a literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_wood_side.png b/src/main/resources/assets/hardworkers/textures/block/miner_wood_side.png new file mode 100644 index 0000000000000000000000000000000000000000..879f448cb73ce2d437f44fafce3e88bb12b592c2 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr*6yKU_=r;m?`IASk*} nD&=7yvm393X~QkHfF?%c{a0sLPyK5KRLtP%>gTe~DWM4ftu`37 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/miner_wood_top.png b/src/main/resources/assets/hardworkers/textures/block/miner_wood_top.png new file mode 100644 index 0000000000000000000000000000000000000000..6beb7c93d896269f4d0b5de54b7767bcb8fbffbd GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`x}GkMAr*6yJqksB`g5i+2wv{G zTkzqaffJ$T4%> z^9irs8mVaa>Mwo$YeLrLEhn??^Bc=NbzI!s4(R9u5)L+w z?{~YZPWZumY@c5k_m{U7Kr_MM!0(Xu*owT^x8l9;kDujL_&dkbJ{lyW*dpM>0U|u^ zJ+X`VdsHTbk%1wZVJClB7(Ww4FGCySmiE=N?fFVdQ I&MBb@0K|iMn*aa+ literal 0 HcmV?d00001 diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/miner_diamond.json b/src/main/resources/data/hardworkers/loot_table/blocks/miner_diamond.json new file mode 100644 index 0000000..c520a30 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/miner_diamond.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:miner_diamond" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/miner_iron.json b/src/main/resources/data/hardworkers/loot_table/blocks/miner_iron.json new file mode 100644 index 0000000..e7ab408 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/miner_iron.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:miner_iron" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/miner_netherite.json b/src/main/resources/data/hardworkers/loot_table/blocks/miner_netherite.json new file mode 100644 index 0000000..124fe00 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/miner_netherite.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:miner_netherite" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/miner_stone.json b/src/main/resources/data/hardworkers/loot_table/blocks/miner_stone.json new file mode 100644 index 0000000..aa0c0cf --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/miner_stone.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:miner_stone" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/miner_wood.json b/src/main/resources/data/hardworkers/loot_table/blocks/miner_wood.json new file mode 100644 index 0000000..d78efbb --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/miner_wood.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:miner_wood" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/recipes/miner_diamond.json b/src/main/resources/data/hardworkers/recipes/miner_diamond.json new file mode 100644 index 0000000..d01b45d --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/miner_diamond.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:diamond_pickaxe" }, + "B": { "item": "hardworkers:miner_iron" } + }, + "result": { "id": "hardworkers:miner_diamond", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/miner_iron.json b/src/main/resources/data/hardworkers/recipes/miner_iron.json new file mode 100644 index 0000000..b68388c --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/miner_iron.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:iron_pickaxe" }, + "B": { "item": "hardworkers:miner_stone" } + }, + "result": { "id": "hardworkers:miner_iron", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/miner_netherite.json b/src/main/resources/data/hardworkers/recipes/miner_netherite.json new file mode 100644 index 0000000..bf19879 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/miner_netherite.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:netherite_pickaxe" }, + "B": { "item": "hardworkers:miner_diamond" } + }, + "result": { "id": "hardworkers:miner_netherite", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/miner_stone.json b/src/main/resources/data/hardworkers/recipes/miner_stone.json new file mode 100644 index 0000000..f7613c6 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/miner_stone.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:stone_pickaxe" }, + "B": { "item": "hardworkers:miner_wood" } + }, + "result": { "id": "hardworkers:miner_stone", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/miner_wood.json b/src/main/resources/data/hardworkers/recipes/miner_wood.json new file mode 100644 index 0000000..1a0e783 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/miner_wood.json @@ -0,0 +1,10 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WWW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:wooden_pickaxe" } + }, + "result": { "id": "hardworkers:miner_wood", "count": 1 } +} From e97e6dba0a367de12696b8f2d717eae30cdac238 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 4 May 2026 03:00:17 +0000 Subject: [PATCH 11/17] Workers hold tier-matched tools; miner digs behind block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LumberjackRenderer and MinerRenderer now add ItemInHandLayer so the held tool is rendered in the entity's hand - LumberjackBlock.onPlace calls setTierEquipment() so the entity spawns holding the matching axe (wooden → netherite); equipment is persisted automatically via mob NBT - MinerBlock.onPlace calls setTierEquipment() so the entity spawns holding the matching pickaxe - MineForwardGoal now digs in facing.getOpposite() so the drill face points toward the player and mining goes into the rock behind the block - Removed gen_textures.py (textures will be created externally) https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- gen_textures.py | 420 ------------------ .../hardworkers/block/LumberjackBlock.java | 1 + .../hardworkers/block/MinerBlock.java | 1 + .../hardworkers/entity/LumberjackEntity.java | 20 +- .../hardworkers/entity/MinerEntity.java | 15 + .../entity/ai/MineForwardGoal.java | 3 +- .../entity/client/LumberjackRenderer.java | 3 + .../entity/client/MinerRenderer.java | 3 + 8 files changed, 40 insertions(+), 426 deletions(-) delete mode 100644 gen_textures.py diff --git a/gen_textures.py b/gen_textures.py deleted file mode 100644 index ada78a2..0000000 --- a/gen_textures.py +++ /dev/null @@ -1,420 +0,0 @@ -#!/usr/bin/env python3 -""" -Generate template textures for the Hard Workers mod. - -Block textures: 16x16, one per face per tier (front / back / top / bottom / side) -Entity texture: 64x64 standard Minecraft skin layout (edit this in any pixel editor) - -Run from the project root: python3 gen_textures.py -""" - -import struct, zlib, os - -# --------------------------------------------------------------------------- -# Minimal PNG writer (no Pillow required) -# --------------------------------------------------------------------------- - -def _chunk(tag, data): - crc = zlib.crc32(tag + data) & 0xFFFFFFFF - return struct.pack(">I", len(data)) + tag + data + struct.pack(">I", crc) - -def write_png(path, w, h, pixels): - """pixels: flat list of (r,g,b,a) tuples, row-major.""" - raw = b"".join( - b"\x00" + b"".join(bytes(pixels[y * w + x]) for x in range(w)) - for y in range(h) - ) - os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, "wb") as f: - f.write(b"\x89PNG\r\n\x1a\n") - f.write(_chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 6, 0, 0, 0))) - f.write(_chunk(b"IDAT", zlib.compress(raw))) - f.write(_chunk(b"IEND", b"")) - print(f" wrote {path}") - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - -def clamp(v): return max(0, min(255, v)) -def shade(c, d): return tuple(clamp(v + d) for v in c) - -def fill(pixels, w, x, y, fw, fh, color): - for py in range(y, y + fh): - for px in range(x, x + fw): - if 0 <= px < w and 0 <= py < len(pixels) // w: - pixels[py * w + px] = color - -# --------------------------------------------------------------------------- -# Block face templates (16x16) -# --------------------------------------------------------------------------- - -TIERS = { - "wood": (139, 100, 40), - "stone": (115, 115, 115), - "iron": (175, 180, 190), - "diamond": ( 45, 195, 210), - "netherite": ( 58, 28, 75), -} - -def block_face(base, variant): - r, g, b = base - pix = [] - for y in range(16): - for x in range(16): - border = x == 0 or x == 15 or y == 0 or y == 15 - dark = shade(base, -45) - if variant == "front": - # two glowing "eye" pixels, dark border, small mouth line - left_eye = (x in (4, 5) and y in (5, 6)) - right_eye = (x in (10, 11) and y in (5, 6)) - mouth = (5 <= x <= 10 and y == 11) - if border: - pix.append(dark + (255,)) - elif left_eye or right_eye: - pix.append((220, 220, 20, 255)) # yellow "eyes" - elif mouth: - pix.append(dark + (255,)) - else: - pix.append((r, g, b, 255)) - elif variant == "top": - light = shade(base, +35) - # cross marker so you can tell which way is up - cross = (x == 8 or y == 8) - if border: - pix.append(shade(base, -20) + (255,)) - elif cross: - pix.append(shade(base, +55) + (255,)) - else: - pix.append(light + (255,)) - elif variant == "bottom": - if border: - pix.append(shade(base, -65) + (255,)) - else: - pix.append(shade(base, -40) + (255,)) - elif variant == "back": - # diagonal stripe to distinguish back from side - stripe = ((x + y) % 6 == 0) - if border: - pix.append(dark + (255,)) - elif stripe: - pix.append(shade(base, -25) + (255,)) - else: - pix.append(shade(base, +10) + (255,)) - else: # side - if border: - pix.append(dark + (255,)) - else: - pix.append((r, g, b, 255)) - return pix - -BLOCK_TEX = "src/main/resources/assets/hardworkers/textures/block" - -for tier, color in TIERS.items(): - for face in ("front", "back", "top", "bottom", "side"): - write_png(f"{BLOCK_TEX}/lumberjack_{tier}_{face}.png", 16, 16, - block_face(color, face)) - -# Miner block textures — same colour palette, front shows a pickaxe-hint -# (two small bright dots for drill bits instead of eyes) -MINER_TIERS = { - "wood": (120, 85, 40), # warm brown, darker than lumberjack - "stone": ( 90, 90, 95), # darker grey - "iron": (155, 160, 175), # steely blue-grey - "diamond": ( 30, 170, 190), # deeper cyan - "netherite": ( 40, 18, 55), # deep purple -} - -def miner_face(base, variant): - r, g, b = base - pix = [] - for y in range(16): - for x in range(16): - border = x == 0 or x == 15 or y == 0 or y == 15 - dark = shade(base, -45) - if variant == "front": - # Two drill-bit dots + lower vent grill - bit_l = (x in (4, 5) and y in (4, 5, 6)) - bit_r = (x in (10, 11) and y in (4, 5, 6)) - grill = (4 <= x <= 11 and y in (10, 12) and x % 2 == 0) - if border: - pix.append(dark + (255,)) - elif bit_l or bit_r: - pix.append((210, 210, 255, 255)) # bright tip - elif grill: - pix.append(shade(base, -30) + (255,)) - else: - pix.append((r, g, b, 255)) - elif variant == "top": - cross = (x == 8 or y == 8) - if border: - pix.append(shade(base, -20) + (255,)) - elif cross: - pix.append(shade(base, +55) + (255,)) - else: - pix.append(shade(base, +30) + (255,)) - elif variant == "bottom": - if border: - pix.append(shade(base, -65) + (255,)) - else: - pix.append(shade(base, -40) + (255,)) - elif variant == "back": - stripe = ((x + y) % 6 == 0) - if border: - pix.append(dark + (255,)) - elif stripe: - pix.append(shade(base, -25) + (255,)) - else: - pix.append(shade(base, +10) + (255,)) - else: # side - if border: - pix.append(dark + (255,)) - else: - pix.append((r, g, b, 255)) - return pix - -for tier, color in MINER_TIERS.items(): - for face in ("front", "back", "top", "bottom", "side"): - write_png(f"{BLOCK_TEX}/miner_{tier}_{face}.png", 16, 16, - miner_face(color, face)) - -# --------------------------------------------------------------------------- -# Entity skin template (64x64, standard Minecraft player UV layout) -# -# Edit lumberjack.png in any pixel editor. UV regions: -# -# ┌─────────────────────────────────────────────────────────────────────────┐ -# │ HEAD │ HEAD OVERLAY (hat / hair) │ -# │ top (8,0 8×8) │ top (40,0 8×8) │ -# │ bot (16,0 8×8) │ bot (48,0 8×8) │ -# │ right (0,8 8×8) │ right (32,8 8×8) │ -# │ front (8,8 8×8) ← face │ front (40,8 8×8) ← hat brim │ -# │ left (16,8 8×8) │ left (48,8 8×8) │ -# │ back (24,8 8×8) │ back (56,8 8×8) │ -# ├─────────────────────────────────────────────────────────────────────────┤ -# │ RIGHT LEG │ BODY │ RIGHT ARM │ -# │ top (4,16 4×4) │ top (20,16 8×4) │ top (44,16 4×4) │ -# │ bot (8,16 4×4) │ bot (28,16 8×4) │ bot (48,16 4×4) │ -# │ r (0,20 4×12)│ r (16,20 4×12) │ r (40,20 4×12) │ -# │ f (4,20 4×12)│ f (20,20 8×12) ←shirt│ f (44,20 4×12) │ -# │ l (8,20 4×12)│ l (28,20 4×12) │ l (48,20 4×12) │ -# │ b (12,20 4×12)│ b (32,20 8×12) │ b (52,20 4×12) │ -# ├─────────────────────────────────────────────────────────────────────────┤ -# │ LEFT LEG (new-format 64px skin) │ LEFT ARM │ -# │ top (20,48 4×4) │ top (36,48 4×4) │ -# │ bot (24,48 4×4) │ bot (40,48 4×4) │ -# │ r (16,52 4×12) │ r (32,52 4×12) │ -# │ f (20,52 4×12) │ f (36,52 4×12) │ -# │ l (24,52 4×12) │ l (40,52 4×12) │ -# │ b (28,52 4×12) │ b (44,52 4×12) │ -# └─────────────────────────────────────────────────────────────────────────┘ -# --------------------------------------------------------------------------- - -SKIN = (200, 150, 100, 255) # face / hands -SKIN2 = (185, 135, 88, 255) # face side / darker skin -BEARD = ( 90, 55, 20, 255) # beard / hair -EYE = ( 40, 30, 20, 255) -HAT_F = ( 55, 90, 35, 255) # green logger hat – front -HAT_S = ( 45, 75, 28, 255) # hat sides -HAT_T = ( 65, 105, 42, 255) # hat top -SHIRT_F = ( 70, 90, 150, 255) # blue shirt front -SHIRT_S = ( 55, 72, 125, 255) # shirt sides / back -SUSP = ( 95, 65, 25, 255) # brown suspenders -PANTS_F = ( 45, 60, 100, 255) # dark trousers -PANTS_S = ( 38, 50, 85, 255) -BOOT = ( 48, 34, 18, 255) # leather boots -BOOT2 = ( 38, 26, 12, 255) -TRANS = ( 0, 0, 0, 0) # transparent (unused skin area) - -W = H = 64 -pixels = [TRANS] * (W * H) - -def f(x, y, fw, fh, color): - fill(pixels, W, x, y, fw, fh, color) - -# ── HEAD ──────────────────────────────────────────────────────────────────── -f( 8, 0, 8, 8, SKIN) # head top -f(16, 0, 8, 8, SKIN2) # head bottom -f( 0, 8, 8, 8, SKIN2) # head right -f( 8, 8, 8, 8, SKIN) # head front ← paint the face here -f(16, 8, 8, 8, SKIN2) # head left -f(24, 8, 8, 8, SKIN2) # head back -# face details on front (8,8) -f(10,10, 2, 2, EYE) # left eye -f(13,10, 2, 2, EYE) # right eye -f( 9,13, 6, 2, BEARD) # beard - -# ── HAT (head overlay) ────────────────────────────────────────────────────── -f(40, 0, 8, 8, HAT_T) # hat top -f(48, 0, 8, 8, HAT_S) # hat bottom inside -f(32, 8, 8, 8, HAT_S) # hat right -f(40, 8, 8, 8, HAT_F) # hat front ← wide brim suggestion -f(48, 8, 8, 8, HAT_S) # hat left -f(56, 8, 8, 8, HAT_S) # hat back - -# ── BODY ──────────────────────────────────────────────────────────────────── -f(20,16, 8, 4, SHIRT_S) # body top -f(28,16, 8, 4, SHIRT_S) # body bottom -f(16,20, 4,12, SHIRT_S) # body right -f(20,20, 8,12, SHIRT_F) # body front ← shirt -f(28,20, 4,12, SHIRT_S) # body left -f(32,20, 8,12, SHIRT_S) # body back -# suspenders drawn over shirt front -f(21,20, 1,12, SUSP) -f(26,20, 1,12, SUSP) - -# ── RIGHT LEG ─────────────────────────────────────────────────────────────── -f( 4,16, 4, 4, PANTS_S) # top -f( 8,16, 4, 4, PANTS_S) # bottom -f( 0,20, 4,12, PANTS_S) # right -f( 4,20, 4,12, PANTS_F) # front -f( 8,20, 4,12, PANTS_S) # left -f(12,20, 4,12, PANTS_S) # back -f( 0,28, 4, 4, BOOT2) # boot right -f( 4,28, 4, 4, BOOT) # boot front -f( 8,28, 4, 4, BOOT2) # boot left -f(12,28, 4, 4, BOOT2) # boot back - -# ── LEFT LEG ──────────────────────────────────────────────────────────────── -f(20,48, 4, 4, PANTS_S) -f(24,48, 4, 4, PANTS_S) -f(16,52, 4,12, PANTS_S) -f(20,52, 4,12, PANTS_F) -f(24,52, 4,12, PANTS_S) -f(28,52, 4,12, PANTS_S) -f(16,60, 4, 4, BOOT2) -f(20,60, 4, 4, BOOT) -f(24,60, 4, 4, BOOT2) -f(28,60, 4, 4, BOOT2) - -# ── RIGHT ARM ─────────────────────────────────────────────────────────────── -f(44,16, 4, 4, SHIRT_S) # top -f(48,16, 4, 4, SHIRT_S) # bottom -f(40,20, 4,12, SHIRT_S) # right -f(44,20, 4,12, SHIRT_F) # front -f(48,20, 4,12, SHIRT_S) # left -f(52,20, 4,12, SHIRT_S) # back -f(40,28, 4, 4, SKIN2) # hand right -f(44,28, 4, 4, SKIN) # hand front -f(48,28, 4, 4, SKIN2) # hand left -f(52,28, 4, 4, SKIN2) # hand back - -# ── LEFT ARM ──────────────────────────────────────────────────────────────── -f(36,48, 4, 4, SHIRT_S) -f(40,48, 4, 4, SHIRT_S) -f(32,52, 4,12, SHIRT_S) -f(36,52, 4,12, SHIRT_F) -f(40,52, 4,12, SHIRT_S) -f(44,52, 4,12, SHIRT_S) -f(32,60, 4, 4, SKIN2) -f(36,60, 4, 4, SKIN) -f(40,60, 4, 4, SKIN2) -f(44,60, 4, 4, SKIN2) - -write_png("src/main/resources/assets/hardworkers/textures/entity/lumberjack.png", - 64, 64, pixels) - -# --------------------------------------------------------------------------- -# Miner entity skin (64x64) -# Colour scheme: grey hard-hat, brown leather jacket, dark trousers, boots -# Same UV layout as the lumberjack skin — edit in any pixel editor. -# --------------------------------------------------------------------------- - -MHAT_F = ( 50, 50, 55, 255) # hard hat front (dark grey) -MHAT_S = ( 40, 40, 45, 255) # hard hat sides -MHAT_T = ( 60, 60, 65, 255) # hard hat top -MJACK_F = ( 90, 65, 35, 255) # leather jacket front (brown) -MJACK_S = ( 70, 50, 25, 255) # jacket sides -MPANTS_F = ( 55, 55, 60, 255) # dark grey trousers -MPANTS_S = ( 45, 45, 50, 255) -MBOOT = ( 35, 25, 15, 255) -MBOOT2 = ( 25, 18, 10, 255) -LAMP = (220, 200, 50, 255) # lamp dot on hard hat - -mp = [TRANS] * (64 * 64) - -def mf(x, y, fw, fh, color): - fill(mp, 64, x, y, fw, fh, color) - -# ── HEAD ──────────────────────────────────────────────────────────────────── -mf( 8, 0, 8, 8, SKIN) -mf(16, 0, 8, 8, SKIN2) -mf( 0, 8, 8, 8, SKIN2) -mf( 8, 8, 8, 8, SKIN) # face -mf(16, 8, 8, 8, SKIN2) -mf(24, 8, 8, 8, SKIN2) -mf(10,10, 2, 2, EYE) -mf(13,10, 2, 2, EYE) -mf( 9,13, 6, 2, BEARD) - -# ── HARD HAT (head overlay) ────────────────────────────────────────────────── -mf(40, 0, 8, 8, MHAT_T) # top — with lamp dot -mf(48, 0, 8, 8, MHAT_S) # bottom inside -mf(32, 8, 8, 8, MHAT_S) # right -mf(40, 8, 8, 8, MHAT_F) # front -mf(48, 8, 8, 8, MHAT_S) # left -mf(56, 8, 8, 8, MHAT_S) # back -# headlamp on hat top (centre-ish) -mf(43, 2, 2, 2, LAMP) - -# ── BODY — leather jacket ──────────────────────────────────────────────────── -mf(20,16, 8, 4, MJACK_S) -mf(28,16, 8, 4, MJACK_S) -mf(16,20, 4,12, MJACK_S) -mf(20,20, 8,12, MJACK_F) -mf(28,20, 4,12, MJACK_S) -mf(32,20, 8,12, MJACK_S) - -# ── RIGHT LEG ─────────────────────────────────────────────────────────────── -mf( 4,16, 4, 4, MPANTS_S) -mf( 8,16, 4, 4, MPANTS_S) -mf( 0,20, 4,12, MPANTS_S) -mf( 4,20, 4,12, MPANTS_F) -mf( 8,20, 4,12, MPANTS_S) -mf(12,20, 4,12, MPANTS_S) -mf( 0,28, 4, 4, MBOOT2) -mf( 4,28, 4, 4, MBOOT) -mf( 8,28, 4, 4, MBOOT2) -mf(12,28, 4, 4, MBOOT2) - -# ── LEFT LEG ──────────────────────────────────────────────────────────────── -mf(20,48, 4, 4, MPANTS_S) -mf(24,48, 4, 4, MPANTS_S) -mf(16,52, 4,12, MPANTS_S) -mf(20,52, 4,12, MPANTS_F) -mf(24,52, 4,12, MPANTS_S) -mf(28,52, 4,12, MPANTS_S) -mf(16,60, 4, 4, MBOOT2) -mf(20,60, 4, 4, MBOOT) -mf(24,60, 4, 4, MBOOT2) -mf(28,60, 4, 4, MBOOT2) - -# ── RIGHT ARM ─────────────────────────────────────────────────────────────── -mf(44,16, 4, 4, MJACK_S) -mf(48,16, 4, 4, MJACK_S) -mf(40,20, 4,12, MJACK_S) -mf(44,20, 4,12, MJACK_F) -mf(48,20, 4,12, MJACK_S) -mf(52,20, 4,12, MJACK_S) -mf(40,28, 4, 4, SKIN2) -mf(44,28, 4, 4, SKIN) -mf(48,28, 4, 4, SKIN2) -mf(52,28, 4, 4, SKIN2) - -# ── LEFT ARM ──────────────────────────────────────────────────────────────── -mf(36,48, 4, 4, MJACK_S) -mf(40,48, 4, 4, MJACK_S) -mf(32,52, 4,12, MJACK_S) -mf(36,52, 4,12, MJACK_F) -mf(40,52, 4,12, MJACK_S) -mf(44,52, 4,12, MJACK_S) -mf(32,60, 4, 4, SKIN2) -mf(36,60, 4, 4, SKIN) -mf(40,60, 4, 4, SKIN2) -mf(44,60, 4, 4, SKIN2) - -write_png("src/main/resources/assets/hardworkers/textures/entity/miner.png", - 64, 64, mp) - -print("\nDone! Edit the PNGs in any pixel editor (e.g. Aseprite, GIMP, Pixelorama).") -print("Block textures are 16x16; entity skins are 64x64 (standard Minecraft layout).") diff --git a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java index fe4c535..3480cbf 100644 --- a/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java +++ b/src/main/java/com/hardworkers/hardworkers/block/LumberjackBlock.java @@ -110,6 +110,7 @@ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldS LumberjackEntity lumberjack = ModEntities.LUMBERJACK.get().create(level); if (lumberjack != null) { lumberjack.setHomePosition(pos); + lumberjack.setTierEquipment(this.tier); lumberjack.moveTo(pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, 0f, 0f); level.addFreshEntity(lumberjack); } diff --git a/src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java b/src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java index fe4f405..ccf4aaf 100644 --- a/src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java +++ b/src/main/java/com/hardworkers/hardworkers/block/MinerBlock.java @@ -98,6 +98,7 @@ public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldS MinerEntity miner = ModEntities.MINER.get().create(level); if (miner != null) { miner.setHomePosition(pos); + miner.setTierEquipment(this.tier); miner.moveTo(pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, 0f, 0f); level.addFreshEntity(miner); } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/LumberjackEntity.java b/src/main/java/com/hardworkers/hardworkers/entity/LumberjackEntity.java index 6d00274..65d167a 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/LumberjackEntity.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/LumberjackEntity.java @@ -1,16 +1,20 @@ package com.hardworkers.hardworkers.entity; +import com.hardworkers.hardworkers.block.LumberjackTier; import com.hardworkers.hardworkers.entity.ai.ChopTreeGoal; import com.hardworkers.hardworkers.entity.ai.FindTreeGoal; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.FloatGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; public class LumberjackEntity extends PathfinderMob { @@ -76,11 +80,17 @@ public void setHomePosition(BlockPos pos) { this.homePosition = pos; } - public BlockPos getTargetTree() { - return targetTree; - } + public BlockPos getTargetTree() { return targetTree; } + public void setTargetTree(BlockPos pos) { this.targetTree = pos; } - public void setTargetTree(BlockPos pos) { - this.targetTree = pos; + public void setTierEquipment(LumberjackTier tier) { + var axe = switch (tier) { + case WOOD -> Items.WOODEN_AXE; + case STONE -> Items.STONE_AXE; + case IRON -> Items.IRON_AXE; + case DIAMOND -> Items.DIAMOND_AXE; + case NETHERITE -> Items.NETHERITE_AXE; + }; + setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(axe)); } } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java b/src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java index facd91a..4c54693 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/MinerEntity.java @@ -1,15 +1,19 @@ package com.hardworkers.hardworkers.entity; +import com.hardworkers.hardworkers.block.MinerTier; import com.hardworkers.hardworkers.entity.ai.MineForwardGoal; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.goal.FloatGoal; import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; public class MinerEntity extends PathfinderMob { @@ -71,4 +75,15 @@ public void readAdditionalSaveData(CompoundTag tag) { public int getCurrentDepth() { return currentDepth; } public void setCurrentDepth(int depth) { this.currentDepth = depth; } + + public void setTierEquipment(MinerTier tier) { + var pick = switch (tier) { + case WOOD -> Items.WOODEN_PICKAXE; + case STONE -> Items.STONE_PICKAXE; + case IRON -> Items.IRON_PICKAXE; + case DIAMOND -> Items.DIAMOND_PICKAXE; + case NETHERITE -> Items.NETHERITE_PICKAXE; + }; + setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(pick)); + } } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java index 23ba7e9..1ed6fdf 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/MineForwardGoal.java @@ -69,7 +69,8 @@ public void tick() { if (facing == null) return; BlockPos homePos = miner.getHomePosition(); - BlockPos facePos = homePos.relative(facing, depth); + // Mine behind the block (opposite of FACING so the drill-face points toward the player) + BlockPos facePos = homePos.relative(facing.getOpposite(), depth); // Walk toward the current mining face and look at it miner.getNavigation().moveTo(facePos.getX() + 0.5, facePos.getY(), facePos.getZ() + 0.5, 0.85); diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java index f054cec..88458e0 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java @@ -5,6 +5,7 @@ import net.minecraft.client.model.geom.ModelLayers; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.HumanoidMobRenderer; +import net.minecraft.client.renderer.entity.layers.ItemInHandLayer; import net.minecraft.resources.ResourceLocation; public class LumberjackRenderer extends HumanoidMobRenderer> { @@ -14,6 +15,7 @@ public class LumberjackRenderer extends HumanoidMobRenderer(ctx.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)), 0.5f); + addLayer(new ItemInHandLayer<>(this, ctx.getItemInHandRenderer())); } @Override @@ -21,3 +23,4 @@ public ResourceLocation getTextureLocation(LumberjackEntity entity) { return TEXTURE; } } + diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java index 6574eed..125382e 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java @@ -5,6 +5,7 @@ import net.minecraft.client.model.geom.ModelLayers; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.HumanoidMobRenderer; +import net.minecraft.client.renderer.entity.layers.ItemInHandLayer; import net.minecraft.resources.ResourceLocation; public class MinerRenderer extends HumanoidMobRenderer> { @@ -14,6 +15,7 @@ public class MinerRenderer extends HumanoidMobRenderer(ctx.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)), 0.5f); + addLayer(new ItemInHandLayer<>(this, ctx.getItemInHandRenderer())); } @Override @@ -21,3 +23,4 @@ public ResourceLocation getTextureLocation(MinerEntity entity) { return TEXTURE; } } + From 9899357e34aede7eaf0b41f59cd1f54d7b73c87c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 4 May 2026 08:00:08 +0000 Subject: [PATCH 12/17] =?UTF-8?q?Add=20Farmer=20worker=20=E2=80=94=205-tie?= =?UTF-8?q?r=20crop=20harvesting=20and=20auto-replanting=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Farmer blocks (wood→netherite) spawn a FarmerEntity that harvests mature crops in a 5×5 area, auto-replants them, and keeps surrounding farmland hydrated without needing a water source. Higher tiers harvest more frequently and force-trigger randomTick on nearby crops to accelerate growth. The farmer entity holds a tier-matched hoe. Supported crops: wheat/carrots/potatoes/beetroot, nether wart, cocoa, sweet berry bush, melons, and pumpkins. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../hardworkers/block/FarmerBlock.java | 148 +++++++++++++ .../hardworkers/block/FarmerTier.java | 21 ++ .../blockentity/FarmerBlockEntity.java | 186 ++++++++++++++++ .../hardworkers/client/ClientEvents.java | 2 + .../hardworkers/entity/FarmerEntity.java | 81 +++++++ .../entity/ai/HarvestCropsGoal.java | 209 ++++++++++++++++++ .../entity/client/FarmerRenderer.java | 25 +++ .../events/ModEventSubscriber.java | 12 + .../hardworkers/init/ModBlockEntities.java | 13 ++ .../hardworkers/init/ModBlocks.java | 22 ++ .../hardworkers/init/ModEntities.java | 9 + .../hardworkers/init/ModItems.java | 6 + .../blockstates/farmer_diamond.json | 8 + .../hardworkers/blockstates/farmer_iron.json | 8 + .../blockstates/farmer_netherite.json | 8 + .../hardworkers/blockstates/farmer_stone.json | 8 + .../hardworkers/blockstates/farmer_wood.json | 8 + .../assets/hardworkers/lang/en_us.json | 9 +- .../models/block/farmer_diamond.json | 12 + .../hardworkers/models/block/farmer_iron.json | 12 + .../models/block/farmer_netherite.json | 12 + .../models/block/farmer_stone.json | 12 + .../hardworkers/models/block/farmer_wood.json | 12 + .../models/item/farmer_diamond.json | 1 + .../hardworkers/models/item/farmer_iron.json | 1 + .../models/item/farmer_netherite.json | 1 + .../hardworkers/models/item/farmer_stone.json | 1 + .../hardworkers/models/item/farmer_wood.json | 1 + .../textures/block/farmer_diamond_back.png | Bin 0 -> 110 bytes .../textures/block/farmer_diamond_bottom.png | Bin 0 -> 110 bytes .../textures/block/farmer_diamond_front.png | Bin 0 -> 110 bytes .../textures/block/farmer_diamond_side.png | Bin 0 -> 110 bytes .../textures/block/farmer_diamond_top.png | Bin 0 -> 110 bytes .../textures/block/farmer_iron_back.png | Bin 0 -> 110 bytes .../textures/block/farmer_iron_bottom.png | Bin 0 -> 110 bytes .../textures/block/farmer_iron_front.png | Bin 0 -> 110 bytes .../textures/block/farmer_iron_side.png | Bin 0 -> 110 bytes .../textures/block/farmer_iron_top.png | Bin 0 -> 110 bytes .../textures/block/farmer_netherite_back.png | Bin 0 -> 110 bytes .../block/farmer_netherite_bottom.png | Bin 0 -> 110 bytes .../textures/block/farmer_netherite_front.png | Bin 0 -> 110 bytes .../textures/block/farmer_netherite_side.png | Bin 0 -> 110 bytes .../textures/block/farmer_netherite_top.png | Bin 0 -> 110 bytes .../textures/block/farmer_stone_back.png | Bin 0 -> 110 bytes .../textures/block/farmer_stone_bottom.png | Bin 0 -> 110 bytes .../textures/block/farmer_stone_front.png | Bin 0 -> 110 bytes .../textures/block/farmer_stone_side.png | Bin 0 -> 110 bytes .../textures/block/farmer_stone_top.png | Bin 0 -> 110 bytes .../textures/block/farmer_wood_back.png | Bin 0 -> 110 bytes .../textures/block/farmer_wood_bottom.png | Bin 0 -> 110 bytes .../textures/block/farmer_wood_front.png | Bin 0 -> 110 bytes .../textures/block/farmer_wood_side.png | Bin 0 -> 110 bytes .../textures/block/farmer_wood_top.png | Bin 0 -> 110 bytes .../hardworkers/textures/entity/farmer.png | Bin 0 -> 331 bytes .../loot_table/blocks/farmer_diamond.json | 8 + .../loot_table/blocks/farmer_iron.json | 8 + .../loot_table/blocks/farmer_netherite.json | 8 + .../loot_table/blocks/farmer_stone.json | 8 + .../loot_table/blocks/farmer_wood.json | 8 + .../hardworkers/recipes/farmer_diamond.json | 11 + .../data/hardworkers/recipes/farmer_iron.json | 11 + .../hardworkers/recipes/farmer_netherite.json | 11 + .../hardworkers/recipes/farmer_stone.json | 11 + .../data/hardworkers/recipes/farmer_wood.json | 10 + 64 files changed, 941 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hardworkers/hardworkers/block/FarmerBlock.java create mode 100644 src/main/java/com/hardworkers/hardworkers/block/FarmerTier.java create mode 100644 src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/FarmerEntity.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/ai/HarvestCropsGoal.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java create mode 100644 src/main/resources/assets/hardworkers/blockstates/farmer_diamond.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/farmer_iron.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/farmer_netherite.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/farmer_stone.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/farmer_wood.json create mode 100644 src/main/resources/assets/hardworkers/models/block/farmer_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/block/farmer_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/block/farmer_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/block/farmer_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/block/farmer_wood.json create mode 100644 src/main/resources/assets/hardworkers/models/item/farmer_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/item/farmer_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/item/farmer_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/item/farmer_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/item/farmer_wood.json create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_diamond_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_diamond_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_diamond_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_diamond_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_diamond_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_iron_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_iron_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_iron_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_iron_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_iron_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_netherite_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_netherite_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_netherite_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_netherite_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_netherite_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_stone_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_stone_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_stone_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_stone_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_stone_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_wood_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_wood_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_wood_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_wood_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/farmer_wood_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/entity/farmer.png create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/farmer_diamond.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/farmer_iron.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/farmer_netherite.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/farmer_stone.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/farmer_wood.json create mode 100644 src/main/resources/data/hardworkers/recipes/farmer_diamond.json create mode 100644 src/main/resources/data/hardworkers/recipes/farmer_iron.json create mode 100644 src/main/resources/data/hardworkers/recipes/farmer_netherite.json create mode 100644 src/main/resources/data/hardworkers/recipes/farmer_stone.json create mode 100644 src/main/resources/data/hardworkers/recipes/farmer_wood.json diff --git a/src/main/java/com/hardworkers/hardworkers/block/FarmerBlock.java b/src/main/java/com/hardworkers/hardworkers/block/FarmerBlock.java new file mode 100644 index 0000000..1a4d2e0 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/block/FarmerBlock.java @@ -0,0 +1,148 @@ +package com.hardworkers.hardworkers.block; + +import com.hardworkers.hardworkers.blockentity.FarmerBlockEntity; +import com.hardworkers.hardworkers.entity.FarmerEntity; +import com.hardworkers.hardworkers.init.ModBlockEntities; +import com.hardworkers.hardworkers.init.ModEntities; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.MapColor; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; + +import javax.annotation.Nullable; + +public class FarmerBlock extends BaseEntityBlock { + + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + + public static final MapCodec WOOD_CODEC = simpleCodec(p -> new FarmerBlock(FarmerTier.WOOD, p)); + public static final MapCodec STONE_CODEC = simpleCodec(p -> new FarmerBlock(FarmerTier.STONE, p)); + public static final MapCodec IRON_CODEC = simpleCodec(p -> new FarmerBlock(FarmerTier.IRON, p)); + public static final MapCodec DIAMOND_CODEC = simpleCodec(p -> new FarmerBlock(FarmerTier.DIAMOND, p)); + public static final MapCodec NETHERITE_CODEC = simpleCodec(p -> new FarmerBlock(FarmerTier.NETHERITE, p)); + + private final FarmerTier tier; + + public FarmerBlock(FarmerTier tier, BlockBehaviour.Properties properties) { + super(properties); + this.tier = tier; + registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH)); + } + + public FarmerTier getTier() { return tier; } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); + } + + @Override + public MapCodec codec() { + return switch (tier) { + case WOOD -> WOOD_CODEC; + case STONE -> STONE_CODEC; + case IRON -> IRON_CODEC; + case DIAMOND -> DIAMOND_CODEC; + case NETHERITE -> NETHERITE_CODEC; + }; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new FarmerBlockEntity(pos, state); + } + + @Nullable + @Override + public BlockEntityTicker getTicker(Level level, BlockState state, BlockEntityType type) { + return level.isClientSide() ? null + : createTickerHelper(type, ModBlockEntities.FARMER_BLOCK_ENTITY.get(), FarmerBlockEntity::serverTick); + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + super.onPlace(state, level, pos, oldState, movedByPiston); + if (!level.isClientSide()) { + FarmerEntity farmer = ModEntities.FARMER.get().create(level); + if (farmer != null) { + farmer.setHomePosition(pos); + farmer.setTierEquipment(this.tier); + farmer.moveTo(pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, 0f, 0f); + level.addFreshEntity(farmer); + } + } + } + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!level.isClientSide() && !state.is(newState.getBlock())) { + if (level.getBlockEntity(pos) instanceof FarmerBlockEntity be) { + Containers.dropContents(level, pos, be); + } + AABB searchArea = new AABB(pos).inflate(3.0); + level.getEntitiesOfClass(FarmerEntity.class, searchArea) + .stream() + .filter(e -> pos.equals(e.getHomePosition())) + .forEach(FarmerEntity::discard); + } + super.onRemove(state, level, pos, newState, movedByPiston); + } + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, + Player player, BlockHitResult hitResult) { + if (!level.isClientSide() && level.getBlockEntity(pos) instanceof FarmerBlockEntity be) { + player.displayClientMessage(be.getStorageStatus(), true); + } + return InteractionResult.sidedSuccess(level.isClientSide()); + } + + public static BlockBehaviour.Properties baseProperties(MapColor color) { + return BlockBehaviour.Properties.of() + .mapColor(color) + .sound(SoundType.WOOD) + .strength(2.0f); + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/block/FarmerTier.java b/src/main/java/com/hardworkers/hardworkers/block/FarmerTier.java new file mode 100644 index 0000000..0cdb0b9 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/block/FarmerTier.java @@ -0,0 +1,21 @@ +package com.hardworkers.hardworkers.block; + +public enum FarmerTier { + WOOD ("wood", 30, 0), + STONE ("stone", 24, 200), + IRON ("iron", 18, 100), + DIAMOND ("diamond", 12, 50), + NETHERITE("netherite", 6, 25); + + public final String id; + /** Ticks between each crop harvest action. */ + public final int harvestInterval; + /** Ticks between forced random-tick calls on crops in range (0 = no boost). */ + public final int growthBoostInterval; + + FarmerTier(String id, int harvestInterval, int growthBoostInterval) { + this.id = id; + this.harvestInterval = harvestInterval; + this.growthBoostInterval = growthBoostInterval; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java b/src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java new file mode 100644 index 0000000..4553c62 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java @@ -0,0 +1,186 @@ +package com.hardworkers.hardworkers.blockentity; + +import com.hardworkers.hardworkers.block.FarmerBlock; +import com.hardworkers.hardworkers.block.FarmerTier; +import com.hardworkers.hardworkers.init.ModBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CocoaBlock; +import net.minecraft.world.level.block.CropBlock; +import net.minecraft.world.level.block.FarmBlock; +import net.minecraft.world.level.block.NetherWartBlock; +import net.minecraft.world.level.block.StemBlock; +import net.minecraft.world.level.block.SweetBerryBushBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.wrapper.InvWrapper; + +/** + * Storage block for the farmer worker. + * + * Server tick responsibilities: + * 1. Keep all farmland within HYDRATION_RADIUS at maximum moisture so the + * block substitutes for a water source in a typical farm layout. + * 2. Call randomTick on every crop in the 5×5 harvest area at the tier's + * growthBoostInterval so higher tiers accelerate plant growth. + */ +public class FarmerBlockEntity extends BlockEntity implements Container { + + public static final int SLOT_COUNT = 27; + private static final int FARM_RADIUS = 2; // 5×5 harvest area + private static final int HYDRATION_RADIUS = 3; // slightly wider to cover farm edges + + private NonNullList items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); + private final IItemHandler itemHandler = new InvWrapper(this); + + public FarmerBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.FARMER_BLOCK_ENTITY.get(), pos, state); + } + + public IItemHandler getItemHandler() { return itemHandler; } + + // ------------------------------------------------------------------------- + // Server tick + // ------------------------------------------------------------------------- + + public static void serverTick(Level level, BlockPos pos, BlockState state, FarmerBlockEntity be) { + if (!(level instanceof ServerLevel serverLevel)) return; + + // Hydrate surrounding farmland every 20 ticks (well ahead of random-tick decay) + if (level.getGameTime() % 20 == 0) { + hydrateFarmland(level, pos); + } + + FarmerTier tier = getTier(serverLevel, pos); + if (tier.growthBoostInterval > 0 && level.getGameTime() % tier.growthBoostInterval == 0) { + boostCropGrowth(serverLevel, pos); + } + } + + private static void hydrateFarmland(Level level, BlockPos center) { + for (int dx = -HYDRATION_RADIUS; dx <= HYDRATION_RADIUS; dx++) { + for (int dz = -HYDRATION_RADIUS; dz <= HYDRATION_RADIUS; dz++) { + for (int dy = -2; dy <= 1; dy++) { + BlockPos p = center.offset(dx, dy, dz); + BlockState s = level.getBlockState(p); + if (s.is(Blocks.FARMLAND) && s.getValue(FarmBlock.MOISTURE) < 7) { + level.setBlock(p, s.setValue(FarmBlock.MOISTURE, 7), Block.UPDATE_CLIENTS); + } + } + } + } + } + + private static void boostCropGrowth(ServerLevel level, BlockPos center) { + for (int dx = -FARM_RADIUS; dx <= FARM_RADIUS; dx++) { + for (int dz = -FARM_RADIUS; dz <= FARM_RADIUS; dz++) { + for (int dy = -1; dy <= 3; dy++) { + BlockPos p = center.offset(dx, dy, dz); + BlockState s = level.getBlockState(p); + Block b = s.getBlock(); + if (b instanceof CropBlock + || b instanceof NetherWartBlock + || b instanceof CocoaBlock + || b instanceof SweetBerryBushBlock + || b instanceof StemBlock) { + b.randomTick(s, level, p, level.random); + } + } + } + } + } + + private static FarmerTier getTier(Level level, BlockPos pos) { + BlockState s = level.getBlockState(pos); + if (s.getBlock() instanceof FarmerBlock fb) return fb.getTier(); + return FarmerTier.WOOD; + } + + // ------------------------------------------------------------------------- + // Storage API + // ------------------------------------------------------------------------- + + public ItemStack insertItem(ItemStack incoming) { + if (incoming.isEmpty()) return ItemStack.EMPTY; + for (int i = 0; i < SLOT_COUNT; i++) { + ItemStack slot = items.get(i); + if (!slot.isEmpty() && slot.getItem() == incoming.getItem() + && ItemStack.isSameItemSameComponents(slot, incoming)) { + int space = slot.getMaxStackSize() - slot.getCount(); + if (space > 0) { + int toAdd = Math.min(space, incoming.getCount()); + slot.grow(toAdd); + setChanged(); + incoming = incoming.copyWithCount(incoming.getCount() - toAdd); + if (incoming.isEmpty()) return ItemStack.EMPTY; + } + } + } + for (int i = 0; i < SLOT_COUNT; i++) { + if (items.get(i).isEmpty()) { + int toStore = Math.min(incoming.getCount(), incoming.getMaxStackSize()); + items.set(i, incoming.copyWithCount(toStore)); + setChanged(); + int leftover = incoming.getCount() - toStore; + return leftover > 0 ? incoming.copyWithCount(leftover) : ItemStack.EMPTY; + } + } + return incoming; + } + + public boolean isFull() { + for (ItemStack s : items) { + if (s.isEmpty() || s.getCount() < s.getMaxStackSize()) return false; + } + return true; + } + + public Component getStorageStatus() { + int used = 0, total = 0; + for (ItemStack s : items) { if (!s.isEmpty()) { used++; total += s.getCount(); } } + if (used == 0) return Component.literal("Storage: empty"); + return Component.literal("Storage: " + used + "/" + SLOT_COUNT + " slots (" + total + " items)"); + } + + // ------------------------------------------------------------------------- + // Container + // ------------------------------------------------------------------------- + + @Override public int getContainerSize() { return SLOT_COUNT; } + @Override public boolean isEmpty() { for (ItemStack s : items) { if (!s.isEmpty()) return false; } return true; } + @Override public ItemStack getItem(int slot) { return slot >= 0 && slot < SLOT_COUNT ? items.get(slot) : ItemStack.EMPTY; } + @Override public ItemStack removeItem(int slot, int amount) { ItemStack r = ContainerHelper.removeItem(items, slot, amount); if (!r.isEmpty()) setChanged(); return r; } + @Override public ItemStack removeItemNoUpdate(int slot) { return ContainerHelper.takeItem(items, slot); } + @Override public void setItem(int slot, ItemStack stack) { if (slot >= 0 && slot < SLOT_COUNT) { items.set(slot, stack); setChanged(); } } + @Override public boolean stillValid(Player player) { return true; } + @Override public void clearContent() { items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); setChanged(); } + + // ------------------------------------------------------------------------- + // NBT + // ------------------------------------------------------------------------- + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + ContainerHelper.saveAllItems(tag, items, registries); + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); + ContainerHelper.loadAllItems(tag, items, registries); + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java b/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java index ec1ca76..6b82c1c 100644 --- a/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java +++ b/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java @@ -1,6 +1,7 @@ package com.hardworkers.hardworkers.client; import com.hardworkers.hardworkers.HardWorkers; +import com.hardworkers.hardworkers.entity.client.FarmerRenderer; import com.hardworkers.hardworkers.entity.client.LumberjackRenderer; import com.hardworkers.hardworkers.entity.client.MinerRenderer; import com.hardworkers.hardworkers.init.ModEntities; @@ -16,5 +17,6 @@ public class ClientEvents { public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerEntityRenderer(ModEntities.LUMBERJACK.get(), LumberjackRenderer::new); event.registerEntityRenderer(ModEntities.MINER.get(), MinerRenderer::new); + event.registerEntityRenderer(ModEntities.FARMER.get(), FarmerRenderer::new); } } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/FarmerEntity.java b/src/main/java/com/hardworkers/hardworkers/entity/FarmerEntity.java new file mode 100644 index 0000000..b278661 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/FarmerEntity.java @@ -0,0 +1,81 @@ +package com.hardworkers.hardworkers.entity; + +import com.hardworkers.hardworkers.block.FarmerTier; +import com.hardworkers.hardworkers.entity.ai.HarvestCropsGoal; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.goal.FloatGoal; +import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; +import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; + +public class FarmerEntity extends PathfinderMob { + + private BlockPos homePosition = BlockPos.ZERO; + + public FarmerEntity(EntityType type, Level level) { + super(type, level); + } + + public static AttributeSupplier.Builder createAttributes() { + return PathfinderMob.createMobAttributes() + .add(Attributes.MAX_HEALTH, 20.0) + .add(Attributes.MOVEMENT_SPEED, 0.3) + .add(Attributes.FOLLOW_RANGE, 16.0); + } + + @Override + protected void registerGoals() { + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new HarvestCropsGoal(this)); + goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.6)); + goalSelector.addGoal(3, new RandomLookAroundGoal(this)); + } + + @Override + public void tick() { + super.tick(); + if (!level().isClientSide() && tickCount % 100 == 0 && !homePosition.equals(BlockPos.ZERO)) { + if (level().getBlockState(homePosition).isAir()) { + discard(); + } + } + } + + @Override + public void addAdditionalSaveData(CompoundTag tag) { + super.addAdditionalSaveData(tag); + tag.putInt("HomeX", homePosition.getX()); + tag.putInt("HomeY", homePosition.getY()); + tag.putInt("HomeZ", homePosition.getZ()); + } + + @Override + public void readAdditionalSaveData(CompoundTag tag) { + super.readAdditionalSaveData(tag); + if (tag.contains("HomeX")) { + homePosition = new BlockPos(tag.getInt("HomeX"), tag.getInt("HomeY"), tag.getInt("HomeZ")); + } + } + + public BlockPos getHomePosition() { return homePosition; } + public void setHomePosition(BlockPos pos) { this.homePosition = pos; } + + public void setTierEquipment(FarmerTier tier) { + var hoe = switch (tier) { + case WOOD -> Items.WOODEN_HOE; + case STONE -> Items.STONE_HOE; + case IRON -> Items.IRON_HOE; + case DIAMOND -> Items.DIAMOND_HOE; + case NETHERITE -> Items.NETHERITE_HOE; + }; + setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(hoe)); + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/HarvestCropsGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/HarvestCropsGoal.java new file mode 100644 index 0000000..52e31e4 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/HarvestCropsGoal.java @@ -0,0 +1,209 @@ +package com.hardworkers.hardworkers.entity.ai; + +import com.hardworkers.hardworkers.block.FarmerBlock; +import com.hardworkers.hardworkers.block.FarmerTier; +import com.hardworkers.hardworkers.blockentity.FarmerBlockEntity; +import com.hardworkers.hardworkers.entity.FarmerEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.CocoaBlock; +import net.minecraft.world.level.block.CropBlock; +import net.minecraft.world.level.block.NetherWartBlock; +import net.minecraft.world.level.block.SweetBerryBushBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.IntegerProperty; + +import java.util.EnumSet; +import java.util.List; + +/** + * Scans the 5×5 area around the farmer's home block for mature crops, + * walks to each one, harvests it, replants it (resetting the block to age 0 + * so the stem/plant remains), and deposits the drops into storage. + * + * Supported crop types: + * CropBlock – wheat, carrots, potatoes, beetroot + * NetherWartBlock – nether wart + * CocoaBlock – cocoa beans (facing preserved on replant) + * SweetBerryBushBlock – sweet berries (set to age 1 on replant) + * Melon / Pumpkin – fruit block is removed; stem stays to regrow + */ +public class HarvestCropsGoal extends Goal { + + private static final int FARM_RADIUS = 2; // 5×5 area + private static final double REACH_SQ = 6.25; // 2.5-block reach + private static final int SCAN_Y_MIN = -1; + private static final int SCAN_Y_MAX = 3; + + private final FarmerEntity farmer; + private BlockPos targetCrop = null; + private int harvestTimer = 0; + private int searchCooldown = 0; + + public HarvestCropsGoal(FarmerEntity farmer) { + this.farmer = farmer; + setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + if (isStorageFull()) return false; + if (searchCooldown > 0) { searchCooldown--; return false; } + targetCrop = findMatureCrop(); + return targetCrop != null; + } + + @Override + public boolean canContinueToUse() { + return targetCrop != null && !isStorageFull(); + } + + @Override + public void start() { + harvestTimer = 0; + navigateTo(targetCrop); + } + + @Override + public void stop() { + targetCrop = null; + harvestTimer = 0; + farmer.getNavigation().stop(); + } + + @Override + public void tick() { + if (targetCrop == null) return; + + // Re-validate: another farmer (or random decay) may have cleared it + if (!isMatureCrop(farmer.level().getBlockState(targetCrop))) { + targetCrop = findMatureCrop(); + if (targetCrop == null) { searchCooldown = 40; return; } + } + + farmer.getLookControl().setLookAt( + targetCrop.getX() + 0.5, targetCrop.getY() + 0.5, targetCrop.getZ() + 0.5); + + double distSq = farmer.distanceToSqr( + targetCrop.getX() + 0.5, targetCrop.getY() + 0.5, targetCrop.getZ() + 0.5); + + if (distSq > REACH_SQ) { + // Navigate to the farmland tile below the crop so the entity walks on ground + navigateTo(targetCrop); + return; + } + + harvestTimer++; + if (harvestTimer < harvestInterval()) return; + harvestTimer = 0; + + harvest(targetCrop); + + // Find the next crop immediately + targetCrop = findMatureCrop(); + if (targetCrop == null) searchCooldown = 40; + } + + // ------------------------------------------------------------------------- + + private void harvest(BlockPos pos) { + Level level = farmer.level(); + if (!(level instanceof ServerLevel serverLevel)) return; + + BlockState state = serverLevel.getBlockState(pos); + if (!isMatureCrop(state)) return; + + Block block = state.getBlock(); + List drops = Block.getDrops(state, serverLevel, pos, null); + + // Replant by resetting to the youngest growth stage + if (block instanceof CropBlock crop) { + serverLevel.setBlock(pos, crop.defaultBlockState(), Block.UPDATE_ALL); + } else if (block instanceof NetherWartBlock) { + serverLevel.setBlock(pos, Blocks.NETHER_WART.defaultBlockState(), Block.UPDATE_ALL); + } else if (block instanceof CocoaBlock) { + // Keep the attached facing when replanting + serverLevel.setBlock(pos, state.setValue(CocoaBlock.AGE, 0), Block.UPDATE_ALL); + } else if (block instanceof SweetBerryBushBlock) { + // Age 1 = regrowth stage (age 0 is the initial tiny sprout) + serverLevel.setBlock(pos, state.setValue(SweetBerryBushBlock.AGE, 1), Block.UPDATE_ALL); + } else { + // Melon / pumpkin — just remove the fruit; stem regrows on its own + serverLevel.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + } + + serverLevel.levelEvent(2001, pos, Block.getId(state)); + depositItems(serverLevel, drops); + } + + private BlockPos findMatureCrop() { + Level level = farmer.level(); + BlockPos home = farmer.getHomePosition(); + + BlockPos nearest = null; + double nearestDistSq = Double.MAX_VALUE; + + for (int dx = -FARM_RADIUS; dx <= FARM_RADIUS; dx++) { + for (int dz = -FARM_RADIUS; dz <= FARM_RADIUS; dz++) { + for (int dy = SCAN_Y_MIN; dy <= SCAN_Y_MAX; dy++) { + BlockPos candidate = home.offset(dx, dy, dz); + if (isMatureCrop(level.getBlockState(candidate))) { + double distSq = home.distSqr(candidate); + if (distSq < nearestDistSq) { + nearestDistSq = distSq; + nearest = candidate; + } + } + } + } + } + return nearest; + } + + private boolean isMatureCrop(BlockState state) { + Block block = state.getBlock(); + if (block instanceof CropBlock crop) return crop.isMaxAge(state); + if (block instanceof NetherWartBlock) return state.getValue(NetherWartBlock.AGE) == 3; + if (block instanceof CocoaBlock) return state.getValue(CocoaBlock.AGE) == 2; + if (block instanceof SweetBerryBushBlock) return state.getValue(SweetBerryBushBlock.AGE) == 3; + if (block == Blocks.MELON || block == Blocks.PUMPKIN) return true; + return false; + } + + private void navigateTo(BlockPos cropPos) { + // Walk to the tile at the crop's Y level (entity walks on same Y as crop for tall crops, + // or one below for standard crops; navigator handles the adjustment) + farmer.getNavigation().moveTo(cropPos.getX() + 0.5, cropPos.getY(), cropPos.getZ() + 0.5, 0.9); + } + + private int harvestInterval() { + BlockState homeState = farmer.level().getBlockState(farmer.getHomePosition()); + if (homeState.getBlock() instanceof FarmerBlock fb) return fb.getTier().harvestInterval; + return 30; + } + + private boolean isStorageFull() { + BlockEntity be = farmer.level().getBlockEntity(farmer.getHomePosition()); + return be instanceof FarmerBlockEntity storage && storage.isFull(); + } + + private void depositItems(ServerLevel level, List drops) { + BlockEntity be = level.getBlockEntity(farmer.getHomePosition()); + if (be instanceof FarmerBlockEntity storage) { + for (ItemStack drop : drops) { + ItemStack remainder = storage.insertItem(drop); + if (!remainder.isEmpty()) { + Block.popResource(level, farmer.blockPosition(), remainder); + } + } + } else { + drops.forEach(d -> Block.popResource(level, farmer.blockPosition(), d)); + } + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java new file mode 100644 index 0000000..8bee274 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java @@ -0,0 +1,25 @@ +package com.hardworkers.hardworkers.entity.client; + +import com.hardworkers.hardworkers.entity.FarmerEntity; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.HumanoidMobRenderer; +import net.minecraft.client.renderer.entity.layers.ItemInHandLayer; +import net.minecraft.resources.ResourceLocation; + +public class FarmerRenderer extends HumanoidMobRenderer> { + + private static final ResourceLocation TEXTURE = + ResourceLocation.fromNamespaceAndPath("hardworkers", "textures/entity/farmer.png"); + + public FarmerRenderer(EntityRendererProvider.Context ctx) { + super(ctx, new HumanoidModel<>(ctx.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)), 0.5f); + addLayer(new ItemInHandLayer<>(this, ctx.getItemInHandRenderer())); + } + + @Override + public ResourceLocation getTextureLocation(FarmerEntity entity) { + return TEXTURE; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java index 70d8d58..c60fe21 100644 --- a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java +++ b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java @@ -1,6 +1,7 @@ package com.hardworkers.hardworkers.events; import com.hardworkers.hardworkers.HardWorkers; +import com.hardworkers.hardworkers.entity.FarmerEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; import com.hardworkers.hardworkers.entity.MinerEntity; import com.hardworkers.hardworkers.init.ModBlockEntities; @@ -21,6 +22,7 @@ public class ModEventSubscriber { public static void onEntityAttributeCreation(EntityAttributeCreationEvent event) { event.put(ModEntities.LUMBERJACK.get(), LumberjackEntity.createAttributes().build()); event.put(ModEntities.MINER.get(), MinerEntity.createAttributes().build()); + event.put(ModEntities.FARMER.get(), FarmerEntity.createAttributes().build()); } @SubscribeEvent @@ -36,6 +38,11 @@ public static void onBuildCreativeTab(BuildCreativeModeTabContentsEvent event) { event.accept(ModItems.MINER_IRON_ITEM); event.accept(ModItems.MINER_DIAMOND_ITEM); event.accept(ModItems.MINER_NETHERITE_ITEM); + event.accept(ModItems.FARMER_WOOD_ITEM); + event.accept(ModItems.FARMER_STONE_ITEM); + event.accept(ModItems.FARMER_IRON_ITEM); + event.accept(ModItems.FARMER_DIAMOND_ITEM); + event.accept(ModItems.FARMER_NETHERITE_ITEM); } } @@ -51,5 +58,10 @@ public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { ModBlockEntities.MINER_BLOCK_ENTITY.get(), (be, side) -> be.getItemHandler() ); + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + ModBlockEntities.FARMER_BLOCK_ENTITY.get(), + (be, side) -> be.getItemHandler() + ); } } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java index 96b7564..3839471 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java @@ -1,6 +1,7 @@ package com.hardworkers.hardworkers.init; import com.hardworkers.hardworkers.HardWorkers; +import com.hardworkers.hardworkers.blockentity.FarmerBlockEntity; import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; import com.hardworkers.hardworkers.blockentity.MinerBlockEntity; import net.minecraft.core.registries.Registries; @@ -36,5 +37,17 @@ public class ModBlockEntities { ModBlocks.MINER_NETHERITE.get() ).build(null) ); + + public static final DeferredHolder, BlockEntityType> FARMER_BLOCK_ENTITY = + BLOCK_ENTITY_TYPES.register("farmer_block", () -> + BlockEntityType.Builder.of( + FarmerBlockEntity::new, + ModBlocks.FARMER_WOOD.get(), + ModBlocks.FARMER_STONE.get(), + ModBlocks.FARMER_IRON.get(), + ModBlocks.FARMER_DIAMOND.get(), + ModBlocks.FARMER_NETHERITE.get() + ).build(null) + ); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java index 9ffe9a2..0eec895 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java @@ -1,6 +1,8 @@ package com.hardworkers.hardworkers.init; import com.hardworkers.hardworkers.HardWorkers; +import com.hardworkers.hardworkers.block.FarmerBlock; +import com.hardworkers.hardworkers.block.FarmerTier; import com.hardworkers.hardworkers.block.LumberjackBlock; import com.hardworkers.hardworkers.block.LumberjackTier; import com.hardworkers.hardworkers.block.MinerBlock; @@ -52,5 +54,25 @@ public class ModBlocks { public static final DeferredBlock MINER_NETHERITE = BLOCKS.register("miner_netherite", () -> new MinerBlock(MinerTier.NETHERITE, MinerBlock.baseProperties(MapColor.COLOR_BLACK))); + + public static final DeferredBlock FARMER_WOOD = + BLOCKS.register("farmer_wood", + () -> new FarmerBlock(FarmerTier.WOOD, FarmerBlock.baseProperties(MapColor.PLANT))); + + public static final DeferredBlock FARMER_STONE = + BLOCKS.register("farmer_stone", + () -> new FarmerBlock(FarmerTier.STONE, FarmerBlock.baseProperties(MapColor.STONE))); + + public static final DeferredBlock FARMER_IRON = + BLOCKS.register("farmer_iron", + () -> new FarmerBlock(FarmerTier.IRON, FarmerBlock.baseProperties(MapColor.METAL))); + + public static final DeferredBlock FARMER_DIAMOND = + BLOCKS.register("farmer_diamond", + () -> new FarmerBlock(FarmerTier.DIAMOND, FarmerBlock.baseProperties(MapColor.DIAMOND))); + + public static final DeferredBlock FARMER_NETHERITE = + BLOCKS.register("farmer_netherite", + () -> new FarmerBlock(FarmerTier.NETHERITE, FarmerBlock.baseProperties(MapColor.COLOR_BLACK))); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java index 8170676..81df28b 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java @@ -1,6 +1,7 @@ package com.hardworkers.hardworkers.init; import com.hardworkers.hardworkers.HardWorkers; +import com.hardworkers.hardworkers.entity.FarmerEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; import com.hardworkers.hardworkers.entity.MinerEntity; import net.minecraft.core.registries.Registries; @@ -29,5 +30,13 @@ public class ModEntities { .clientTrackingRange(80) .build("miner") ); + + public static final DeferredHolder, EntityType> FARMER = + ENTITY_TYPES.register("farmer", () -> + EntityType.Builder.of(FarmerEntity::new, MobCategory.MISC) + .sized(0.6f, 1.95f) + .clientTrackingRange(10) + .build("farmer") + ); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java index 3bf5ae6..7d1e830 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java @@ -20,5 +20,11 @@ public class ModItems { public static final DeferredItem MINER_IRON_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_IRON); public static final DeferredItem MINER_DIAMOND_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_DIAMOND); public static final DeferredItem MINER_NETHERITE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.MINER_NETHERITE); + + public static final DeferredItem FARMER_WOOD_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_WOOD); + public static final DeferredItem FARMER_STONE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_STONE); + public static final DeferredItem FARMER_IRON_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_IRON); + public static final DeferredItem FARMER_DIAMOND_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_DIAMOND); + public static final DeferredItem FARMER_NETHERITE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_NETHERITE); } diff --git a/src/main/resources/assets/hardworkers/blockstates/farmer_diamond.json b/src/main/resources/assets/hardworkers/blockstates/farmer_diamond.json new file mode 100644 index 0000000..181dee8 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/farmer_diamond.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/farmer_diamond" }, + "facing=east": { "model": "hardworkers:block/farmer_diamond", "y": 90 }, + "facing=south": { "model": "hardworkers:block/farmer_diamond", "y": 180 }, + "facing=west": { "model": "hardworkers:block/farmer_diamond", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/farmer_iron.json b/src/main/resources/assets/hardworkers/blockstates/farmer_iron.json new file mode 100644 index 0000000..bb5ec1c --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/farmer_iron.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/farmer_iron" }, + "facing=east": { "model": "hardworkers:block/farmer_iron", "y": 90 }, + "facing=south": { "model": "hardworkers:block/farmer_iron", "y": 180 }, + "facing=west": { "model": "hardworkers:block/farmer_iron", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/farmer_netherite.json b/src/main/resources/assets/hardworkers/blockstates/farmer_netherite.json new file mode 100644 index 0000000..e94297c --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/farmer_netherite.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/farmer_netherite" }, + "facing=east": { "model": "hardworkers:block/farmer_netherite", "y": 90 }, + "facing=south": { "model": "hardworkers:block/farmer_netherite", "y": 180 }, + "facing=west": { "model": "hardworkers:block/farmer_netherite", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/farmer_stone.json b/src/main/resources/assets/hardworkers/blockstates/farmer_stone.json new file mode 100644 index 0000000..6881f8a --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/farmer_stone.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/farmer_stone" }, + "facing=east": { "model": "hardworkers:block/farmer_stone", "y": 90 }, + "facing=south": { "model": "hardworkers:block/farmer_stone", "y": 180 }, + "facing=west": { "model": "hardworkers:block/farmer_stone", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/farmer_wood.json b/src/main/resources/assets/hardworkers/blockstates/farmer_wood.json new file mode 100644 index 0000000..0972c6a --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/farmer_wood.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/farmer_wood" }, + "facing=east": { "model": "hardworkers:block/farmer_wood", "y": 90 }, + "facing=south": { "model": "hardworkers:block/farmer_wood", "y": 180 }, + "facing=west": { "model": "hardworkers:block/farmer_wood", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/lang/en_us.json b/src/main/resources/assets/hardworkers/lang/en_us.json index 298c4e6..90fd750 100644 --- a/src/main/resources/assets/hardworkers/lang/en_us.json +++ b/src/main/resources/assets/hardworkers/lang/en_us.json @@ -11,5 +11,12 @@ "block.hardworkers.miner_iron": "Miner Block (Iron)", "block.hardworkers.miner_diamond": "Miner Block (Diamond)", "block.hardworkers.miner_netherite": "Miner Block (Netherite)", - "entity.hardworkers.miner": "Miner" + "entity.hardworkers.miner": "Miner", + + "block.hardworkers.farmer_wood": "Farmer Block (Wood)", + "block.hardworkers.farmer_stone": "Farmer Block (Stone)", + "block.hardworkers.farmer_iron": "Farmer Block (Iron)", + "block.hardworkers.farmer_diamond": "Farmer Block (Diamond)", + "block.hardworkers.farmer_netherite": "Farmer Block (Netherite)", + "entity.hardworkers.farmer": "Farmer" } diff --git a/src/main/resources/assets/hardworkers/models/block/farmer_diamond.json b/src/main/resources/assets/hardworkers/models/block/farmer_diamond.json new file mode 100644 index 0000000..68919fe --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/farmer_diamond.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/farmer_diamond_front", + "up": "hardworkers:block/farmer_diamond_top", + "down": "hardworkers:block/farmer_diamond_bottom", + "north": "hardworkers:block/farmer_diamond_front", + "south": "hardworkers:block/farmer_diamond_back", + "east": "hardworkers:block/farmer_diamond_side", + "west": "hardworkers:block/farmer_diamond_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/farmer_iron.json b/src/main/resources/assets/hardworkers/models/block/farmer_iron.json new file mode 100644 index 0000000..a628a1f --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/farmer_iron.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/farmer_iron_front", + "up": "hardworkers:block/farmer_iron_top", + "down": "hardworkers:block/farmer_iron_bottom", + "north": "hardworkers:block/farmer_iron_front", + "south": "hardworkers:block/farmer_iron_back", + "east": "hardworkers:block/farmer_iron_side", + "west": "hardworkers:block/farmer_iron_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/farmer_netherite.json b/src/main/resources/assets/hardworkers/models/block/farmer_netherite.json new file mode 100644 index 0000000..d1d7378 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/farmer_netherite.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/farmer_netherite_front", + "up": "hardworkers:block/farmer_netherite_top", + "down": "hardworkers:block/farmer_netherite_bottom", + "north": "hardworkers:block/farmer_netherite_front", + "south": "hardworkers:block/farmer_netherite_back", + "east": "hardworkers:block/farmer_netherite_side", + "west": "hardworkers:block/farmer_netherite_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/farmer_stone.json b/src/main/resources/assets/hardworkers/models/block/farmer_stone.json new file mode 100644 index 0000000..e24b574 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/farmer_stone.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/farmer_stone_front", + "up": "hardworkers:block/farmer_stone_top", + "down": "hardworkers:block/farmer_stone_bottom", + "north": "hardworkers:block/farmer_stone_front", + "south": "hardworkers:block/farmer_stone_back", + "east": "hardworkers:block/farmer_stone_side", + "west": "hardworkers:block/farmer_stone_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/farmer_wood.json b/src/main/resources/assets/hardworkers/models/block/farmer_wood.json new file mode 100644 index 0000000..b48f4f9 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/farmer_wood.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "hardworkers:block/farmer_wood_front", + "up": "hardworkers:block/farmer_wood_top", + "down": "hardworkers:block/farmer_wood_bottom", + "north": "hardworkers:block/farmer_wood_front", + "south": "hardworkers:block/farmer_wood_back", + "east": "hardworkers:block/farmer_wood_side", + "west": "hardworkers:block/farmer_wood_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/item/farmer_diamond.json b/src/main/resources/assets/hardworkers/models/item/farmer_diamond.json new file mode 100644 index 0000000..3f07faf --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/farmer_diamond.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/farmer_diamond" } diff --git a/src/main/resources/assets/hardworkers/models/item/farmer_iron.json b/src/main/resources/assets/hardworkers/models/item/farmer_iron.json new file mode 100644 index 0000000..f0bf775 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/farmer_iron.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/farmer_iron" } diff --git a/src/main/resources/assets/hardworkers/models/item/farmer_netherite.json b/src/main/resources/assets/hardworkers/models/item/farmer_netherite.json new file mode 100644 index 0000000..aac49f3 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/farmer_netherite.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/farmer_netherite" } diff --git a/src/main/resources/assets/hardworkers/models/item/farmer_stone.json b/src/main/resources/assets/hardworkers/models/item/farmer_stone.json new file mode 100644 index 0000000..42d65e8 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/farmer_stone.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/farmer_stone" } diff --git a/src/main/resources/assets/hardworkers/models/item/farmer_wood.json b/src/main/resources/assets/hardworkers/models/item/farmer_wood.json new file mode 100644 index 0000000..68af928 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/farmer_wood.json @@ -0,0 +1 @@ +{ "parent": "hardworkers:block/farmer_wood" } diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_back.png b/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_bottom.png b/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_front.png b/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_side.png b/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_top.png b/src/main/resources/assets/hardworkers/textures/block/farmer_diamond_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_iron_back.png b/src/main/resources/assets/hardworkers/textures/block/farmer_iron_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_iron_bottom.png b/src/main/resources/assets/hardworkers/textures/block/farmer_iron_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_iron_front.png b/src/main/resources/assets/hardworkers/textures/block/farmer_iron_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_iron_side.png b/src/main/resources/assets/hardworkers/textures/block/farmer_iron_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_iron_top.png b/src/main/resources/assets/hardworkers/textures/block/farmer_iron_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_back.png b/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_bottom.png b/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_front.png b/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_side.png b/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_top.png b/src/main/resources/assets/hardworkers/textures/block/farmer_netherite_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_stone_back.png b/src/main/resources/assets/hardworkers/textures/block/farmer_stone_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_stone_bottom.png b/src/main/resources/assets/hardworkers/textures/block/farmer_stone_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_stone_front.png b/src/main/resources/assets/hardworkers/textures/block/farmer_stone_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_stone_side.png b/src/main/resources/assets/hardworkers/textures/block/farmer_stone_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_stone_top.png b/src/main/resources/assets/hardworkers/textures/block/farmer_stone_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_wood_back.png b/src/main/resources/assets/hardworkers/textures/block/farmer_wood_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_wood_bottom.png b/src/main/resources/assets/hardworkers/textures/block/farmer_wood_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_wood_front.png b/src/main/resources/assets/hardworkers/textures/block/farmer_wood_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_wood_side.png b/src/main/resources/assets/hardworkers/textures/block/farmer_wood_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/farmer_wood_top.png b/src/main/resources/assets/hardworkers/textures/block/farmer_wood_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/entity/farmer.png b/src/main/resources/assets/hardworkers/textures/entity/farmer.png new file mode 100644 index 0000000000000000000000000000000000000000..912a45db02109a3f612c0e5f5d201fba431cd698 GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU=;FnaSW-L^Y*48*C7Xi)`v0& zye?@ZEmTlqIil9%T;?!_wYS(l6}?;gty+|$L7#Ej zK3Q4pZ21b@ut*EctWR>ql`G<+*swl{|nDAyw3 z#Gwc#5{0XF{OY%z!NkBYk+DT$&7YDFAO&F1!fe17Zf*aEg`pvjc@M+a``h0s`p;Yi OlJj)+b6Mw<&;$S>rg{Sa literal 0 HcmV?d00001 diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/farmer_diamond.json b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_diamond.json new file mode 100644 index 0000000..4d5cf7a --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_diamond.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:farmer_diamond" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/farmer_iron.json b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_iron.json new file mode 100644 index 0000000..dfe7034 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_iron.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:farmer_iron" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/farmer_netherite.json b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_netherite.json new file mode 100644 index 0000000..3abba91 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_netherite.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:farmer_netherite" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/farmer_stone.json b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_stone.json new file mode 100644 index 0000000..10491c5 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_stone.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:farmer_stone" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/farmer_wood.json b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_wood.json new file mode 100644 index 0000000..35d0a15 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/farmer_wood.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ "type": "minecraft:item", "name": "hardworkers:farmer_wood" }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/recipes/farmer_diamond.json b/src/main/resources/data/hardworkers/recipes/farmer_diamond.json new file mode 100644 index 0000000..88c86c0 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/farmer_diamond.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:diamond_hoe" }, + "B": { "item": "hardworkers:farmer_iron" } + }, + "result": { "id": "hardworkers:farmer_diamond", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/farmer_iron.json b/src/main/resources/data/hardworkers/recipes/farmer_iron.json new file mode 100644 index 0000000..6ecbd44 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/farmer_iron.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:iron_hoe" }, + "B": { "item": "hardworkers:farmer_stone" } + }, + "result": { "id": "hardworkers:farmer_iron", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/farmer_netherite.json b/src/main/resources/data/hardworkers/recipes/farmer_netherite.json new file mode 100644 index 0000000..e7ee353 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/farmer_netherite.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:netherite_hoe" }, + "B": { "item": "hardworkers:farmer_diamond" } + }, + "result": { "id": "hardworkers:farmer_netherite", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/farmer_stone.json b/src/main/resources/data/hardworkers/recipes/farmer_stone.json new file mode 100644 index 0000000..cac11d1 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/farmer_stone.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WBW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:stone_hoe" }, + "B": { "item": "hardworkers:farmer_wood" } + }, + "result": { "id": "hardworkers:farmer_stone", "count": 1 } +} diff --git a/src/main/resources/data/hardworkers/recipes/farmer_wood.json b/src/main/resources/data/hardworkers/recipes/farmer_wood.json new file mode 100644 index 0000000..94516b4 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/farmer_wood.json @@ -0,0 +1,10 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "pattern": [ "WWW", "WAW", "WWW" ], + "key": { + "W": { "tag": "minecraft:planks" }, + "A": { "item": "minecraft:wooden_hoe" } + }, + "result": { "id": "hardworkers:farmer_wood", "count": 1 } +} From 1b4480ad34184cbc907c3d9b6d0217c80c1ac874 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 03:00:03 +0000 Subject: [PATCH 13/17] Fix worker model rendering by using ModelLayers.PLAYER instead of PLAYER_INNER_ARMOR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PLAYER_INNER_ARMOR uses the slim inner-armor layer mesh which lacks the correct UV mapping for a full player skin, causing visual artifacts. PLAYER uses the standard player model layer that matches the 64×64 skin texture. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../hardworkers/hardworkers/entity/client/FarmerRenderer.java | 2 +- .../hardworkers/entity/client/LumberjackRenderer.java | 2 +- .../hardworkers/hardworkers/entity/client/MinerRenderer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java index 8bee274..ed9923a 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/FarmerRenderer.java @@ -14,7 +14,7 @@ public class FarmerRenderer extends HumanoidMobRenderer(ctx.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)), 0.5f); + super(ctx, new HumanoidModel<>(ctx.bakeLayer(ModelLayers.PLAYER)), 0.5f); addLayer(new ItemInHandLayer<>(this, ctx.getItemInHandRenderer())); } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java index 88458e0..6b322b3 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/LumberjackRenderer.java @@ -14,7 +14,7 @@ public class LumberjackRenderer extends HumanoidMobRenderer(ctx.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)), 0.5f); + super(ctx, new HumanoidModel<>(ctx.bakeLayer(ModelLayers.PLAYER)), 0.5f); addLayer(new ItemInHandLayer<>(this, ctx.getItemInHandRenderer())); } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java index 125382e..b225453 100644 --- a/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/MinerRenderer.java @@ -14,7 +14,7 @@ public class MinerRenderer extends HumanoidMobRenderer(ctx.bakeLayer(ModelLayers.PLAYER_INNER_ARMOR)), 0.5f); + super(ctx, new HumanoidModel<>(ctx.bakeLayer(ModelLayers.PLAYER)), 0.5f); addLayer(new ItemInHandLayer<>(this, ctx.getItemInHandRenderer())); } From 4625ceb77b621356ffe7d9ceddaef1449ee560fc Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 04:07:29 +0000 Subject: [PATCH 14/17] Fix compilation error: replace protected randomTick call with public BonemealableBlock API randomTick is protected in BlockBehaviour so it cannot be called from mod code without an access transformer. CropBlock and StemBlock implement BonemealableBlock, so performBonemeal() is used instead. NetherWart, Cocoa, and SweetBerry don't support bonemeal, so their age property is advanced by 1 directly. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../blockentity/FarmerBlockEntity.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java b/src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java index 4553c62..f103320 100644 --- a/src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java +++ b/src/main/java/com/hardworkers/hardworkers/blockentity/FarmerBlockEntity.java @@ -16,14 +16,14 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BonemealableBlock; import net.minecraft.world.level.block.CocoaBlock; -import net.minecraft.world.level.block.CropBlock; import net.minecraft.world.level.block.FarmBlock; import net.minecraft.world.level.block.NetherWartBlock; -import net.minecraft.world.level.block.StemBlock; import net.minecraft.world.level.block.SweetBerryBushBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.wrapper.InvWrapper; @@ -90,18 +90,28 @@ private static void boostCropGrowth(ServerLevel level, BlockPos center) { BlockPos p = center.offset(dx, dy, dz); BlockState s = level.getBlockState(p); Block b = s.getBlock(); - if (b instanceof CropBlock - || b instanceof NetherWartBlock - || b instanceof CocoaBlock - || b instanceof SweetBerryBushBlock - || b instanceof StemBlock) { - b.randomTick(s, level, p, level.random); + if (b instanceof BonemealableBlock bm && bm.isValidBonemealTarget(level, p, s)) { + bm.performBonemeal(level, level.random, p, s); + } else if (b instanceof NetherWartBlock) { + advanceAge(level, p, s, NetherWartBlock.AGE, 3); + } else if (b instanceof CocoaBlock) { + advanceAge(level, p, s, CocoaBlock.AGE, 2); + } else if (b instanceof SweetBerryBushBlock) { + advanceAge(level, p, s, SweetBerryBushBlock.AGE, 3); } } } } } + private static void advanceAge(ServerLevel level, BlockPos pos, BlockState state, + IntegerProperty ageProp, int maxAge) { + int age = state.getValue(ageProp); + if (age < maxAge) { + level.setBlock(pos, state.setValue(ageProp, age + 1), Block.UPDATE_CLIENTS); + } + } + private static FarmerTier getTier(Level level, BlockPos pos) { BlockState s = level.getBlockState(pos); if (s.getBlock() instanceof FarmerBlock fb) return fb.getTier(); From 2bda2bb0f9354c259d277bbd3575cf98b760ec17 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 5 May 2026 15:27:07 +0700 Subject: [PATCH 15/17] Updated entity textures with nova skins --- .../hardworkers/textures/entity/farmer.png | Bin 331 -> 3908 bytes .../hardworkers/textures/entity/lumberjack.png | Bin 331 -> 3412 bytes .../hardworkers/textures/entity/miner.png | Bin 323 -> 3162 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/hardworkers/textures/entity/farmer.png b/src/main/resources/assets/hardworkers/textures/entity/farmer.png index 912a45db02109a3f612c0e5f5d201fba431cd698..7b543ff5d05670483e34bd0695ba26e371719189 100644 GIT binary patch literal 3908 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 diff --git a/src/main/resources/assets/hardworkers/textures/entity/lumberjack.png b/src/main/resources/assets/hardworkers/textures/entity/lumberjack.png index 912a45db02109a3f612c0e5f5d201fba431cd698..d45c19b190db0454bf66da715c2804473d72a1ff 100644 GIT binary patch delta 3410 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 delta 305 zcmca2b((2{ay_Gvr;B4q#hkY{4Y>|E2(&(wIpB3kBWa<663Y>_USBD}HLRY=laDz^ zR5QPS#JlGz7o(zNin~O($G7?q0YZ0n$g>Ekc}}r;KlSt1LhH)aYu7K`v1ZQtn~9Is ze*MJ&0T3;m&rPsb*WtZ1wU=WaOxRbDLZPZnVC-OI@mY3_feNEr|J~>Qb=Q>Fi zs3l2k68tfnU*FsiS{Y|%((tWl+1?yRpj?Z96Ne(0NEEKx@vGl<1``9rM8+10HGfJz sFfwRCwYM-E@P%93|6yTh$Yb8a@b&)ocZ&WqSAk?bUHx3vIVCg!0C*aA%m4rY diff --git a/src/main/resources/assets/hardworkers/textures/entity/miner.png b/src/main/resources/assets/hardworkers/textures/entity/miner.png index e7b674981515596e8a16e4b671b059421092907b..c912e53c1eb29732c42d497d213f28eaf4ef3304 100644 GIT binary patch delta 3158 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 pFGHIW>c{c31!a From 1ac02804d3695a314057f6048d85f3cd40d993f4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 08:51:24 +0000 Subject: [PATCH 16/17] =?UTF-8?q?Add=20Warehouse=20worker=20=E2=80=94=205-?= =?UTF-8?q?tier=20item=20collection=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warehouse blocks (wood→netherite) spawn a WarehouseWorkerEntity that roams up to 32 blocks to find nearby Lumberjack, Miner, and Farmer block entities with items, collects from them, and deposits into the warehouse's 27-slot chest. Right-clicking the warehouse block opens a chest GUI so players can extract items to central storage. Higher tiers collect more stacks per trip (4→27) and move faster (0.25→0.43 speed). The worker carries no visible items. Source locations are cached to avoid repeated world scans during steady-state collection. Recipe: 8× material surrounding the previous tier (wood uses a chest as the center ingredient). https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .../hardworkers/block/WarehouseBlock.java | 139 ++++++++++++ .../hardworkers/block/WarehouseTier.java | 19 ++ .../blockentity/WarehouseBlockEntity.java | 107 +++++++++ .../hardworkers/client/ClientEvents.java | 2 + .../entity/WarehouseWorkerEntity.java | 87 ++++++++ .../entity/ai/CollectItemsGoal.java | 209 ++++++++++++++++++ .../client/WarehouseWorkerRenderer.java | 24 ++ .../events/ModEventSubscriber.java | 12 + .../hardworkers/init/ModBlockEntities.java | 13 ++ .../hardworkers/init/ModBlocks.java | 22 ++ .../hardworkers/init/ModEntities.java | 9 + .../hardworkers/init/ModItems.java | 6 + .../blockstates/warehouse_diamond.json | 8 + .../blockstates/warehouse_iron.json | 8 + .../blockstates/warehouse_netherite.json | 8 + .../blockstates/warehouse_stone.json | 8 + .../blockstates/warehouse_wood.json | 8 + .../assets/hardworkers/lang/en_us.json | 11 +- .../models/block/warehouse_diamond.json | 12 + .../models/block/warehouse_iron.json | 12 + .../models/block/warehouse_netherite.json | 12 + .../models/block/warehouse_stone.json | 12 + .../models/block/warehouse_wood.json | 12 + .../models/item/warehouse_diamond.json | 3 + .../models/item/warehouse_iron.json | 3 + .../models/item/warehouse_netherite.json | 3 + .../models/item/warehouse_stone.json | 3 + .../models/item/warehouse_wood.json | 3 + .../textures/block/warehouse_diamond_back.png | Bin 0 -> 110 bytes .../block/warehouse_diamond_bottom.png | Bin 0 -> 110 bytes .../block/warehouse_diamond_front.png | Bin 0 -> 110 bytes .../textures/block/warehouse_diamond_side.png | Bin 0 -> 110 bytes .../textures/block/warehouse_diamond_top.png | Bin 0 -> 110 bytes .../textures/block/warehouse_iron_back.png | Bin 0 -> 110 bytes .../textures/block/warehouse_iron_bottom.png | Bin 0 -> 110 bytes .../textures/block/warehouse_iron_front.png | Bin 0 -> 110 bytes .../textures/block/warehouse_iron_side.png | Bin 0 -> 110 bytes .../textures/block/warehouse_iron_top.png | Bin 0 -> 110 bytes .../block/warehouse_netherite_back.png | Bin 0 -> 110 bytes .../block/warehouse_netherite_bottom.png | Bin 0 -> 110 bytes .../block/warehouse_netherite_front.png | Bin 0 -> 110 bytes .../block/warehouse_netherite_side.png | Bin 0 -> 110 bytes .../block/warehouse_netherite_top.png | Bin 0 -> 110 bytes .../textures/block/warehouse_stone_back.png | Bin 0 -> 110 bytes .../textures/block/warehouse_stone_bottom.png | Bin 0 -> 110 bytes .../textures/block/warehouse_stone_front.png | Bin 0 -> 110 bytes .../textures/block/warehouse_stone_side.png | Bin 0 -> 110 bytes .../textures/block/warehouse_stone_top.png | Bin 0 -> 110 bytes .../textures/block/warehouse_wood_back.png | Bin 0 -> 110 bytes .../textures/block/warehouse_wood_bottom.png | Bin 0 -> 110 bytes .../textures/block/warehouse_wood_front.png | Bin 0 -> 110 bytes .../textures/block/warehouse_wood_side.png | Bin 0 -> 110 bytes .../textures/block/warehouse_wood_top.png | Bin 0 -> 110 bytes .../textures/entity/warehouse_worker.png | Bin 0 -> 331 bytes .../loot_table/blocks/warehouse_diamond.json | 11 + .../loot_table/blocks/warehouse_iron.json | 11 + .../blocks/warehouse_netherite.json | 11 + .../loot_table/blocks/warehouse_stone.json | 11 + .../loot_table/blocks/warehouse_wood.json | 11 + .../recipes/warehouse_diamond.json | 9 + .../hardworkers/recipes/warehouse_iron.json | 9 + .../recipes/warehouse_netherite.json | 9 + .../hardworkers/recipes/warehouse_stone.json | 9 + .../hardworkers/recipes/warehouse_wood.json | 9 + 64 files changed, 874 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/hardworkers/hardworkers/block/WarehouseBlock.java create mode 100644 src/main/java/com/hardworkers/hardworkers/block/WarehouseTier.java create mode 100644 src/main/java/com/hardworkers/hardworkers/blockentity/WarehouseBlockEntity.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/WarehouseWorkerEntity.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/ai/CollectItemsGoal.java create mode 100644 src/main/java/com/hardworkers/hardworkers/entity/client/WarehouseWorkerRenderer.java create mode 100644 src/main/resources/assets/hardworkers/blockstates/warehouse_diamond.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/warehouse_iron.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/warehouse_netherite.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/warehouse_stone.json create mode 100644 src/main/resources/assets/hardworkers/blockstates/warehouse_wood.json create mode 100644 src/main/resources/assets/hardworkers/models/block/warehouse_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/block/warehouse_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/block/warehouse_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/block/warehouse_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/block/warehouse_wood.json create mode 100644 src/main/resources/assets/hardworkers/models/item/warehouse_diamond.json create mode 100644 src/main/resources/assets/hardworkers/models/item/warehouse_iron.json create mode 100644 src/main/resources/assets/hardworkers/models/item/warehouse_netherite.json create mode 100644 src/main/resources/assets/hardworkers/models/item/warehouse_stone.json create mode 100644 src/main/resources/assets/hardworkers/models/item/warehouse_wood.json create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_iron_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_iron_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_iron_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_iron_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_iron_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_stone_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_stone_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_stone_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_stone_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_stone_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_wood_back.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_wood_bottom.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_wood_front.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_wood_side.png create mode 100644 src/main/resources/assets/hardworkers/textures/block/warehouse_wood_top.png create mode 100644 src/main/resources/assets/hardworkers/textures/entity/warehouse_worker.png create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/warehouse_diamond.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/warehouse_iron.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/warehouse_netherite.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/warehouse_stone.json create mode 100644 src/main/resources/data/hardworkers/loot_table/blocks/warehouse_wood.json create mode 100644 src/main/resources/data/hardworkers/recipes/warehouse_diamond.json create mode 100644 src/main/resources/data/hardworkers/recipes/warehouse_iron.json create mode 100644 src/main/resources/data/hardworkers/recipes/warehouse_netherite.json create mode 100644 src/main/resources/data/hardworkers/recipes/warehouse_stone.json create mode 100644 src/main/resources/data/hardworkers/recipes/warehouse_wood.json diff --git a/src/main/java/com/hardworkers/hardworkers/block/WarehouseBlock.java b/src/main/java/com/hardworkers/hardworkers/block/WarehouseBlock.java new file mode 100644 index 0000000..bfb35fe --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/block/WarehouseBlock.java @@ -0,0 +1,139 @@ +package com.hardworkers.hardworkers.block; + +import com.hardworkers.hardworkers.blockentity.WarehouseBlockEntity; +import com.hardworkers.hardworkers.entity.WarehouseWorkerEntity; +import com.hardworkers.hardworkers.init.ModBlockEntities; +import com.hardworkers.hardworkers.init.ModEntities; +import com.mojang.serialization.MapCodec; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.MapColor; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; + +import javax.annotation.Nullable; + +public class WarehouseBlock extends BaseEntityBlock { + + public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + + public static final MapCodec WOOD_CODEC = simpleCodec(p -> new WarehouseBlock(WarehouseTier.WOOD, p)); + public static final MapCodec STONE_CODEC = simpleCodec(p -> new WarehouseBlock(WarehouseTier.STONE, p)); + public static final MapCodec IRON_CODEC = simpleCodec(p -> new WarehouseBlock(WarehouseTier.IRON, p)); + public static final MapCodec DIAMOND_CODEC = simpleCodec(p -> new WarehouseBlock(WarehouseTier.DIAMOND, p)); + public static final MapCodec NETHERITE_CODEC = simpleCodec(p -> new WarehouseBlock(WarehouseTier.NETHERITE, p)); + + private final WarehouseTier tier; + + public WarehouseBlock(WarehouseTier tier, BlockBehaviour.Properties properties) { + super(properties); + this.tier = tier; + registerDefaultState(defaultBlockState().setValue(FACING, Direction.NORTH)); + } + + public WarehouseTier getTier() { return tier; } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); + } + + @Override + public MapCodec codec() { + return switch (tier) { + case WOOD -> WOOD_CODEC; + case STONE -> STONE_CODEC; + case IRON -> IRON_CODEC; + case DIAMOND -> DIAMOND_CODEC; + case NETHERITE -> NETHERITE_CODEC; + }; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new WarehouseBlockEntity(pos, state); + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + @Override + public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { + super.onPlace(state, level, pos, oldState, movedByPiston); + if (!level.isClientSide()) { + WarehouseWorkerEntity worker = ModEntities.WAREHOUSE_WORKER.get().create(level); + if (worker != null) { + worker.setHomePosition(pos); + worker.setTier(this.tier); + worker.moveTo(pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, 0f, 0f); + level.addFreshEntity(worker); + } + } + } + + @Override + public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!level.isClientSide() && !state.is(newState.getBlock())) { + if (level.getBlockEntity(pos) instanceof WarehouseBlockEntity be) { + Containers.dropContents(level, pos, be); + } + AABB searchArea = new AABB(pos).inflate(3.0); + level.getEntitiesOfClass(WarehouseWorkerEntity.class, searchArea) + .stream() + .filter(e -> pos.equals(e.getHomePosition())) + .forEach(WarehouseWorkerEntity::discard); + } + super.onRemove(state, level, pos, newState, movedByPiston); + } + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, + Player player, BlockHitResult hitResult) { + if (!level.isClientSide() && level.getBlockEntity(pos) instanceof WarehouseBlockEntity be) { + player.openMenu(be); + } + return InteractionResult.sidedSuccess(level.isClientSide()); + } + + public static BlockBehaviour.Properties baseProperties(MapColor color) { + return BlockBehaviour.Properties.of() + .mapColor(color) + .sound(SoundType.WOOD) + .strength(2.5f); + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/block/WarehouseTier.java b/src/main/java/com/hardworkers/hardworkers/block/WarehouseTier.java new file mode 100644 index 0000000..15cba64 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/block/WarehouseTier.java @@ -0,0 +1,19 @@ +package com.hardworkers.hardworkers.block; + +public enum WarehouseTier { + WOOD("wood", 4, 0.25), + STONE("stone", 8, 0.28), + IRON("iron", 12, 0.32), + DIAMOND("diamond", 18, 0.37), + NETHERITE("netherite", 27, 0.43); + + public final String id; + public final int stacksPerTrip; + public final double moveSpeed; + + WarehouseTier(String id, int stacksPerTrip, double moveSpeed) { + this.id = id; + this.stacksPerTrip = stacksPerTrip; + this.moveSpeed = moveSpeed; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/blockentity/WarehouseBlockEntity.java b/src/main/java/com/hardworkers/hardworkers/blockentity/WarehouseBlockEntity.java new file mode 100644 index 0000000..e90dfb2 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/blockentity/WarehouseBlockEntity.java @@ -0,0 +1,107 @@ +package com.hardworkers.hardworkers.blockentity; + +import com.hardworkers.hardworkers.init.ModBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.Container; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.wrapper.InvWrapper; + +public class WarehouseBlockEntity extends BlockEntity implements Container, MenuProvider { + + public static final int SLOT_COUNT = 27; + + private NonNullList items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); + private final IItemHandler itemHandler = new InvWrapper(this); + + public WarehouseBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntities.WAREHOUSE_BLOCK_ENTITY.get(), pos, state); + } + + public IItemHandler getItemHandler() { return itemHandler; } + + public ItemStack insertItem(ItemStack incoming) { + if (incoming.isEmpty()) return ItemStack.EMPTY; + for (int i = 0; i < SLOT_COUNT; i++) { + ItemStack slot = items.get(i); + if (!slot.isEmpty() && slot.getItem() == incoming.getItem() + && ItemStack.isSameItemSameComponents(slot, incoming)) { + int space = slot.getMaxStackSize() - slot.getCount(); + if (space > 0) { + int toAdd = Math.min(space, incoming.getCount()); + slot.grow(toAdd); + setChanged(); + incoming = incoming.copyWithCount(incoming.getCount() - toAdd); + if (incoming.isEmpty()) return ItemStack.EMPTY; + } + } + } + for (int i = 0; i < SLOT_COUNT; i++) { + if (items.get(i).isEmpty()) { + int toStore = Math.min(incoming.getCount(), incoming.getMaxStackSize()); + items.set(i, incoming.copyWithCount(toStore)); + setChanged(); + int leftover = incoming.getCount() - toStore; + return leftover > 0 ? incoming.copyWithCount(leftover) : ItemStack.EMPTY; + } + } + return incoming; + } + + public boolean isFull() { + for (ItemStack s : items) { + if (s.isEmpty() || s.getCount() < s.getMaxStackSize()) return false; + } + return true; + } + + // MenuProvider — opens a 3×9 chest screen + + @Override + public Component getDisplayName() { + return Component.translatable("container.hardworkers.warehouse"); + } + + @Override + public AbstractContainerMenu createMenu(int id, Inventory playerInventory, Player player) { + return ChestMenu.threeRows(id, playerInventory, this); + } + + // Container + + @Override public int getContainerSize() { return SLOT_COUNT; } + @Override public boolean isEmpty() { for (ItemStack s : items) { if (!s.isEmpty()) return false; } return true; } + @Override public ItemStack getItem(int slot) { return slot >= 0 && slot < SLOT_COUNT ? items.get(slot) : ItemStack.EMPTY; } + @Override public ItemStack removeItem(int slot, int amount) { ItemStack r = ContainerHelper.removeItem(items, slot, amount); if (!r.isEmpty()) setChanged(); return r; } + @Override public ItemStack removeItemNoUpdate(int slot) { return ContainerHelper.takeItem(items, slot); } + @Override public void setItem(int slot, ItemStack stack) { if (slot >= 0 && slot < SLOT_COUNT) { items.set(slot, stack); setChanged(); } } + @Override public boolean stillValid(Player player) { return true; } + @Override public void clearContent() { items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); setChanged(); } + + // NBT + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + ContainerHelper.saveAllItems(tag, items, registries); + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + items = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); + ContainerHelper.loadAllItems(tag, items, registries); + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java b/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java index 6b82c1c..146978f 100644 --- a/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java +++ b/src/main/java/com/hardworkers/hardworkers/client/ClientEvents.java @@ -4,6 +4,7 @@ import com.hardworkers.hardworkers.entity.client.FarmerRenderer; import com.hardworkers.hardworkers.entity.client.LumberjackRenderer; import com.hardworkers.hardworkers.entity.client.MinerRenderer; +import com.hardworkers.hardworkers.entity.client.WarehouseWorkerRenderer; import com.hardworkers.hardworkers.init.ModEntities; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; @@ -18,5 +19,6 @@ public static void registerRenderers(EntityRenderersEvent.RegisterRenderers even event.registerEntityRenderer(ModEntities.LUMBERJACK.get(), LumberjackRenderer::new); event.registerEntityRenderer(ModEntities.MINER.get(), MinerRenderer::new); event.registerEntityRenderer(ModEntities.FARMER.get(), FarmerRenderer::new); + event.registerEntityRenderer(ModEntities.WAREHOUSE_WORKER.get(), WarehouseWorkerRenderer::new); } } diff --git a/src/main/java/com/hardworkers/hardworkers/entity/WarehouseWorkerEntity.java b/src/main/java/com/hardworkers/hardworkers/entity/WarehouseWorkerEntity.java new file mode 100644 index 0000000..d603e73 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/WarehouseWorkerEntity.java @@ -0,0 +1,87 @@ +package com.hardworkers.hardworkers.entity; + +import com.hardworkers.hardworkers.block.WarehouseTier; +import com.hardworkers.hardworkers.entity.ai.CollectItemsGoal; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.ai.goal.FloatGoal; +import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal; +import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import java.util.ArrayList; +import java.util.List; + +public class WarehouseWorkerEntity extends PathfinderMob { + + private BlockPos homePosition = BlockPos.ZERO; + private WarehouseTier tier = WarehouseTier.WOOD; + public final List carrying = new ArrayList<>(); + + public WarehouseWorkerEntity(EntityType type, Level level) { + super(type, level); + } + + public static AttributeSupplier.Builder createAttributes() { + return PathfinderMob.createMobAttributes() + .add(Attributes.MAX_HEALTH, 20.0) + .add(Attributes.MOVEMENT_SPEED, 0.25) + .add(Attributes.FOLLOW_RANGE, 64.0); + } + + @Override + protected void registerGoals() { + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new CollectItemsGoal(this)); + goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.6)); + goalSelector.addGoal(3, new RandomLookAroundGoal(this)); + } + + @Override + public void tick() { + super.tick(); + if (!level().isClientSide() && tickCount % 100 == 0 && !homePosition.equals(BlockPos.ZERO)) { + if (level().getBlockState(homePosition).isAir()) { + discard(); + } + } + } + + public void setTier(WarehouseTier tier) { + this.tier = tier; + var speedAttr = getAttribute(Attributes.MOVEMENT_SPEED); + if (speedAttr != null) speedAttr.setBaseValue(tier.moveSpeed); + } + + public WarehouseTier getTier() { return tier; } + public int getStacksPerTrip() { return tier.stacksPerTrip; } + public BlockPos getHomePosition() { return homePosition; } + public void setHomePosition(BlockPos pos) { this.homePosition = pos; } + + @Override + public void addAdditionalSaveData(CompoundTag tag) { + super.addAdditionalSaveData(tag); + tag.putInt("HomeX", homePosition.getX()); + tag.putInt("HomeY", homePosition.getY()); + tag.putInt("HomeZ", homePosition.getZ()); + tag.putString("Tier", tier.name()); + } + + @Override + public void readAdditionalSaveData(CompoundTag tag) { + super.readAdditionalSaveData(tag); + if (tag.contains("HomeX")) { + homePosition = new BlockPos(tag.getInt("HomeX"), tag.getInt("HomeY"), tag.getInt("HomeZ")); + } + if (tag.contains("Tier")) { + try { + setTier(WarehouseTier.valueOf(tag.getString("Tier"))); + } catch (IllegalArgumentException ignored) {} + } + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/ai/CollectItemsGoal.java b/src/main/java/com/hardworkers/hardworkers/entity/ai/CollectItemsGoal.java new file mode 100644 index 0000000..a7eb2e8 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/ai/CollectItemsGoal.java @@ -0,0 +1,209 @@ +package com.hardworkers.hardworkers.entity.ai; + +import com.hardworkers.hardworkers.blockentity.FarmerBlockEntity; +import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; +import com.hardworkers.hardworkers.blockentity.MinerBlockEntity; +import com.hardworkers.hardworkers.blockentity.WarehouseBlockEntity; +import com.hardworkers.hardworkers.entity.WarehouseWorkerEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.items.IItemHandler; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +public class CollectItemsGoal extends Goal { + + private static final int SEARCH_RADIUS = 32; + private static final double REACH_SQ = 9.0; // 3-block reach + private static final int IDLE_COOLDOWN = 60; // ticks to wait when nothing found + + private final WarehouseWorkerEntity worker; + private BlockPos sourcePos = null; + private int cooldown = 0; + private BlockPos cachedSource = null; + private int fullScanCooldown = 0; + + public CollectItemsGoal(WarehouseWorkerEntity worker) { + this.worker = worker; + setFlags(EnumSet.of(Flag.MOVE)); + } + + @Override + public boolean canUse() { + BlockPos home = worker.getHomePosition(); + if (home == null || home.equals(BlockPos.ZERO)) return false; + + // Already carrying — must return to deposit + if (!worker.carrying.isEmpty()) return true; + + if (cooldown-- > 0) return false; + + // Skip if warehouse is full + WarehouseBlockEntity wbe = getWarehouse(home); + if (wbe == null || wbe.isFull()) { cooldown = IDLE_COOLDOWN; return false; } + + sourcePos = findSource(worker.level(), home); + if (sourcePos == null) { cooldown = IDLE_COOLDOWN; return false; } + return true; + } + + @Override + public boolean canContinueToUse() { + BlockPos home = worker.getHomePosition(); + return home != null && !home.equals(BlockPos.ZERO) + && (sourcePos != null || !worker.carrying.isEmpty()); + } + + @Override + public void start() { + if (!worker.carrying.isEmpty()) { + navigateTo(worker.getHomePosition()); + } else if (sourcePos != null) { + navigateTo(sourcePos); + } + } + + @Override + public void tick() { + BlockPos home = worker.getHomePosition(); + if (home == null) { stop(); return; } + + if (!worker.carrying.isEmpty()) { + // Return home and deposit + if (worker.blockPosition().distSqr(home) <= REACH_SQ) { + depositItems(home); + sourcePos = null; + cooldown = 20; + stop(); + } else if (!worker.getNavigation().isInProgress()) { + navigateTo(home); + } + } else if (sourcePos != null) { + // Head to source and collect + if (worker.blockPosition().distSqr(sourcePos) <= REACH_SQ) { + collectItems(); + if (worker.carrying.isEmpty()) { + // Source was empty — try again later + cachedSource = null; + sourcePos = null; + cooldown = IDLE_COOLDOWN; + stop(); + } else { + sourcePos = null; + navigateTo(home); + } + } else if (!worker.getNavigation().isInProgress()) { + navigateTo(sourcePos); + } + } else { + stop(); + } + } + + @Override + public void stop() { + sourcePos = null; + worker.getNavigation().stop(); + } + + private void navigateTo(BlockPos pos) { + worker.getNavigation().moveTo(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 1.0); + } + + private void collectItems() { + if (sourcePos == null) return; + BlockEntity be = worker.level().getBlockEntity(sourcePos); + IItemHandler handler = getItemHandler(be); + if (handler == null) { sourcePos = null; return; } + + int limit = worker.getStacksPerTrip(); + int collected = 0; + for (int slot = 0; slot < handler.getSlots() && collected < limit; slot++) { + ItemStack stack = handler.extractItem(slot, 64, false); + if (!stack.isEmpty()) { + worker.carrying.add(stack); + collected++; + } + } + cachedSource = sourcePos; + } + + private void depositItems(BlockPos home) { + WarehouseBlockEntity wbe = getWarehouse(home); + List overflow = new ArrayList<>(); + for (ItemStack stack : worker.carrying) { + if (wbe != null) { + ItemStack leftover = wbe.insertItem(stack); + if (!leftover.isEmpty()) overflow.add(leftover); + } else { + overflow.add(stack); + } + } + worker.carrying.clear(); + // Drop any overflow near the warehouse + for (ItemStack stack : overflow) { + worker.level().addFreshEntity(new ItemEntity( + worker.level(), + home.getX() + 0.5, home.getY() + 1.0, home.getZ() + 0.5, + stack)); + } + } + + private WarehouseBlockEntity getWarehouse(BlockPos pos) { + BlockEntity be = worker.level().getBlockEntity(pos); + return be instanceof WarehouseBlockEntity wbe ? wbe : null; + } + + private BlockPos findSource(Level level, BlockPos home) { + // Try cached source first (avoids full scan in steady state) + if (cachedSource != null) { + BlockEntity be = level.getBlockEntity(cachedSource); + if (isNonEmptySource(be)) return cachedSource; + cachedSource = null; + } + + if (fullScanCooldown-- > 0) return null; + fullScanCooldown = 40; + + BlockPos closest = null; + int closestDistSq = Integer.MAX_VALUE; + for (int dx = -SEARCH_RADIUS; dx <= SEARCH_RADIUS; dx++) { + for (int dy = -4; dy <= 4; dy++) { + for (int dz = -SEARCH_RADIUS; dz <= SEARCH_RADIUS; dz++) { + BlockPos p = home.offset(dx, dy, dz); + if (p.equals(home)) continue; + BlockEntity be = level.getBlockEntity(p); + if (isNonEmptySource(be)) { + int distSq = (int) home.distSqr(p); + if (distSq < closestDistSq) { + closestDistSq = distSq; + closest = p.immutable(); + } + } + } + } + } + cachedSource = closest; + return closest; + } + + private boolean isNonEmptySource(BlockEntity be) { + if (be instanceof LumberjackBlockEntity lbe) return !lbe.isEmpty(); + if (be instanceof MinerBlockEntity mbe) return !mbe.isEmpty(); + if (be instanceof FarmerBlockEntity fbe) return !fbe.isEmpty(); + return false; + } + + private IItemHandler getItemHandler(BlockEntity be) { + if (be instanceof LumberjackBlockEntity lbe) return lbe.getItemHandler(); + if (be instanceof MinerBlockEntity mbe) return mbe.getItemHandler(); + if (be instanceof FarmerBlockEntity fbe) return fbe.getItemHandler(); + return null; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/entity/client/WarehouseWorkerRenderer.java b/src/main/java/com/hardworkers/hardworkers/entity/client/WarehouseWorkerRenderer.java new file mode 100644 index 0000000..c1f4ba0 --- /dev/null +++ b/src/main/java/com/hardworkers/hardworkers/entity/client/WarehouseWorkerRenderer.java @@ -0,0 +1,24 @@ +package com.hardworkers.hardworkers.entity.client; + +import com.hardworkers.hardworkers.entity.WarehouseWorkerEntity; +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.HumanoidMobRenderer; +import net.minecraft.resources.ResourceLocation; + +public class WarehouseWorkerRenderer extends HumanoidMobRenderer> { + + private static final ResourceLocation TEXTURE = + ResourceLocation.fromNamespaceAndPath("hardworkers", "textures/entity/warehouse_worker.png"); + + public WarehouseWorkerRenderer(EntityRendererProvider.Context ctx) { + super(ctx, new HumanoidModel<>(ctx.bakeLayer(ModelLayers.PLAYER)), 0.5f); + // No ItemInHandLayer — warehouse workers don't carry visible items + } + + @Override + public ResourceLocation getTextureLocation(WarehouseWorkerEntity entity) { + return TEXTURE; + } +} diff --git a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java index c60fe21..fa79b86 100644 --- a/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java +++ b/src/main/java/com/hardworkers/hardworkers/events/ModEventSubscriber.java @@ -4,6 +4,7 @@ import com.hardworkers.hardworkers.entity.FarmerEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; import com.hardworkers.hardworkers.entity.MinerEntity; +import com.hardworkers.hardworkers.entity.WarehouseWorkerEntity; import com.hardworkers.hardworkers.init.ModBlockEntities; import com.hardworkers.hardworkers.init.ModEntities; import com.hardworkers.hardworkers.init.ModItems; @@ -23,6 +24,7 @@ public static void onEntityAttributeCreation(EntityAttributeCreationEvent event) event.put(ModEntities.LUMBERJACK.get(), LumberjackEntity.createAttributes().build()); event.put(ModEntities.MINER.get(), MinerEntity.createAttributes().build()); event.put(ModEntities.FARMER.get(), FarmerEntity.createAttributes().build()); + event.put(ModEntities.WAREHOUSE_WORKER.get(), WarehouseWorkerEntity.createAttributes().build()); } @SubscribeEvent @@ -43,6 +45,11 @@ public static void onBuildCreativeTab(BuildCreativeModeTabContentsEvent event) { event.accept(ModItems.FARMER_IRON_ITEM); event.accept(ModItems.FARMER_DIAMOND_ITEM); event.accept(ModItems.FARMER_NETHERITE_ITEM); + event.accept(ModItems.WAREHOUSE_WOOD_ITEM); + event.accept(ModItems.WAREHOUSE_STONE_ITEM); + event.accept(ModItems.WAREHOUSE_IRON_ITEM); + event.accept(ModItems.WAREHOUSE_DIAMOND_ITEM); + event.accept(ModItems.WAREHOUSE_NETHERITE_ITEM); } } @@ -63,5 +70,10 @@ public static void onRegisterCapabilities(RegisterCapabilitiesEvent event) { ModBlockEntities.FARMER_BLOCK_ENTITY.get(), (be, side) -> be.getItemHandler() ); + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + ModBlockEntities.WAREHOUSE_BLOCK_ENTITY.get(), + (be, side) -> be.getItemHandler() + ); } } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java index 3839471..5d3b894 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlockEntities.java @@ -4,6 +4,7 @@ import com.hardworkers.hardworkers.blockentity.FarmerBlockEntity; import com.hardworkers.hardworkers.blockentity.LumberjackBlockEntity; import com.hardworkers.hardworkers.blockentity.MinerBlockEntity; +import com.hardworkers.hardworkers.blockentity.WarehouseBlockEntity; import net.minecraft.core.registries.Registries; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.neoforge.registries.DeferredHolder; @@ -49,5 +50,17 @@ public class ModBlockEntities { ModBlocks.FARMER_NETHERITE.get() ).build(null) ); + + public static final DeferredHolder, BlockEntityType> WAREHOUSE_BLOCK_ENTITY = + BLOCK_ENTITY_TYPES.register("warehouse_block", () -> + BlockEntityType.Builder.of( + WarehouseBlockEntity::new, + ModBlocks.WAREHOUSE_WOOD.get(), + ModBlocks.WAREHOUSE_STONE.get(), + ModBlocks.WAREHOUSE_IRON.get(), + ModBlocks.WAREHOUSE_DIAMOND.get(), + ModBlocks.WAREHOUSE_NETHERITE.get() + ).build(null) + ); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java index 0eec895..07bc136 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModBlocks.java @@ -7,6 +7,8 @@ import com.hardworkers.hardworkers.block.LumberjackTier; import com.hardworkers.hardworkers.block.MinerBlock; import com.hardworkers.hardworkers.block.MinerTier; +import com.hardworkers.hardworkers.block.WarehouseBlock; +import com.hardworkers.hardworkers.block.WarehouseTier; import net.minecraft.world.level.material.MapColor; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; @@ -74,5 +76,25 @@ public class ModBlocks { public static final DeferredBlock FARMER_NETHERITE = BLOCKS.register("farmer_netherite", () -> new FarmerBlock(FarmerTier.NETHERITE, FarmerBlock.baseProperties(MapColor.COLOR_BLACK))); + + public static final DeferredBlock WAREHOUSE_WOOD = + BLOCKS.register("warehouse_wood", + () -> new WarehouseBlock(WarehouseTier.WOOD, WarehouseBlock.baseProperties(MapColor.WOOD))); + + public static final DeferredBlock WAREHOUSE_STONE = + BLOCKS.register("warehouse_stone", + () -> new WarehouseBlock(WarehouseTier.STONE, WarehouseBlock.baseProperties(MapColor.STONE))); + + public static final DeferredBlock WAREHOUSE_IRON = + BLOCKS.register("warehouse_iron", + () -> new WarehouseBlock(WarehouseTier.IRON, WarehouseBlock.baseProperties(MapColor.METAL))); + + public static final DeferredBlock WAREHOUSE_DIAMOND = + BLOCKS.register("warehouse_diamond", + () -> new WarehouseBlock(WarehouseTier.DIAMOND, WarehouseBlock.baseProperties(MapColor.DIAMOND))); + + public static final DeferredBlock WAREHOUSE_NETHERITE = + BLOCKS.register("warehouse_netherite", + () -> new WarehouseBlock(WarehouseTier.NETHERITE, WarehouseBlock.baseProperties(MapColor.COLOR_BLACK))); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java b/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java index 81df28b..8cbb444 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModEntities.java @@ -4,6 +4,7 @@ import com.hardworkers.hardworkers.entity.FarmerEntity; import com.hardworkers.hardworkers.entity.LumberjackEntity; import com.hardworkers.hardworkers.entity.MinerEntity; +import com.hardworkers.hardworkers.entity.WarehouseWorkerEntity; import net.minecraft.core.registries.Registries; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; @@ -38,5 +39,13 @@ public class ModEntities { .clientTrackingRange(10) .build("farmer") ); + + public static final DeferredHolder, EntityType> WAREHOUSE_WORKER = + ENTITY_TYPES.register("warehouse_worker", () -> + EntityType.Builder.of(WarehouseWorkerEntity::new, MobCategory.MISC) + .sized(0.6f, 1.95f) + .clientTrackingRange(10) + .build("warehouse_worker") + ); } diff --git a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java index 7d1e830..d7ae5c0 100644 --- a/src/main/java/com/hardworkers/hardworkers/init/ModItems.java +++ b/src/main/java/com/hardworkers/hardworkers/init/ModItems.java @@ -26,5 +26,11 @@ public class ModItems { public static final DeferredItem FARMER_IRON_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_IRON); public static final DeferredItem FARMER_DIAMOND_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_DIAMOND); public static final DeferredItem FARMER_NETHERITE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.FARMER_NETHERITE); + + public static final DeferredItem WAREHOUSE_WOOD_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.WAREHOUSE_WOOD); + public static final DeferredItem WAREHOUSE_STONE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.WAREHOUSE_STONE); + public static final DeferredItem WAREHOUSE_IRON_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.WAREHOUSE_IRON); + public static final DeferredItem WAREHOUSE_DIAMOND_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.WAREHOUSE_DIAMOND); + public static final DeferredItem WAREHOUSE_NETHERITE_ITEM = ITEMS.registerSimpleBlockItem(ModBlocks.WAREHOUSE_NETHERITE); } diff --git a/src/main/resources/assets/hardworkers/blockstates/warehouse_diamond.json b/src/main/resources/assets/hardworkers/blockstates/warehouse_diamond.json new file mode 100644 index 0000000..853e373 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/warehouse_diamond.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/warehouse_diamond" }, + "facing=south": { "model": "hardworkers:block/warehouse_diamond", "y": 180 }, + "facing=east": { "model": "hardworkers:block/warehouse_diamond", "y": 90 }, + "facing=west": { "model": "hardworkers:block/warehouse_diamond", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/warehouse_iron.json b/src/main/resources/assets/hardworkers/blockstates/warehouse_iron.json new file mode 100644 index 0000000..4da8fff --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/warehouse_iron.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/warehouse_iron" }, + "facing=south": { "model": "hardworkers:block/warehouse_iron", "y": 180 }, + "facing=east": { "model": "hardworkers:block/warehouse_iron", "y": 90 }, + "facing=west": { "model": "hardworkers:block/warehouse_iron", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/warehouse_netherite.json b/src/main/resources/assets/hardworkers/blockstates/warehouse_netherite.json new file mode 100644 index 0000000..bd2d59d --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/warehouse_netherite.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/warehouse_netherite" }, + "facing=south": { "model": "hardworkers:block/warehouse_netherite", "y": 180 }, + "facing=east": { "model": "hardworkers:block/warehouse_netherite", "y": 90 }, + "facing=west": { "model": "hardworkers:block/warehouse_netherite", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/warehouse_stone.json b/src/main/resources/assets/hardworkers/blockstates/warehouse_stone.json new file mode 100644 index 0000000..3dbdc83 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/warehouse_stone.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/warehouse_stone" }, + "facing=south": { "model": "hardworkers:block/warehouse_stone", "y": 180 }, + "facing=east": { "model": "hardworkers:block/warehouse_stone", "y": 90 }, + "facing=west": { "model": "hardworkers:block/warehouse_stone", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/blockstates/warehouse_wood.json b/src/main/resources/assets/hardworkers/blockstates/warehouse_wood.json new file mode 100644 index 0000000..a7aa495 --- /dev/null +++ b/src/main/resources/assets/hardworkers/blockstates/warehouse_wood.json @@ -0,0 +1,8 @@ +{ + "variants": { + "facing=north": { "model": "hardworkers:block/warehouse_wood" }, + "facing=south": { "model": "hardworkers:block/warehouse_wood", "y": 180 }, + "facing=east": { "model": "hardworkers:block/warehouse_wood", "y": 90 }, + "facing=west": { "model": "hardworkers:block/warehouse_wood", "y": 270 } + } +} diff --git a/src/main/resources/assets/hardworkers/lang/en_us.json b/src/main/resources/assets/hardworkers/lang/en_us.json index 90fd750..c369a59 100644 --- a/src/main/resources/assets/hardworkers/lang/en_us.json +++ b/src/main/resources/assets/hardworkers/lang/en_us.json @@ -18,5 +18,14 @@ "block.hardworkers.farmer_iron": "Farmer Block (Iron)", "block.hardworkers.farmer_diamond": "Farmer Block (Diamond)", "block.hardworkers.farmer_netherite": "Farmer Block (Netherite)", - "entity.hardworkers.farmer": "Farmer" + "entity.hardworkers.farmer": "Farmer", + + "block.hardworkers.warehouse_wood": "Warehouse Block (Wood)", + "block.hardworkers.warehouse_stone": "Warehouse Block (Stone)", + "block.hardworkers.warehouse_iron": "Warehouse Block (Iron)", + "block.hardworkers.warehouse_diamond": "Warehouse Block (Diamond)", + "block.hardworkers.warehouse_netherite": "Warehouse Block (Netherite)", + "entity.hardworkers.warehouse_worker": "Warehouse Worker", + + "container.hardworkers.warehouse": "Warehouse Storage" } diff --git a/src/main/resources/assets/hardworkers/models/block/warehouse_diamond.json b/src/main/resources/assets/hardworkers/models/block/warehouse_diamond.json new file mode 100644 index 0000000..d16865b --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/warehouse_diamond.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "down": "hardworkers:block/warehouse_diamond_bottom", + "up": "hardworkers:block/warehouse_diamond_top", + "north": "hardworkers:block/warehouse_diamond_front", + "south": "hardworkers:block/warehouse_diamond_back", + "east": "hardworkers:block/warehouse_diamond_side", + "west": "hardworkers:block/warehouse_diamond_side", + "particle": "hardworkers:block/warehouse_diamond_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/warehouse_iron.json b/src/main/resources/assets/hardworkers/models/block/warehouse_iron.json new file mode 100644 index 0000000..dce5a56 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/warehouse_iron.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "down": "hardworkers:block/warehouse_iron_bottom", + "up": "hardworkers:block/warehouse_iron_top", + "north": "hardworkers:block/warehouse_iron_front", + "south": "hardworkers:block/warehouse_iron_back", + "east": "hardworkers:block/warehouse_iron_side", + "west": "hardworkers:block/warehouse_iron_side", + "particle": "hardworkers:block/warehouse_iron_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/warehouse_netherite.json b/src/main/resources/assets/hardworkers/models/block/warehouse_netherite.json new file mode 100644 index 0000000..effc7bc --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/warehouse_netherite.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "down": "hardworkers:block/warehouse_netherite_bottom", + "up": "hardworkers:block/warehouse_netherite_top", + "north": "hardworkers:block/warehouse_netherite_front", + "south": "hardworkers:block/warehouse_netherite_back", + "east": "hardworkers:block/warehouse_netherite_side", + "west": "hardworkers:block/warehouse_netherite_side", + "particle": "hardworkers:block/warehouse_netherite_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/warehouse_stone.json b/src/main/resources/assets/hardworkers/models/block/warehouse_stone.json new file mode 100644 index 0000000..11e7728 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/warehouse_stone.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "down": "hardworkers:block/warehouse_stone_bottom", + "up": "hardworkers:block/warehouse_stone_top", + "north": "hardworkers:block/warehouse_stone_front", + "south": "hardworkers:block/warehouse_stone_back", + "east": "hardworkers:block/warehouse_stone_side", + "west": "hardworkers:block/warehouse_stone_side", + "particle": "hardworkers:block/warehouse_stone_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/block/warehouse_wood.json b/src/main/resources/assets/hardworkers/models/block/warehouse_wood.json new file mode 100644 index 0000000..f61f834 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/block/warehouse_wood.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "down": "hardworkers:block/warehouse_wood_bottom", + "up": "hardworkers:block/warehouse_wood_top", + "north": "hardworkers:block/warehouse_wood_front", + "south": "hardworkers:block/warehouse_wood_back", + "east": "hardworkers:block/warehouse_wood_side", + "west": "hardworkers:block/warehouse_wood_side", + "particle": "hardworkers:block/warehouse_wood_side" + } +} diff --git a/src/main/resources/assets/hardworkers/models/item/warehouse_diamond.json b/src/main/resources/assets/hardworkers/models/item/warehouse_diamond.json new file mode 100644 index 0000000..62c0a25 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/warehouse_diamond.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/warehouse_diamond" +} diff --git a/src/main/resources/assets/hardworkers/models/item/warehouse_iron.json b/src/main/resources/assets/hardworkers/models/item/warehouse_iron.json new file mode 100644 index 0000000..6a8e272 --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/warehouse_iron.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/warehouse_iron" +} diff --git a/src/main/resources/assets/hardworkers/models/item/warehouse_netherite.json b/src/main/resources/assets/hardworkers/models/item/warehouse_netherite.json new file mode 100644 index 0000000..3d71a1a --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/warehouse_netherite.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/warehouse_netherite" +} diff --git a/src/main/resources/assets/hardworkers/models/item/warehouse_stone.json b/src/main/resources/assets/hardworkers/models/item/warehouse_stone.json new file mode 100644 index 0000000..35c720e --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/warehouse_stone.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/warehouse_stone" +} diff --git a/src/main/resources/assets/hardworkers/models/item/warehouse_wood.json b/src/main/resources/assets/hardworkers/models/item/warehouse_wood.json new file mode 100644 index 0000000..71e494f --- /dev/null +++ b/src/main/resources/assets/hardworkers/models/item/warehouse_wood.json @@ -0,0 +1,3 @@ +{ + "parent": "hardworkers:block/warehouse_wood" +} diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_back.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_bottom.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_front.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_side.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_top.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_diamond_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_back.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_bottom.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_front.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_side.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_top.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_iron_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_back.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_bottom.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_front.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_side.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_top.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_netherite_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_back.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_bottom.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_front.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_side.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_top.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_stone_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_back.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_back.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_bottom.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_front.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_front.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_side.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_side.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_top.png b/src/main/resources/assets/hardworkers/textures/block/warehouse_wood_top.png new file mode 100644 index 0000000000000000000000000000000000000000..8307aed98721150f7f554436d8020810ad67cc2f GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rk*a2Ar*6yJB-tR_;aQ)2;S9{ zN_qG&>%d%VrG%b!606PNb#4+s;>zzfd(*my85}S Ib4q9e00PD!761SM literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/hardworkers/textures/entity/warehouse_worker.png b/src/main/resources/assets/hardworkers/textures/entity/warehouse_worker.png new file mode 100644 index 0000000000000000000000000000000000000000..912a45db02109a3f612c0e5f5d201fba431cd698 GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU=;FnaSW-L^Y*48*C7Xi)`v0& zye?@ZEmTlqIil9%T;?!_wYS(l6}?;gty+|$L7#Ej zK3Q4pZ21b@ut*EctWR>ql`G<+*swl{|nDAyw3 z#Gwc#5{0XF{OY%z!NkBYk+DT$&7YDFAO&F1!fe17Zf*aEg`pvjc@M+a``h0s`p;Yi OlJj)+b6Mw<&;$S>rg{Sa literal 0 HcmV?d00001 diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_diamond.json b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_diamond.json new file mode 100644 index 0000000..fb23961 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_diamond.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ + "type": "minecraft:item", + "name": "hardworkers:warehouse_diamond" + }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_iron.json b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_iron.json new file mode 100644 index 0000000..d5e9e81 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_iron.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ + "type": "minecraft:item", + "name": "hardworkers:warehouse_iron" + }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_netherite.json b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_netherite.json new file mode 100644 index 0000000..5fef24d --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_netherite.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ + "type": "minecraft:item", + "name": "hardworkers:warehouse_netherite" + }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_stone.json b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_stone.json new file mode 100644 index 0000000..4f4bd68 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_stone.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ + "type": "minecraft:item", + "name": "hardworkers:warehouse_stone" + }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_wood.json b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_wood.json new file mode 100644 index 0000000..9a412b7 --- /dev/null +++ b/src/main/resources/data/hardworkers/loot_table/blocks/warehouse_wood.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:block", + "pools": [{ + "rolls": 1, + "entries": [{ + "type": "minecraft:item", + "name": "hardworkers:warehouse_wood" + }], + "conditions": [{ "condition": "minecraft:survives_explosion" }] + }] +} diff --git a/src/main/resources/data/hardworkers/recipes/warehouse_diamond.json b/src/main/resources/data/hardworkers/recipes/warehouse_diamond.json new file mode 100644 index 0000000..cb2aad3 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/warehouse_diamond.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": ["DDD", "DWD", "DDD"], + "key": { + "D": {"item": "minecraft:diamond"}, + "W": {"item": "hardworkers:warehouse_iron"} + }, + "result": {"id": "hardworkers:warehouse_diamond", "count": 1} +} diff --git a/src/main/resources/data/hardworkers/recipes/warehouse_iron.json b/src/main/resources/data/hardworkers/recipes/warehouse_iron.json new file mode 100644 index 0000000..4f90eea --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/warehouse_iron.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": ["III", "IWI", "III"], + "key": { + "I": {"item": "minecraft:iron_ingot"}, + "W": {"item": "hardworkers:warehouse_stone"} + }, + "result": {"id": "hardworkers:warehouse_iron", "count": 1} +} diff --git a/src/main/resources/data/hardworkers/recipes/warehouse_netherite.json b/src/main/resources/data/hardworkers/recipes/warehouse_netherite.json new file mode 100644 index 0000000..41175c7 --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/warehouse_netherite.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": ["NNN", "NWN", "NNN"], + "key": { + "N": {"item": "minecraft:netherite_ingot"}, + "W": {"item": "hardworkers:warehouse_diamond"} + }, + "result": {"id": "hardworkers:warehouse_netherite", "count": 1} +} diff --git a/src/main/resources/data/hardworkers/recipes/warehouse_stone.json b/src/main/resources/data/hardworkers/recipes/warehouse_stone.json new file mode 100644 index 0000000..21213cc --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/warehouse_stone.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": ["SSS", "SWS", "SSS"], + "key": { + "S": {"item": "minecraft:cobblestone"}, + "W": {"item": "hardworkers:warehouse_wood"} + }, + "result": {"id": "hardworkers:warehouse_stone", "count": 1} +} diff --git a/src/main/resources/data/hardworkers/recipes/warehouse_wood.json b/src/main/resources/data/hardworkers/recipes/warehouse_wood.json new file mode 100644 index 0000000..99108ce --- /dev/null +++ b/src/main/resources/data/hardworkers/recipes/warehouse_wood.json @@ -0,0 +1,9 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": ["PPP", "PCP", "PPP"], + "key": { + "P": {"item": "minecraft:oak_planks"}, + "C": {"item": "minecraft:chest"} + }, + "result": {"id": "hardworkers:warehouse_wood", "count": 1} +} From 3db2f391135c7108d2be5d1d4f16df7812d3b1a6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 25 May 2026 01:07:00 +0000 Subject: [PATCH 17/17] Add release workflow and reset version to 0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On each PR merge to main the workflow: 1. Increments mod_version by 0.1 (0.9 → 1.0, etc.) 2. Builds the JAR via ./gradlew build 3. Commits the version bump back to main 4. Creates a GitHub release and attaches the JAR Version starts at 0.0 so the first merge produces 0.1. https://claude.ai/code/session_01Ko8zQjsoTR3N7dKcVea1uz --- .github/workflows/release.yml | 73 +++++++++++++++++++++++++++++++++++ gradle.properties | 2 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c63faf5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,73 @@ +name: Build and Release + +on: + pull_request: + types: [closed] + branches: [main] + +jobs: + release: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Java 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: ${{ runner.os }}-gradle- + + - name: Increment version + id: version + run: | + CURRENT=$(grep '^mod_version=' gradle.properties | cut -d'=' -f2) + NEW=$(python3 -c " + parts = '$CURRENT'.split('.') + major, minor = int(parts[0]), int(parts[1]) + minor += 1 + if minor >= 10: + major += 1 + minor = 0 + print(f'{major}.{minor}') + ") + sed -i "s/^mod_version=.*/mod_version=$NEW/" gradle.properties + echo "version=$NEW" >> "$GITHUB_OUTPUT" + echo "Bumped $CURRENT → $NEW" + + - name: Build + run: ./gradlew build + + - name: Commit version bump + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add gradle.properties + git commit -m "chore: bump version to ${{ steps.version.outputs.version }} [skip ci]" + git push + + - name: Create release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + JAR=$(find build/libs -name "hardworkers-*.jar" ! -name "*-sources*" | head -1) + gh release create "v${{ steps.version.outputs.version }}" \ + "$JAR#hardworkers-${{ steps.version.outputs.version }}.jar" \ + --title "Hard Workers v${{ steps.version.outputs.version }}" \ + --generate-notes diff --git a/gradle.properties b/gradle.properties index aa8185b..242c861 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ loader_version_range=[2,) mod_id=hardworkers mod_name=Hard Workers mod_license=MIT -mod_version=1.0.0 +mod_version=0.0 mod_group_id=com.hardworkers.hardworkers mod_authors=holty07 mod_description=Adds worker blocks that spawn entities to automate tasks. Place a Lumberjack Block and watch him chop trees and replant saplings.