diff --git a/gradle.properties b/gradle.properties index 97215dd..a631032 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,7 @@ org.gradle.parallel=true minecraft_version=1.21.5 yarn_mappings=1.21.5+build.1 loader_version=0.16.12 +loom_version=1.11-SNAPSHOT # Mod Properties mod_version=1.0.0 @@ -14,4 +15,4 @@ maven_group=com.uncreativebunch.magicmod archives_base_name=magic_mod # Dependencies -fabric_version=0.119.9+1.21.5 \ No newline at end of file +fabric_version=0.128.2+1.21.5 \ No newline at end of file diff --git a/src/client/java/com/uncreativebunch/magicmod/MagicModClient.java b/src/client/java/com/uncreativebunch/magicmod/MagicModClient.java index e8c8cfa..931be3b 100644 --- a/src/client/java/com/uncreativebunch/magicmod/MagicModClient.java +++ b/src/client/java/com/uncreativebunch/magicmod/MagicModClient.java @@ -1,10 +1,14 @@ package com.uncreativebunch.magicmod; +import com.uncreativebunch.magicmod.screen.MagicalCraftingScreen; +import com.uncreativebunch.magicmod.screen.ModScreenHandlers; import net.fabricmc.api.ClientModInitializer; +import net.minecraft.client.gui.screen.ingame.HandledScreens; public class MagicModClient implements ClientModInitializer { @Override public void onInitializeClient() { // This entrypoint is suitable for setting up client-specific logic, such as rendering. + HandledScreens.register(ModScreenHandlers.MAGICAL_CRAFTING_SCREEN_HANDLER, MagicalCraftingScreen::new); } } \ No newline at end of file diff --git a/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java b/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java new file mode 100644 index 0000000..1cfed57 --- /dev/null +++ b/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java @@ -0,0 +1,39 @@ +package com.uncreativebunch.magicmod.screen; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class MagicalCraftingScreen extends HandledScreen { + private static final Identifier TEXTURE = Identifier.of("textures/gui/container/anvil.png"); + + public MagicalCraftingScreen(MagicalCraftingScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + } + + @Override + protected void drawBackground(DrawContext context, float delta, int mouseX, int mouseY) { + context.drawTexture(RenderLayer::getGuiTextured, TEXTURE, + this.x, (this.height - this.backgroundHeight) / 2, + 0.0F, 0.0F, + this.backgroundWidth, this.backgroundHeight, + 256, 256); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + renderBackground(context, mouseX, mouseY, delta); + super.render(context, mouseX, mouseY, delta); + drawMouseoverTooltip(context, mouseX, mouseY); + } + + @Override + protected void init() { + super.init(); + // Centers the title + titleX = (backgroundWidth - textRenderer.getWidth(title)) / 2; + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java index 7eb74e2..787bf10 100644 --- a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java +++ b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java @@ -3,6 +3,7 @@ package com.uncreativebunch.magicmod; import com.uncreativebunch.magicmod.block.ModBlocks; import com.uncreativebunch.magicmod.item.ModItemGroups; import com.uncreativebunch.magicmod.item.ModItems; +import com.uncreativebunch.magicmod.screen.ModScreenHandlers; import net.fabricmc.api.ModInitializer; import org.slf4j.Logger; @@ -29,6 +30,7 @@ public class MagicMod implements ModInitializer { ModBlocks.init(); ModItems.init(); ModItemGroups.init(); + ModScreenHandlers.init(); LOGGER.info("Finished Magic Mod initialization!"); } } \ No newline at end of file diff --git a/src/main/java/com/uncreativebunch/magicmod/block/ModBlocks.java b/src/main/java/com/uncreativebunch/magicmod/block/ModBlocks.java index 3eb060c..ddde5c1 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/ModBlocks.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/ModBlocks.java @@ -4,7 +4,6 @@ import com.uncreativebunch.magicmod.MagicMod; import com.uncreativebunch.magicmod.block.crafting.MagicalCrafterBlock; import net.minecraft.block.AbstractBlock; import net.minecraft.block.Block; -import net.minecraft.block.SlabBlock; import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.registry.Registries; @@ -19,6 +18,7 @@ import java.util.function.Function; public class ModBlocks { //region Block Definitions + /** The magical crafting table block. */ public static final Block MAGICAL_CRAFTING_TABLE = register( "magical_crafting_table", MagicalCrafterBlock::new, diff --git a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java index 275a177..af5da16 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java @@ -1,18 +1,24 @@ package com.uncreativebunch.magicmod.block.crafting; import com.uncreativebunch.magicmod.MagicMod; +import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler; import net.minecraft.block.AbstractBlock; import net.minecraft.block.BlockState; -import net.minecraft.block.SlabBlock; +import net.minecraft.block.CraftingTableBlock; import net.minecraft.entity.EntityType; import net.minecraft.entity.LightningEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.screen.NamedScreenHandlerFactory; +import net.minecraft.screen.ScreenHandlerContext; +import net.minecraft.screen.SimpleNamedScreenHandlerFactory; +import net.minecraft.stat.Stats; +import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -public class MagicalCrafterBlock extends SlabBlock { +public class MagicalCrafterBlock extends CraftingTableBlock { /** * Creates a new MagicalCrafterBlock. @@ -21,7 +27,8 @@ public class MagicalCrafterBlock extends SlabBlock { public MagicalCrafterBlock(AbstractBlock.Settings settings) { super(settings); } /** - * Occurs whenever this block is used. This is intended to open a crafting window in the future. + * Occurs whenever this block is used. + * It opens a crafting window and summons lightning on the interacting player. * @param state The current {@link BlockState} of this block. * @param world The {@link World} that this block exists in. * @param pos The {@link BlockPos} that this block is at. @@ -31,17 +38,37 @@ public class MagicalCrafterBlock extends SlabBlock { */ @Override protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) { - if (world.isClient) return ActionResult.PASS; + // The server is the game's sole source of truth. Even though some things are displayed + // on the client, these actions usually, with very few exceptions, are routed and triggered + // through the server first. + if (!world.isClient) { + MagicMod.LOGGER.info("Block was used at location {}", pos.toShortString()); - MagicMod.LOGGER.info("Block was used at location {}", pos.toShortString()); + MagicMod.LOGGER.info("Electrocuting player..."); + BlockPos playerPos = player.getBlockPos(); + LightningEntity lightningEntity = new LightningEntity(EntityType.LIGHTNING_BOLT, world); + lightningEntity.setPosition(playerPos.toCenterPos()); + world.spawnEntity(lightningEntity); + MagicMod.LOGGER.info("Player electrocuted."); - MagicMod.LOGGER.info("Electrocuting player..."); - BlockPos playerPos = player.getBlockPos(); - LightningEntity lightningEntity = new LightningEntity(EntityType.LIGHTNING_BOLT, world); - lightningEntity.setPosition(playerPos.toCenterPos()); - world.spawnEntity(lightningEntity); - MagicMod.LOGGER.info("Player electrocuted."); + NamedScreenHandlerFactory screenFactory = state.createScreenHandlerFactory(world, pos); + if (screenFactory != null) { + player.openHandledScreen(screenFactory); + player.incrementStat(Stats.INTERACT_WITH_CRAFTING_TABLE); + } else { + MagicMod.LOGGER.info("NamedScreenHandlerFactory returned from the BlockState is null."); + } + } return ActionResult.SUCCESS; } + + @Override + public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) { + return new SimpleNamedScreenHandlerFactory( + ((syncId, playerInventory, player) -> + new MagicalCraftingScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))), + Text.translatable("container.magical_crafting_table") + ); + } } diff --git a/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java b/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java new file mode 100644 index 0000000..1ed50a7 --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java @@ -0,0 +1,109 @@ +package com.uncreativebunch.magicmod.screen; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ScreenHandlerContext; +import net.minecraft.screen.slot.Slot; + +public class MagicalCraftingScreenHandler extends ScreenHandler { + + private final ScreenHandlerContext context; + private final CraftingInventory inventory; + + /** + * Client-side constructor. + * @param syncId The sync ID that's sent from the server. + * @param playerInventory The player's inventory. + */ + public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory) { + this(syncId, playerInventory, ScreenHandlerContext.EMPTY); + } + + /** + * Server-side constructor. The server knows the player's inventory and so can provide it to the client. + * It handles creating the inventory slots and syncs all information from the server to the client. + * @param syncId The sync ID that's sent from the server. + * @param playerInventory The player's inventory. + * @param screenHandlerContext The context of the screen handler. Not sure if this is needed anymore. + */ + public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory, ScreenHandlerContext screenHandlerContext) { + super(ModScreenHandlers.MAGICAL_CRAFTING_SCREEN_HANDLER, syncId); + this.context = screenHandlerContext; + this.inventory = new CraftingInventory(this, 3, 3); + + // Ensure that the crafting inventory is the correct size. + checkSize(inventory, 9); + + // Ensure that the inventory's custom logic runs (if any) + inventory.onOpen(playerInventory.player); + + // Place all inventory slots within the screen. This piece doesn't actually render the background of the slots, + // as this is handled by the client-side application (the Screen), but it does tell the screen where to render. + int m, l; + for (m = 0; m < 3; m++) { + for (l = 0; l < 3; l++) { + // Create the crafting slots. This is good for a 3x3 grid of crafting slots. It registers them to a + // specific inventory and location on the client's screen, relative to the screen's size and origin. + this.addSlot(new Slot(inventory, l + m * 3, 62 + l * 18, 17 + m * 18)); + } + } + + // Add all player inventory slots. Start with the main inventory, then the hotbar. + for (m = 0; m < 3; m++) { + for (l = 0; l < 9; l++) { + this.addSlot(new Slot(playerInventory, l + m * 9 + 9, 8 + l * 18, 84 + m * 18)); + } + } + for (m = 0; m < 9; m++) { + this.addSlot(new Slot(playerInventory, m, 8 + m * 18, 142)); + } + } + + /** + * Checks if the specified slot index is within the crafting inventory. + * @param slotIndex The slot index to check. + * @return True if the slot index is within the crafting inventory, false otherwise. + */ + protected boolean isCraftingInventorySlot(int slotIndex) { + return slotIndex < this.inventory.size(); + } + + @Override + public ItemStack quickMove(PlayerEntity player, int slotIndex) { + // If the slot is in the crafting inventory, move it to the player's inventory. + // Otherwise, don't move it at all. + ItemStack newStack = ItemStack.EMPTY; + Slot slot = this.slots.get(slotIndex); + + if (slot.hasStack()) { + ItemStack stack = slot.getStack(); + newStack = stack.copy(); + if (isCraftingInventorySlot(slotIndex)) { + // Attempt to move the stack into the player's inventory. + if (!this.insertItem(stack, this.inventory.size(), this.slots.size(), true)) { + return ItemStack.EMPTY; + } + } else { + // Don't move the stack. + return ItemStack.EMPTY; + } + + // Check if the stack was moved. If moved entirely, empty the slot. + if (stack.isEmpty()) { + slot.setStack(ItemStack.EMPTY); + } else { + slot.markDirty(); + } + } + + return newStack; + } + + @Override + public boolean canUse(PlayerEntity player) { + return inventory.canPlayerUse(player); + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/screen/ModScreenHandlers.java b/src/main/java/com/uncreativebunch/magicmod/screen/ModScreenHandlers.java new file mode 100644 index 0000000..81f992c --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/screen/ModScreenHandlers.java @@ -0,0 +1,21 @@ +package com.uncreativebunch.magicmod.screen; + +import com.uncreativebunch.magicmod.MagicMod; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.resource.featuretoggle.FeatureFlags; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ScreenHandlerType; + +public class ModScreenHandlers { + public static final ScreenHandlerType MAGICAL_CRAFTING_SCREEN_HANDLER = + register("magical_crafting", MagicalCraftingScreenHandler::new); + + private static ScreenHandlerType register(String id, ScreenHandlerType.Factory factory) { + return Registry.register(Registries.SCREEN_HANDLER, id, new ScreenHandlerType<>(factory, FeatureFlags.VANILLA_FEATURES)); + } + + public static void init() { + MagicMod.LOGGER.info("Screen handlers initialized."); + } +} diff --git a/src/main/resources/assets/magic_mod/lang/en_us.json b/src/main/resources/assets/magic_mod/lang/en_us.json index dbb8cf4..26b438b 100644 --- a/src/main/resources/assets/magic_mod/lang/en_us.json +++ b/src/main/resources/assets/magic_mod/lang/en_us.json @@ -13,6 +13,7 @@ "item.magic_mod.pale_oak_stick": "Pale Oak Stick", "item.magic_mod.spruce_stick": "Spruce Stick", "item.magic_mod.warped_stick": "Warped Stick", + "container.magical_crafting_table": "Magical Crafting", "magic_mod.magic_group": "Testing Mod Items", "tag.item.magic_mod.sticks": "Sticks" } \ No newline at end of file