From 5d4af71099b86e9b138397bd85da4fc2d8d91024 Mon Sep 17 00:00:00 2001 From: EmmaTheMartian Date: Fri, 27 Dec 2024 11:25:25 -0600 Subject: [PATCH] Fix pump not filling itself and overhaul Tweakeruler controls --- .../martian/minefactorial/Minefactorial.java | 2 +- .../minefactorial/MinefactorialListeners.java | 76 +++++++++- .../martian/minefactorial/client/MFKeys.java | 42 +++++- .../client/MinefactorialClient.java | 48 ++++++- .../client/overlay/package-info.java | 5 + .../overlay/scrollmenu/OverlayScrollMenu.java | 74 ++++++++++ .../client/overlay/scrollmenu/ScrollMenu.java | 53 +++++++ .../overlay/scrollmenu/ScrollMenuEntry.java | 10 ++ .../overlay/scrollmenu/package-info.java | 5 + .../content/block/machinery/BlockPumpBE.java | 2 +- .../minefactorial/content/item/ItemRuler.java | 7 +- .../{ => tweakeruler}/ItemTweakeruler.java | 135 ++++++++---------- .../item/tweakeruler/TweakerulerAction.java | 28 ++++ .../item/tweakeruler/TweakerulerHistory.java | 47 ++++++ .../item/tweakeruler/TweakerulerMode.java | 70 +++++++++ .../PacketServerboundSetTweakerulerMode.java | 42 ++++++ .../net/PacketServerboundTweakerulerRedo.java | 35 +++++ .../net/PacketServerboundTweakerulerUndo.java | 35 +++++ .../content/registry/MFDataComponents.java | 9 +- .../content/registry/MFItems.java | 4 +- .../minefactorial/foundation/HistoryList.java | 90 ++++++++++++ .../minefactorial/foundation/item/MFItem.java | 2 - .../managers/TweakerulerHistoryManager.java | 27 ++++ .../assets/minefactorial/lang/en_us.yml | 5 + 24 files changed, 754 insertions(+), 99 deletions(-) create mode 100644 src/main/java/martian/minefactorial/client/overlay/package-info.java create mode 100644 src/main/java/martian/minefactorial/client/overlay/scrollmenu/OverlayScrollMenu.java create mode 100644 src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenu.java create mode 100644 src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenuEntry.java create mode 100644 src/main/java/martian/minefactorial/client/overlay/scrollmenu/package-info.java rename src/main/java/martian/minefactorial/content/item/{ => tweakeruler}/ItemTweakeruler.java (53%) create mode 100644 src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerAction.java create mode 100644 src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerHistory.java create mode 100644 src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerMode.java create mode 100644 src/main/java/martian/minefactorial/content/net/PacketServerboundSetTweakerulerMode.java create mode 100644 src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerRedo.java create mode 100644 src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerUndo.java create mode 100644 src/main/java/martian/minefactorial/foundation/HistoryList.java create mode 100644 src/main/java/martian/minefactorial/managers/TweakerulerHistoryManager.java diff --git a/src/main/java/martian/minefactorial/Minefactorial.java b/src/main/java/martian/minefactorial/Minefactorial.java index e79c97d..d1ebfd5 100644 --- a/src/main/java/martian/minefactorial/Minefactorial.java +++ b/src/main/java/martian/minefactorial/Minefactorial.java @@ -33,7 +33,7 @@ public Minefactorial(IEventBus modBus, Dist dist) { // Add event listeners modBus.addListener(MFTabs::addItems); modBus.register(MinefactorialListeners.ModBusEvents.class); -// NeoForge.EVENT_BUS.register(MinefactorialListeners.GameBusEvents.class); // There are no events on this *yet* + NeoForge.EVENT_BUS.register(MinefactorialListeners.GameBusEvents.class); if (dist.isClient()) { modBus.register(MinefactorialClient.ModBusEvents.class); NeoForge.EVENT_BUS.register(MinefactorialClient.GameBusEvents.class); diff --git a/src/main/java/martian/minefactorial/MinefactorialListeners.java b/src/main/java/martian/minefactorial/MinefactorialListeners.java index 0c8c0a4..84d8b2b 100644 --- a/src/main/java/martian/minefactorial/MinefactorialListeners.java +++ b/src/main/java/martian/minefactorial/MinefactorialListeners.java @@ -1,15 +1,25 @@ package martian.minefactorial; +import martian.minefactorial.client.overlay.scrollmenu.OverlayScrollMenu; import martian.minefactorial.content.block.logistics.BlockFluidExtractorBE; import martian.minefactorial.content.block.machinery.husbandry.BlockSlaughterhouseBE; import martian.minefactorial.content.block.power.BlockSteamBoilerBE; import martian.minefactorial.content.block.storage.BlockCapacitorBE; +import martian.minefactorial.content.item.tweakeruler.ItemTweakeruler; import martian.minefactorial.content.net.PacketServerboundSetSmasherFortuneLevel; +import martian.minefactorial.content.net.PacketServerboundSetTweakerulerMode; +import martian.minefactorial.content.net.PacketServerboundTweakerulerRedo; +import martian.minefactorial.content.net.PacketServerboundTweakerulerUndo; import martian.minefactorial.content.registry.MFBlockEntityTypes; +import martian.minefactorial.content.registry.MFDataComponents; +import martian.minefactorial.content.registry.MFItems; import martian.minefactorial.foundation.block.AbstractEnergyBE; import martian.minefactorial.foundation.block.IInventoryBE; import martian.minefactorial.foundation.block.ISingleTankBE; +import martian.minefactorial.managers.TweakerulerHistoryManager; +import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.bus.api.SubscribeEvent; @@ -17,20 +27,25 @@ import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.capabilities.ICapabilityProvider; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; +import net.neoforged.neoforge.common.util.TriState; import net.neoforged.neoforge.energy.IEnergyStorage; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import net.neoforged.neoforge.fluids.capability.IFluidHandler; import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.ItemStackHandler; import net.neoforged.neoforge.items.wrapper.SidedInvWrapper; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; import net.neoforged.neoforge.network.registration.PayloadRegistrar; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; +import java.util.Optional; final class MinefactorialListeners { private static @Nullable RegisterCapabilitiesEvent registerCapabilitiesEvent; - private MinefactorialListeners() { } + private MinefactorialListeners() { + } @EventBusSubscriber(modid = Minefactorial.MODID, bus = EventBusSubscriber.Bus.MOD) static final class ModBusEvents { @@ -118,11 +133,64 @@ static void onRegisterPayloadHandlers(final RegisterPayloadHandlersEvent event) final PayloadRegistrar registrar = event.registrar("1"); registrar.playToServer(PacketServerboundSetSmasherFortuneLevel.TYPE, PacketServerboundSetSmasherFortuneLevel.STREAM_CODEC, PacketServerboundSetSmasherFortuneLevel::handle); + registrar.playToServer(PacketServerboundTweakerulerUndo.TYPE, PacketServerboundTweakerulerUndo.STREAM_CODEC, PacketServerboundTweakerulerUndo::handle); + registrar.playToServer(PacketServerboundTweakerulerRedo.TYPE, PacketServerboundTweakerulerRedo.STREAM_CODEC, PacketServerboundTweakerulerRedo::handle); + registrar.playToServer(PacketServerboundSetTweakerulerMode.TYPE, PacketServerboundSetTweakerulerMode.STREAM_CODEC, PacketServerboundSetTweakerulerMode::handle); } } -// @EventBusSubscriber(modid = Minefactorial.MODID, bus = EventBusSubscriber.Bus.GAME) -// static final class GameBusEvents { } + @EventBusSubscriber(modid = Minefactorial.MODID, bus = EventBusSubscriber.Bus.GAME) + static final class GameBusEvents { + @SubscribeEvent + static void onPlayerLogout(final PlayerEvent.PlayerLoggedOutEvent event) { + if (event.getEntity() instanceof ServerPlayer serverPlayer) { + TweakerulerHistoryManager.removeHistoryFor(serverPlayer); + } + } + + @SubscribeEvent + static void onPlayerLeftClickEmpty(final PlayerInteractEvent.LeftClickEmpty event) { + // Cancel a Ruler or Tweakeruler selection + if (event.getItemStack().is(MFItems.RULER) || event.getItemStack().is(MFItems.TWEAKERULER)) { + @Nullable Optional posComponent = event.getItemStack().get(MFDataComponents.POS); + if (posComponent != null && posComponent.isPresent()) { + event.getItemStack().set(MFDataComponents.POS, Optional.empty()); + } + // If the position was not set, check if the player was trying to close a Tweakeruler menu + else if ( + event.getLevel().isClientSide && + OverlayScrollMenu.isMenuOpen() && + event.getItemStack().is(MFItems.TWEAKERULER) && + OverlayScrollMenu.getTopMenu() instanceof ItemTweakeruler.Menu + ) { + OverlayScrollMenu.popMenu(); + } + } + } + + @SubscribeEvent + static void onPlayerLeftClickBlock(final PlayerInteractEvent.LeftClickBlock event) { + // Cancel a Ruler or Tweakeruler selection + if (event.getItemStack().is(MFItems.RULER) || event.getItemStack().is(MFItems.TWEAKERULER)) { + @Nullable Optional posComponent = event.getItemStack().get(MFDataComponents.POS); + if (posComponent != null && posComponent.isPresent()) { + event.getItemStack().set(MFDataComponents.POS, Optional.empty()); + } + // If the position was not set, check if the player was trying to close a Tweakeruler menu + else if ( + event.getLevel().isClientSide && + OverlayScrollMenu.isMenuOpen() && + event.getItemStack().is(MFItems.TWEAKERULER) && + OverlayScrollMenu.getTopMenu() instanceof ItemTweakeruler.Menu + ) { + OverlayScrollMenu.popMenu(); + } + + // Prevent rulers and tweakerulers from hitting blocks + event.setCanceled(true); + } + } + } // Helpers private static void registerEnergyCapability(BlockEntityType type) { diff --git a/src/main/java/martian/minefactorial/client/MFKeys.java b/src/main/java/martian/minefactorial/client/MFKeys.java index 5b5b244..3a585c1 100644 --- a/src/main/java/martian/minefactorial/client/MFKeys.java +++ b/src/main/java/martian/minefactorial/client/MFKeys.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.KeyMapping; import net.neoforged.neoforge.client.settings.KeyConflictContext; +import net.neoforged.neoforge.client.settings.KeyModifier; import net.neoforged.neoforge.common.util.Lazy; import org.lwjgl.glfw.GLFW; @@ -14,6 +15,45 @@ private MFKeys() { } KeyConflictContext.GUI, InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_LEFT_SHIFT, - "key.categories.misc" + "key.categories.minefactorial" + )); + + public static final Lazy SCROLL_MENU_UP = Lazy.of(() -> new KeyMapping( + "key.minefactorial.scroll_menu_up", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_PAGE_UP, + "key.categories.minefactorial" + )); + + public static final Lazy SCROLL_MENU_DOWN = Lazy.of(() -> new KeyMapping( + "key.minefactorial.scroll_menu_down", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_PAGE_DOWN, + "key.categories.minefactorial" + )); + + public static final Lazy SCROLL_MENU_SELECT = Lazy.of(() -> new KeyMapping( + "key.minefactorial.scroll_menu_select", + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_ENTER, + "key.categories.minefactorial" + )); + + public static final Lazy TWEAKERULER_UNDO = Lazy.of(() -> new KeyMapping( + "key.minefactorial.tweakeruler_undo", + KeyConflictContext.IN_GAME, + KeyModifier.CONTROL, + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_Z, + "key.categories.minefactorial" + )); + + public static final Lazy TWEAKERULER_REDO = Lazy.of(() -> new KeyMapping( + "key.minefactorial.tweakeruler_redo", + KeyConflictContext.IN_GAME, + KeyModifier.CONTROL, + InputConstants.Type.KEYSYM, + GLFW.GLFW_KEY_Y, + "key.categories.minefactorial" )); } diff --git a/src/main/java/martian/minefactorial/client/MinefactorialClient.java b/src/main/java/martian/minefactorial/client/MinefactorialClient.java index 745c102..c03fb65 100644 --- a/src/main/java/martian/minefactorial/client/MinefactorialClient.java +++ b/src/main/java/martian/minefactorial/client/MinefactorialClient.java @@ -3,8 +3,12 @@ import com.mojang.blaze3d.platform.InputConstants; import com.mojang.blaze3d.vertex.PoseStack; import martian.minefactorial.Minefactorial; +import martian.minefactorial.client.overlay.scrollmenu.OverlayScrollMenu; +import martian.minefactorial.client.overlay.scrollmenu.ScrollMenu; import martian.minefactorial.client.screen.*; import martian.minefactorial.content.MFTags; +import martian.minefactorial.content.net.PacketServerboundTweakerulerRedo; +import martian.minefactorial.content.net.PacketServerboundTweakerulerUndo; import martian.minefactorial.content.registry.MFDataComponents; import martian.minefactorial.content.registry.MFFluidTypes; import martian.minefactorial.content.registry.MFItems; @@ -29,16 +33,16 @@ import net.neoforged.api.distmarker.OnlyIn; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; -import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; -import net.neoforged.neoforge.client.event.RenderLevelStageEvent; -import net.neoforged.neoforge.client.event.ScreenEvent; +import net.neoforged.neoforge.client.event.*; import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; +import net.neoforged.neoforge.network.PacketDistributor; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.Optional; +import static martian.minefactorial.Minefactorial.id; + @OnlyIn(Dist.CLIENT) public final class MinefactorialClient { private MinefactorialClient() { } @@ -83,6 +87,16 @@ static void registerClientExtensions(final RegisterClientExtensionsEvent event) @SubscribeEvent static void registerKeys(final RegisterKeyMappingsEvent event) { event.register(MFKeys.SHOW_EXTENDED_TOOLTIP.get()); + event.register(MFKeys.SCROLL_MENU_UP.get()); + event.register(MFKeys.SCROLL_MENU_DOWN.get()); + event.register(MFKeys.SCROLL_MENU_SELECT.get()); + event.register(MFKeys.TWEAKERULER_UNDO.get()); + event.register(MFKeys.TWEAKERULER_REDO.get()); + } + + @SubscribeEvent + static void registerGuiLayers(final RegisterGuiLayersEvent event) { + event.registerBelowAll(id("scroll_menu"), new OverlayScrollMenu()); } } @@ -156,6 +170,15 @@ static void onRenderWorld(final RenderLevelStageEvent event) { } } + @SubscribeEvent + static void onClientTick(final ClientTickEvent.Pre event) { + if (MFKeys.TWEAKERULER_UNDO.get().consumeClick()) { + PacketDistributor.sendToServer(new PacketServerboundTweakerulerUndo()); + } else if (MFKeys.TWEAKERULER_REDO.get().consumeClick()) { + PacketDistributor.sendToServer(new PacketServerboundTweakerulerRedo()); + } + } + @SubscribeEvent static void onScreenKeyPress(final ScreenEvent.KeyPressed.Post event) { if (MFKeys.SHOW_EXTENDED_TOOLTIP.get().isActiveAndMatches(InputConstants.Type.KEYSYM.getOrCreate(event.getKeyCode()))) { @@ -169,5 +192,22 @@ static void onScreenKeyRelease(final ScreenEvent.KeyReleased.Post event) { MFItem.showExtendedTooltip = false; } } + + @SubscribeEvent + static void onScroll(final InputEvent.MouseScrollingEvent event) { + if (OverlayScrollMenu.isMenuOpen()) { + ScrollMenu top = OverlayScrollMenu.getTopMenu(); + + top.selection += event.getScrollDeltaY() > 0 ? -1 : 1; + + if (top.selection >= top.entries.size()) { + top.selection = 0; + } else if (top.selection < 0) { + top.selection = top.entries.size() - 1; + } + + event.setCanceled(true); + } + } } } diff --git a/src/main/java/martian/minefactorial/client/overlay/package-info.java b/src/main/java/martian/minefactorial/client/overlay/package-info.java new file mode 100644 index 0000000..7108110 --- /dev/null +++ b/src/main/java/martian/minefactorial/client/overlay/package-info.java @@ -0,0 +1,5 @@ +@OnlyIn(Dist.CLIENT) +package martian.minefactorial.client.overlay; + +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; diff --git a/src/main/java/martian/minefactorial/client/overlay/scrollmenu/OverlayScrollMenu.java b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/OverlayScrollMenu.java new file mode 100644 index 0000000..4aa09a8 --- /dev/null +++ b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/OverlayScrollMenu.java @@ -0,0 +1,74 @@ +package martian.minefactorial.client.overlay.scrollmenu; + +import martian.minefactorial.client.MFKeys; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.LayeredDraw; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.NotNull; + +import java.util.Stack; + +public class OverlayScrollMenu implements LayeredDraw.Layer { + private static final Stack MENUS = new Stack<>(); + + public static final int LINE_HEIGHT = 20; + public static final float EASING = 0.1f; + + @Override + public void render(@NotNull GuiGraphics graphics, @NotNull DeltaTracker delta) { + if (MENUS.isEmpty()) { + return; + } + + // Render each menu + for (int i = 0; i < MENUS.size(); i++) { + int finalI = i; // Copy `i` + MENUS.forEach(menu -> menu.render(graphics, finalI * 64)); + } + + // Get the top menu to control it + ScrollMenu top = MENUS.peek(); + + if (MFKeys.SCROLL_MENU_DOWN.get().consumeClick()) { + top.selection++; + if (top.selection >= top.entries.size()) { + top.selection = 0; + } + } else if (MFKeys.SCROLL_MENU_UP.get().consumeClick()) { + top.selection--; + if (top.selection < 0) { + top.selection = top.entries.size() - 1; + } + } + + if (MFKeys.SCROLL_MENU_SELECT.get().consumeClick()) { + top.entries.get(top.selection).callback().onSelect(); + MENUS.pop(); + } + } + + public static boolean isMenuOpen() { + return !MENUS.isEmpty(); + } + + public static void clearMenus() { + MENUS.clear(); + } + + public static void pushMenu(ScrollMenu menu) { + MENUS.push(menu); + } + + public static ScrollMenu popMenu() { + return MENUS.pop(); + } + + public static ScrollMenu getTopMenu() { + return MENUS.peek(); + } + + public static Stack getMenus() { + return MENUS; + } +} diff --git a/src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenu.java b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenu.java new file mode 100644 index 0000000..a521e5d --- /dev/null +++ b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenu.java @@ -0,0 +1,53 @@ +package martian.minefactorial.client.overlay.scrollmenu; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.util.CommonColors; +import net.minecraft.util.Mth; + +import java.util.ArrayList; +import java.util.List; + +public class ScrollMenu { + public List entries = new ArrayList<>(); + public int selection = 0; + + public int x = 8; + public int y = 0; + public int targetY = y; + + public int textColourUnselected = CommonColors.WHITE; + public int textColourSelected = CommonColors.YELLOW; + + public ScrollMenu() { + } + + public ScrollMenu addEntry(ScrollMenuEntry entry) { + entries.add(entry); + return this; + } + + public void render(GuiGraphics graphics, int xOffset) { + targetY = ((entries.size() - selection) * OverlayScrollMenu.LINE_HEIGHT); + y = Mth.lerpInt(OverlayScrollMenu.EASING, y, targetY); + + ScrollMenuEntry entry; + for (int i = 0; i < entries.size(); i++) { + entry = entries.get(i); + graphics.drawString( + Minecraft.getInstance().font, + entry.text(), + xOffset + (i == selection ? x + 4 : x), + y + (i * OverlayScrollMenu.LINE_HEIGHT), + i == selection ? textColourSelected : textColourUnselected + ); + } + } + + public void triggerSelect(boolean shouldCloseMenu) { + entries.get(selection).callback().onSelect(); + if (shouldCloseMenu) { + OverlayScrollMenu.popMenu(); + } + } +} diff --git a/src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenuEntry.java b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenuEntry.java new file mode 100644 index 0000000..467107b --- /dev/null +++ b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/ScrollMenuEntry.java @@ -0,0 +1,10 @@ +package martian.minefactorial.client.overlay.scrollmenu; + +import net.minecraft.network.chat.Component; + +public record ScrollMenuEntry(Component text, Callback callback) { + @FunctionalInterface + public interface Callback { + void onSelect(); + } +} diff --git a/src/main/java/martian/minefactorial/client/overlay/scrollmenu/package-info.java b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/package-info.java new file mode 100644 index 0000000..8dce8cd --- /dev/null +++ b/src/main/java/martian/minefactorial/client/overlay/scrollmenu/package-info.java @@ -0,0 +1,5 @@ +@OnlyIn(Dist.CLIENT) +package martian.minefactorial.client.overlay.scrollmenu; + +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; diff --git a/src/main/java/martian/minefactorial/content/block/machinery/BlockPumpBE.java b/src/main/java/martian/minefactorial/content/block/machinery/BlockPumpBE.java index 9799be1..5377eed 100644 --- a/src/main/java/martian/minefactorial/content/block/machinery/BlockPumpBE.java +++ b/src/main/java/martian/minefactorial/content/block/machinery/BlockPumpBE.java @@ -68,7 +68,7 @@ public boolean checkForWork(ServerLevel level) { @Override public void doWork(ServerLevel level) { - getTank().fill(new FluidStack(getTargetFluidState(level).getType(), 1000), IFluidHandler.FluidAction.EXECUTE); + ((MFFluidTank) getTank()).forceFill(new FluidStack(getTargetFluidState(level).getType(), 1000), IFluidHandler.FluidAction.EXECUTE); level.setBlockAndUpdate(getTargetPos(), Blocks.AIR.defaultBlockState()); } } diff --git a/src/main/java/martian/minefactorial/content/item/ItemRuler.java b/src/main/java/martian/minefactorial/content/item/ItemRuler.java index 5b90409..d6f97fa 100644 --- a/src/main/java/martian/minefactorial/content/item/ItemRuler.java +++ b/src/main/java/martian/minefactorial/content/item/ItemRuler.java @@ -37,12 +37,13 @@ public void appendHoverText(ItemStack stack, TooltipContext context, List posComponent = context.getItemInHand().get(MFDataComponents.POS); + ItemStack stack = context.getItemInHand(); + @Nullable Optional posComponent = stack.get(MFDataComponents.POS); //noinspection OptionalAssignedToNull if (posComponent == null || posComponent.isEmpty()) { - context.getItemInHand().set(MFDataComponents.POS, Optional.of(context.getClickedPos())); + stack.set(MFDataComponents.POS, Optional.of(context.getClickedPos())); } else { - context.getItemInHand().set(MFDataComponents.POS, Optional.empty()); + stack.set(MFDataComponents.POS, Optional.empty()); } return InteractionResult.SUCCESS; } diff --git a/src/main/java/martian/minefactorial/content/item/ItemTweakeruler.java b/src/main/java/martian/minefactorial/content/item/tweakeruler/ItemTweakeruler.java similarity index 53% rename from src/main/java/martian/minefactorial/content/item/ItemTweakeruler.java rename to src/main/java/martian/minefactorial/content/item/tweakeruler/ItemTweakeruler.java index e5cb8f1..9b62700 100644 --- a/src/main/java/martian/minefactorial/content/item/ItemTweakeruler.java +++ b/src/main/java/martian/minefactorial/content/item/tweakeruler/ItemTweakeruler.java @@ -1,42 +1,36 @@ -package martian.minefactorial.content.item; +package martian.minefactorial.content.item.tweakeruler; -import com.mojang.serialization.Codec; -import io.netty.buffer.ByteBuf; +import martian.minefactorial.client.overlay.scrollmenu.OverlayScrollMenu; +import martian.minefactorial.client.overlay.scrollmenu.ScrollMenu; +import martian.minefactorial.client.overlay.scrollmenu.ScrollMenuEntry; +import martian.minefactorial.content.net.PacketServerboundSetTweakerulerMode; import martian.minefactorial.content.registry.MFDataComponents; import martian.minefactorial.foundation.item.MFItem; -import martian.minefactorial.foundation.world.AABBHelpers; +import martian.minefactorial.managers.TweakerulerHistoryManager; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.util.StringRepresentable; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.phys.AABB; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.network.PacketDistributor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import java.util.List; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; public class ItemTweakeruler extends MFItem { - public ItemTweakeruler(Properties properties, String... hoverText) { - super(properties, hoverText); - } - public ItemTweakeruler(Properties properties, String[] hoverText, String[] longHoverText) { super(properties, hoverText, longHoverText); } @@ -50,7 +44,7 @@ public void appendHoverText(ItemStack stack, TooltipContext context, List posComponent = context.getItemInHand().get(MFDataComponents.POS); //noinspection OptionalAssignedToNull if (posComponent == null || posComponent.isEmpty()) { - context.getItemInHand().set(MFDataComponents.POS, Optional.of(context.getClickedPos())); + BlockPos pos = context.getClickedPos(); + + @Nullable TweakerulerMode modeComponent = context.getItemInHand().get(MFDataComponents.TWEAKERULER_MODE); + if (modeComponent == TweakerulerMode.PLACE) { + // Placing atop the clicked face instead of selecting that block + pos = pos.relative(context.getClickedFace()); + } + + context.getItemInHand().set(MFDataComponents.POS, Optional.of(pos)); } else { BlockPos from = posComponent.get(); BlockPos to = context.getClickedPos(); - @Nullable Mode modeComponent = context.getItemInHand().get(MFDataComponents.TWEAKERULER_MODE); + @Nullable TweakerulerMode modeComponent = context.getItemInHand().get(MFDataComponents.TWEAKERULER_MODE); if (modeComponent != null) { Player player = context.getPlayer(); if (player == null) { return InteractionResult.SUCCESS; // This should never happen } + // Find the non-ruler item ItemStack nonRuler = context.getHand() == InteractionHand.MAIN_HAND ? player.getOffhandItem() : player.getMainHandItem(); - int blocksChanged = modeComponent.run(from, to, context.getItemInHand(), nonRuler, player, context.getLevel()); + // Run the action + TweakerulerHistory history = TweakerulerHistoryManager.getHistoryFor((ServerPlayer) player); + int blocksChanged = history.run(context.getLevel(), from, to, context.getItemInHand().copy(), nonRuler.copy(), player, modeComponent); + // Notify the player with the amount of changed blocks player.sendSystemMessage(Component.translatable("messages.minefactorial.blocks_changed", Component.literal(String.valueOf(blocksChanged)).withStyle(ChatFormatting.RED))); } @@ -94,25 +100,17 @@ public void appendHoverText(ItemStack stack, TooltipContext context, List use(Level level, Player player, InteractionHand usedHand) { ItemStack stack = player.getItemInHand(usedHand); - if (level.isClientSide) { - return InteractionResultHolder.success(stack); - } - - if (player.isCrouching()) { - @Nullable Mode modeComponent = stack.get(MFDataComponents.TWEAKERULER_MODE); - Mode mode = modeComponent == null ? - Mode.NONE : - switch (modeComponent) { - case NONE -> Mode.REPLACE; - case REPLACE -> Mode.REMOVE; - case REMOVE -> Mode.NONE; - }; - stack.set(MFDataComponents.TWEAKERULER_MODE, mode); - player.sendSystemMessage(Component.translatable("messages.minefactorial.set_mode_to", - Component.literal(mode.getSerializedName()).withStyle(ChatFormatting.AQUA))); + if (level.isClientSide()) { + if (!OverlayScrollMenu.isMenuOpen()) { + OverlayScrollMenu.pushMenu(getScrollMenu(player, stack)); + return InteractionResultHolder.success(stack); + } else if (OverlayScrollMenu.getTopMenu() instanceof Menu m) { + // If the menu is a tweakeruler menu, we can close it this way. + m.triggerSelect(true); + } } - return InteractionResultHolder.success(stack); + return InteractionResultHolder.pass(stack); } @Override @@ -125,7 +123,7 @@ public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotI return; } - @Nullable Mode modeComponent = stack.get(MFDataComponents.TWEAKERULER_MODE); + @Nullable TweakerulerMode modeComponent = stack.get(MFDataComponents.TWEAKERULER_MODE); if (modeComponent != null) { player.displayClientMessage(Component.translatable( "messages.minefactorial.current_mode", @@ -135,48 +133,31 @@ public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotI } } - @FunctionalInterface - public interface ITweakerulerAction { - int run(BlockPos from, BlockPos to, ItemStack ruler, ItemStack other, Player player, Level level); - } + @OnlyIn(Dist.CLIENT) + public static ScrollMenu getScrollMenu(Player player, ItemStack stack) { + @Nullable TweakerulerMode modeComponent = stack.get(MFDataComponents.TWEAKERULER_MODE); + TweakerulerMode currentMode = modeComponent == null ? TweakerulerMode.NONE : modeComponent; - public enum Mode implements StringRepresentable, ITweakerulerAction { - NONE((from, to, ruler, other, player, level) -> 0), - REPLACE((from, to, ruler, other, player, level) -> { - Block replaceWith = other.getItem() instanceof BlockItem blockItem ? blockItem.getBlock() : Blocks.AIR; - AtomicInteger blocksChanged = new AtomicInteger(0); - BlockPos.betweenClosedStream(from, to).forEach(pos -> { - level.setBlockAndUpdate(pos, replaceWith.defaultBlockState()); - blocksChanged.incrementAndGet(); - }); - return blocksChanged.get(); - }), - REMOVE((from, to, ruler, ignoredOther, player, level) -> { - AtomicInteger blocksChanged = new AtomicInteger(0); - BlockPos.betweenClosedStream(from, to).forEach(pos -> { - level.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState()); - blocksChanged.incrementAndGet(); - }); - return blocksChanged.get(); - }), - ; - - public static final Codec CODEC = StringRepresentable.fromEnum(Mode::values); - public static final StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodec(CODEC); - - private final ITweakerulerAction action; - - Mode(ITweakerulerAction action) { - this.action = action; - } + Menu menu = new Menu(); - public int run(BlockPos from, BlockPos to, ItemStack ruler, ItemStack other, Player player, Level level) { - return this.action.run(from, to, ruler, other, player, level); - } + for (int i = 0; i < TweakerulerMode.values().length; i++) { + TweakerulerMode mode = TweakerulerMode.values()[i]; + + menu.addEntry(new ScrollMenuEntry(Component.literal(mode.getSerializedName()), () -> { + PacketDistributor.sendToServer(new PacketServerboundSetTweakerulerMode(mode)); +// player.sendSystemMessage(Component.translatable("messages.minefactorial.set_mode_to", +// Component.literal(mode.getSerializedName()).withStyle(ChatFormatting.AQUA))); + })); - @Override - public @NotNull String getSerializedName() { - return this.name().toUpperCase(); + if (mode == currentMode) { + menu.selection = i; + } } + + return menu; + } + + // We extend it here so that we can confirm if a scroll menu was made by the tweakeruler or not later on. + public static class Menu extends ScrollMenu { } } diff --git a/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerAction.java b/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerAction.java new file mode 100644 index 0000000..6265870 --- /dev/null +++ b/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerAction.java @@ -0,0 +1,28 @@ +package martian.minefactorial.content.item.tweakeruler; + +import net.minecraft.core.BlockPos; +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.state.BlockState; + +import java.util.Map; + +public record TweakerulerAction( + Map statesBeforeAction, + BlockPos from, + BlockPos to, + ItemStack rulerStack, + ItemStack otherStack, + Player user, + Callback action +) { + public int run() { + return this.action.run(from, to, rulerStack, otherStack, user, user.level()); + } + + @FunctionalInterface + public interface Callback { + int run(BlockPos from, BlockPos to, ItemStack ruler, ItemStack other, Player player, Level level); + } +} diff --git a/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerHistory.java b/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerHistory.java new file mode 100644 index 0000000..d51b152 --- /dev/null +++ b/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerHistory.java @@ -0,0 +1,47 @@ +package martian.minefactorial.content.item.tweakeruler; + +import martian.minefactorial.foundation.HistoryList; +import net.minecraft.core.BlockPos; +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.state.BlockState; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class TweakerulerHistory extends HistoryList { + protected Level level; + + public TweakerulerHistory(Level level, int maxHistoryLength) { + super(maxHistoryLength); + this.level = level; + } + + @Override + public void onUndo(TweakerulerAction undoneItem) { + undoneItem.statesBeforeAction().forEach(level::setBlockAndUpdate); + } + + @Override + public void onRedo(TweakerulerAction redoneItem) { + redoneItem.run(); + } + + public int run(Level level, BlockPos from, BlockPos to, ItemStack rulerStack, ItemStack otherStack, @Nullable Player user, TweakerulerAction.Callback actionCallback) { + Map beforeTweaking = new HashMap<>(); + BlockPos.betweenClosedStream(from, to).forEach(pos -> { + BlockPos immutable = pos.immutable(); + beforeTweaking.put(immutable, level.getBlockState(immutable)); + }); + TweakerulerAction action = new TweakerulerAction(beforeTweaking, from, to, rulerStack.copy(), otherStack.copy(), user, actionCallback); + return this.run(action); + } + + protected int run(TweakerulerAction action) { + int affected = action.run(); + this.push(action); + return affected; + } +} diff --git a/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerMode.java b/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerMode.java new file mode 100644 index 0000000..f941faf --- /dev/null +++ b/src/main/java/martian/minefactorial/content/item/tweakeruler/TweakerulerMode.java @@ -0,0 +1,70 @@ +package martian.minefactorial.content.item.tweakeruler; + +import com.mojang.serialization.Codec; +import io.netty.buffer.ByteBuf; +import net.minecraft.core.BlockPos; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +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 org.jetbrains.annotations.NotNull; + +import java.util.concurrent.atomic.AtomicInteger; + +public enum TweakerulerMode implements StringRepresentable, TweakerulerAction.Callback { + NONE((from, to, ruler, other, player, level) -> 0), + PLACE((from, to, ruler, other, player, level) -> { + Block replaceWith = other.getItem() instanceof BlockItem blockItem ? blockItem.getBlock() : Blocks.AIR; + AtomicInteger blocksChanged = new AtomicInteger(0); + BlockPos.betweenClosedStream(from, to).forEach(pos -> { + if (level.getBlockState(pos).isAir()) { + level.setBlockAndUpdate(pos, replaceWith.defaultBlockState()); + blocksChanged.incrementAndGet(); + } + }); + return blocksChanged.get(); + }), + REPLACE((from, to, ruler, other, player, level) -> { + Block replaceWith = other.getItem() instanceof BlockItem blockItem ? blockItem.getBlock() : Blocks.AIR; + AtomicInteger blocksChanged = new AtomicInteger(0); + BlockPos.betweenClosedStream(from, to).forEach(pos -> { + level.setBlockAndUpdate(pos, replaceWith.defaultBlockState()); + blocksChanged.incrementAndGet(); + }); + return blocksChanged.get(); + }), + REMOVE((from, to, ruler, ignoredOther, player, level) -> { + AtomicInteger blocksChanged = new AtomicInteger(0); + BlockPos.betweenClosedStream(from, to).forEach(pos -> { + if (!level.getBlockState(pos).isAir()) { + level.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState()); + blocksChanged.incrementAndGet(); + } + }); + return blocksChanged.get(); + }), + ; + + public static final Codec CODEC = StringRepresentable.fromEnum(TweakerulerMode::values); + public static final StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodec(CODEC); + + private final TweakerulerAction.Callback action; + + TweakerulerMode(TweakerulerAction.Callback action) { + this.action = action; + } + + public int run(BlockPos from, BlockPos to, ItemStack ruler, ItemStack other, Player player, Level level) { + return this.action.run(from, to, ruler, other, player, level); + } + + @Override + public @NotNull String getSerializedName() { + return this.name().toUpperCase(); + } +} diff --git a/src/main/java/martian/minefactorial/content/net/PacketServerboundSetTweakerulerMode.java b/src/main/java/martian/minefactorial/content/net/PacketServerboundSetTweakerulerMode.java new file mode 100644 index 0000000..823eeb9 --- /dev/null +++ b/src/main/java/martian/minefactorial/content/net/PacketServerboundSetTweakerulerMode.java @@ -0,0 +1,42 @@ +package martian.minefactorial.content.net; + +import martian.minefactorial.Minefactorial; +import martian.minefactorial.content.item.tweakeruler.TweakerulerMode; +import martian.minefactorial.content.menu.ContainerSmasher; +import martian.minefactorial.content.registry.MFDataComponents; +import martian.minefactorial.content.registry.MFItems; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.NotNull; + +import static martian.minefactorial.Minefactorial.id; + +public record PacketServerboundSetTweakerulerMode(TweakerulerMode mode) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(id("set_tweakeruler_mode")); + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + TweakerulerMode.STREAM_CODEC, PacketServerboundSetTweakerulerMode::mode, + PacketServerboundSetTweakerulerMode::new + ); + + @Override + public @NotNull Type type() { + return TYPE; + } + + public static void handle(PacketServerboundSetTweakerulerMode packet, IPayloadContext context) { + context.enqueueWork(() -> { + final Player player = context.player(); + ItemStack tweakeruler = player.getMainHandItem().is(MFItems.TWEAKERULER) ? player.getMainHandItem() : player.getOffhandItem(); + if (!tweakeruler.is(MFItems.TWEAKERULER)) { + Minefactorial.LOGGER.debug("Player {} sent set_tweakeruler_mode but is not holding a tweakeruler", player); + return; + } + tweakeruler.set(MFDataComponents.TWEAKERULER_MODE, packet.mode); + }); + } +} diff --git a/src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerRedo.java b/src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerRedo.java new file mode 100644 index 0000000..2ec186b --- /dev/null +++ b/src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerRedo.java @@ -0,0 +1,35 @@ +package martian.minefactorial.content.net; + +import martian.minefactorial.content.item.tweakeruler.TweakerulerHistory; +import martian.minefactorial.managers.TweakerulerHistoryManager; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.NotNull; + +import static martian.minefactorial.Minefactorial.LOGGER; +import static martian.minefactorial.Minefactorial.id; + +public record PacketServerboundTweakerulerRedo() implements CustomPacketPayload { + public static final Type TYPE = new Type<>(id("tweakeruler_redo")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.unit(new PacketServerboundTweakerulerRedo()); + + @Override + public @NotNull CustomPacketPayload.Type type() { + return TYPE; + } + + public static void handle(PacketServerboundTweakerulerRedo packet, IPayloadContext context) { + context.enqueueWork(() -> { + final ServerPlayer player = (ServerPlayer) context.player(); + LOGGER.info("Redo requested for {}", player.getName()); + TweakerulerHistory history = TweakerulerHistoryManager.getHistoryFor(player); + history.redo(); + player.sendSystemMessage(Component.literal("Redid changes. data.size: %d, redoHistory.size: %d".formatted(history.dataSize(), history.historySize()))); + }); + } +} diff --git a/src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerUndo.java b/src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerUndo.java new file mode 100644 index 0000000..978a583 --- /dev/null +++ b/src/main/java/martian/minefactorial/content/net/PacketServerboundTweakerulerUndo.java @@ -0,0 +1,35 @@ +package martian.minefactorial.content.net; + +import martian.minefactorial.content.item.tweakeruler.TweakerulerHistory; +import martian.minefactorial.managers.TweakerulerHistoryManager; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import org.jetbrains.annotations.NotNull; + +import static martian.minefactorial.Minefactorial.LOGGER; +import static martian.minefactorial.Minefactorial.id; + +public record PacketServerboundTweakerulerUndo() implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(id("tweakeruler_undo")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.unit(new PacketServerboundTweakerulerUndo()); + + @Override + public @NotNull CustomPacketPayload.Type type() { + return TYPE; + } + + public static void handle(PacketServerboundTweakerulerUndo packet, IPayloadContext context) { + context.enqueueWork(() -> { + final ServerPlayer player = (ServerPlayer) context.player(); + LOGGER.info("Undo requested for {}", player.getName()); + TweakerulerHistory history = TweakerulerHistoryManager.getHistoryFor(player); + history.undo(); + player.sendSystemMessage(Component.literal("Undid changes. data.size: %d, redoHistory.size: %d".formatted(history.dataSize(), history.historySize()))); + }); + } +} diff --git a/src/main/java/martian/minefactorial/content/registry/MFDataComponents.java b/src/main/java/martian/minefactorial/content/registry/MFDataComponents.java index 03f749d..33e8a45 100644 --- a/src/main/java/martian/minefactorial/content/registry/MFDataComponents.java +++ b/src/main/java/martian/minefactorial/content/registry/MFDataComponents.java @@ -1,12 +1,11 @@ package martian.minefactorial.content.registry; import martian.minefactorial.Minefactorial; -import martian.minefactorial.content.item.ItemTweakeruler; +import martian.minefactorial.content.item.tweakeruler.TweakerulerMode; import net.minecraft.core.BlockPos; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.util.StringRepresentable; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; @@ -23,10 +22,10 @@ private MFDataComponents() { } .networkSynchronized(ByteBufCodecs.optional(BlockPos.STREAM_CODEC)) ); - public static final DeferredHolder, DataComponentType> TWEAKERULER_MODE = REGISTRY.registerComponentType( + public static final DeferredHolder, DataComponentType> TWEAKERULER_MODE = REGISTRY.registerComponentType( "tweakeruler_mode", builder -> builder - .persistent(ItemTweakeruler.Mode.CODEC) - .networkSynchronized(ItemTweakeruler.Mode.STREAM_CODEC) + .persistent(TweakerulerMode.CODEC) + .networkSynchronized(TweakerulerMode.STREAM_CODEC) ); } diff --git a/src/main/java/martian/minefactorial/content/registry/MFItems.java b/src/main/java/martian/minefactorial/content/registry/MFItems.java index 9734fd1..22b2525 100644 --- a/src/main/java/martian/minefactorial/content/registry/MFItems.java +++ b/src/main/java/martian/minefactorial/content/registry/MFItems.java @@ -4,6 +4,8 @@ import martian.minefactorial.Minefactorial; import martian.minefactorial.content.MFFoodProperties; import martian.minefactorial.content.item.*; +import martian.minefactorial.content.item.tweakeruler.ItemTweakeruler; +import martian.minefactorial.content.item.tweakeruler.TweakerulerMode; import martian.minefactorial.foundation.item.MFItem; import martian.regolith.DeferredHolders; import martian.regolith.RegolithItemUtil; @@ -70,7 +72,7 @@ private static DeferredItem simpleItem(String id, String[] hoverText) { TWEAKERULER = register("tweakeruler", () -> new ItemTweakeruler( new Item.Properties().stacksTo(1) .component(MFDataComponents.POS, Optional.empty()) - .component(MFDataComponents.TWEAKERULER_MODE, ItemTweakeruler.Mode.NONE), + .component(MFDataComponents.TWEAKERULER_MODE, TweakerulerMode.NONE), getHoverTextIdsFor("tweakeruler", 2), getLongHoverTextIdsFor("tweakeruler", 4))), TREE_TAP = register("tree_tap", () -> new ItemTreeTap(new Item.Properties().stacksTo(1).durability(150), getHoverTextIdsFor("tree_tap"))), diff --git a/src/main/java/martian/minefactorial/foundation/HistoryList.java b/src/main/java/martian/minefactorial/foundation/HistoryList.java new file mode 100644 index 0000000..83ba217 --- /dev/null +++ b/src/main/java/martian/minefactorial/foundation/HistoryList.java @@ -0,0 +1,90 @@ +package martian.minefactorial.foundation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * A helper class for storing and managing a list representing history. + *

+ * {@see TweakerulerHistory} for a usage example. + */ +public class HistoryList { + protected Stack redoHistory = new Stack<>(); + protected List data = new ArrayList<>(); + + /** + * The maximum combined length of each list. + */ + protected int maxLength; + + public HistoryList(int maxLength) { + this.maxLength = maxLength; + } + + /** + * Clears all data and history from this list. + */ + public void clear() { + data.clear(); + redoHistory.clear(); + } + + public int dataSize() { + return data.size(); + } + + public int historySize() { + return redoHistory.size(); + } + + /** + * Pushes an item onto the history. This will remove elements after the index, if + * there are any. + * + * @param it The item to add. + */ + public void push(T it) { + data.add(it); + + // Clear any now-invalid redo history. + if (!redoHistory.isEmpty()) { + redoHistory.clear(); + } + + // If we exceed the history, remove the oldest item on the list. + if (data.size() > maxLength) { + data.removeFirst(); + } + } + + public boolean canUndo() { + return !data.isEmpty(); + } + + public void undo() { + if (canUndo()) { + T undone = data.removeLast(); + onUndo(undone); + redoHistory.push(undone); + } + } + + public boolean canRedo() { + return !redoHistory.isEmpty(); + } + + public void redo() { + if (canRedo()) { + T redone = redoHistory.pop(); + onRedo(redone); + data.add(redone); + } + } + + protected void onUndo(T undoneItem) { + } + + protected void onRedo(T redoneItem) { + } +} diff --git a/src/main/java/martian/minefactorial/foundation/item/MFItem.java b/src/main/java/martian/minefactorial/foundation/item/MFItem.java index 6b3e22b..fa1a7d5 100644 --- a/src/main/java/martian/minefactorial/foundation/item/MFItem.java +++ b/src/main/java/martian/minefactorial/foundation/item/MFItem.java @@ -1,7 +1,5 @@ package martian.minefactorial.foundation.item; -import com.mojang.blaze3d.platform.InputConstants; -import martian.minefactorial.client.MFKeys; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.world.item.Item; diff --git a/src/main/java/martian/minefactorial/managers/TweakerulerHistoryManager.java b/src/main/java/martian/minefactorial/managers/TweakerulerHistoryManager.java new file mode 100644 index 0000000..d8538d1 --- /dev/null +++ b/src/main/java/martian/minefactorial/managers/TweakerulerHistoryManager.java @@ -0,0 +1,27 @@ +package martian.minefactorial.managers; + +import martian.minefactorial.content.item.tweakeruler.TweakerulerHistory; +import net.minecraft.server.level.ServerPlayer; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public final class TweakerulerHistoryManager { + public static final int MAX_LENGTH = 10; + + private static final Map playerHistories = new HashMap<>(); + + private TweakerulerHistoryManager() { } + + public static void removeHistoryFor(ServerPlayer player) { + playerHistories.remove(player.getUUID()); + } + + public static TweakerulerHistory getHistoryFor(ServerPlayer player) { + if (!playerHistories.containsKey(player.getUUID())) { + playerHistories.put(player.getUUID(), new TweakerulerHistory(player.level(), MAX_LENGTH)); + } + return playerHistories.get(player.getUUID()); + } +} diff --git a/src/main/resources/assets/minefactorial/lang/en_us.yml b/src/main/resources/assets/minefactorial/lang/en_us.yml index 941f76e..64b07c1 100644 --- a/src/main/resources/assets/minefactorial/lang/en_us.yml +++ b/src/main/resources/assets/minefactorial/lang/en_us.yml @@ -168,6 +168,11 @@ messages.minefactorial: key.minefactorial: show_extended_tooltip: "Show Extended Tooltip" + scroll_menu_up: "Up (Scroll Menu)" + scroll_menu_down: "Down (Scroll Menu)" + scroll_menu_select: "Select (Scroll Menu)" + tweakeruler_undo: "Undo (Tweakeruler)" + tweakeruler_redo: "Redo (Tweakeruler)" emi.category.minefactorial: maceration: Maceration