diff --git a/src/main/java/ru/windcorp/progressia/test/inv/HandSlots.java b/src/main/java/ru/windcorp/progressia/test/inv/HandSlots.java new file mode 100644 index 0000000..005a36b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/test/inv/HandSlots.java @@ -0,0 +1,110 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.test.inv; + +import glm.vec._2.i.Vec2i; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.backend.InputTracker; +import ru.windcorp.progressia.client.graphics.flat.RenderTarget; +import ru.windcorp.progressia.client.graphics.gui.Component; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; + +public class HandSlots extends Component { + + private final SlotComponent left; + private final SlotComponent right; + + /** + * Right is 0, left is 1 + */ + private float selection = 0; + private static final float ANIMATION_SPEED = 10f; + + public HandSlots(String name, SlotComponent left, SlotComponent right) { + super(name); + this.left = left; + this.right = right; + + addChild(left); + addChild(right); + setLayout(null); + + centerAtOrigin(left); + centerAtOrigin(right); + layoutSelf(); + } + + private static void centerAtOrigin(Component component) { + Vec2i size = component.getPreferredSize(); + component.setBounds(-size.x / 2, -size.y / 2, size); + } + + @Override + protected void assembleChildren(RenderTarget target) { + + Renderable leftRenderable = left.assembleToRenderable(); + Renderable rightRenderable = right.assembleToRenderable(); + + target.addCustomRenderer(renderer -> { + + tickAnimation(); + + renderer.pushTransform().translate( + (float) InputTracker.getCursorX(), + (float) InputTracker.getCursorY(), + 0 + ); + + if (selection > 0.5) { + renderHand(renderer, leftRenderable, selection, -1); + renderHand(renderer, rightRenderable, 1 - selection, +1); + } else { + renderHand(renderer, rightRenderable, 1 - selection, +1); + renderHand(renderer, leftRenderable, selection, -1); + } + + renderer.popTransform(); + + }); + + } + + private float stretch(float t, float zero, float one) { + return zero * (1 - t) + one * t; + } + + private void renderHand(ShapeRenderHelper renderer, Renderable renderable, float selected, float direction) { + + float offsetX = direction * stretch(selected, 40, 0); + float offsetY = direction * stretch(selected, 30, 0); + float scale = selected < 0.5 ? stretch(selected * 2, 0.6f, 1.0f) : 1; + + renderer.pushTransform().translate(offsetX, offsetY, 0).scale(scale); + renderable.render(renderer); + renderer.popTransform(); + + } + + private void tickAnimation() { + float desired = InventoryScreen.isLeftHandSelected() ? 1 : 0; + float difference = selection - desired; + selection += difference * (1 - Math.exp(ANIMATION_SPEED * GraphicsInterface.getFrameLength())); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java b/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java index 08aa9fa..a3c7275 100644 --- a/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java +++ b/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java @@ -22,66 +22,47 @@ import java.util.function.Supplier; import org.lwjgl.glfw.GLFW; -import glm.vec._2.i.Vec2i; import ru.windcorp.progressia.client.graphics.Colors; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.backend.InputTracker; -import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.gui.BasicButton; import ru.windcorp.progressia.client.graphics.gui.Component; import ru.windcorp.progressia.client.graphics.gui.Components; -import ru.windcorp.progressia.client.graphics.gui.Group; -import ru.windcorp.progressia.client.graphics.gui.Layout; import ru.windcorp.progressia.client.graphics.gui.Panel; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill; import ru.windcorp.progressia.client.graphics.input.KeyEvent; +import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent; import ru.windcorp.progressia.client.graphics.input.bus.InputListener; -import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.world.entity.EntityDataPlayer; import ru.windcorp.progressia.common.world.item.ItemContainer; import ru.windcorp.progressia.common.world.item.ItemSlot; import ru.windcorp.progressia.common.world.item.Items; public class InventoryScreen extends Component { + + private static final double MIN_PICK_ALL_DELAY = Units.get("0.5 s"); - public static class CursorFollower extends Component { - - public CursorFollower(Component child) { - super("CursorFollower"); - addChild(child); - setLayout(null); - - Vec2i size = child.getPreferredSize(); - child.setBounds(-size.x / 2, -size.y / 2, size); - layoutSelf(); - } - - @Override - protected void assembleChildren(RenderTarget target) { - Renderable renderable = getChildren().get(0).assembleToRenderable(); - - target.addCustomRenderer(renderer -> { - renderer.pushTransform().translate( - (float) InputTracker.getCursorX(), - (float) InputTracker.getCursorY(), - 0 - ); - - renderable.render(renderer); - - renderer.popTransform(); - - }); - } - + public static boolean isLeftHandSelected() { + return InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) + || InputTracker.isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL); } private final ItemContainer leftHand; private final ItemContainer rightHand; + private final InventoryComponent mainInventory; + + private double lastLeftClick = Double.NEGATIVE_INFINITY; + private ItemSlot lastLeftClickSlot = null; public InventoryScreen(String name, InventoryComponent mainInventory, EntityDataPlayer player) { super(name); + this.mainInventory = mainInventory; + this.leftHand = player.getLeftHand(); + this.rightHand = player.getRightHand(); + setLayout(new LayoutFill(0)); addChild(new Panel(name + ".Background", new LayoutAlign(10), Colors.toVector(0x66000000), null)); @@ -90,47 +71,14 @@ public class InventoryScreen extends Component { mainInventoryPanel.addChild(mainInventory); addChild(Components.center(mainInventoryPanel)); - this.leftHand = player.getLeftHand(); - this.rightHand = player.getRightHand(); - addChild(new CursorFollower(createHands(name, leftHand, rightHand))); - - addListeners(mainInventory); - } - - private Component createHands(String name, ItemContainer leftHand, ItemContainer rightHand) { - SlotComponent leftComponent = new SlotComponent(name + ".HandLeft", leftHand, 0); SlotComponent rightComponent = new SlotComponent(name + ".HandRight", rightHand, 0); + + addChild(new HandSlots(name + ".Hands", leftComponent, rightComponent)); - final int gap = 15; - final int offset = 60; - - Component hands = new Group(name + ".Hands", null, leftComponent, rightComponent); - hands.setLayout(new Layout() { - - @Override - public void layout(Component c) { - Vec2i leftSize = leftComponent.getPreferredSize(); - Vec2i rightSize = rightComponent.getPreferredSize(); - - leftComponent.setBounds(c.getX(), c.getY(), leftSize); - rightComponent.setBounds(c.getX() + leftSize.x + gap, c.getY() + offset, rightSize); - } - - @Override - public Vec2i calculatePreferredSize(Component c) { - Vec2i leftSize = leftComponent.getPreferredSize(); - Vec2i rightSize = rightComponent.getPreferredSize(); - - return new Vec2i( - leftSize.x + gap + rightSize.x, - Math.max(leftSize.y + offset, rightSize.y + offset) - ); - } - - }); - - return hands; + addListeners(mainInventory); + + mainInventory.focusNext(); } private void addListeners(InventoryComponent mainInventory) { @@ -138,18 +86,14 @@ public class InventoryScreen extends Component { ItemSlot left = leftHand.getSlot(0); ItemSlot right = rightHand.getSlot(0); - Supplier handSlot = () -> { - boolean hasCtrl = InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) - || InputTracker.isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL); - - return hasCtrl ? left : right; - }; + Supplier handSlot = () -> isLeftHandSelected() ? left : right; Consumer pickAll = createPickAllAction(handSlot); for (DecoratedSlotComponent component : mainInventory.getSlots()) { component.addAction(pickAll); component.addListener(KeyEvent.class, createRMBAction(component.getSlot(), handSlot)); + component.addListener(WheelScrollEvent.class, createWheelAction(component.getSlot(), handSlot)); } } @@ -158,18 +102,54 @@ public class InventoryScreen extends Component { ItemSlot handSlot = handSlotChooser.get(); ItemSlot invSlot = ((DecoratedSlotComponent) button).getSlot(); - - if (Items.pour(handSlot, invSlot) == 0) { - if (!Items.swap(handSlot, invSlot)) { - return; - } + + boolean success = false; + + double now = GraphicsInterface.getTime(); + if (lastLeftClickSlot == invSlot && now - lastLeftClick < MIN_PICK_ALL_DELAY) { + + lastLeftClickSlot = null; + lastLeftClick = Double.NEGATIVE_INFINITY; + + pickAll(handSlot); + + success = true; + + } else { + lastLeftClick = now; + lastLeftClickSlot = invSlot; + } + + if (!success && !leftHand.getSlot(0).isEmpty() && rightHand.getSlot(0).isEmpty()) { + success = Items.pour(leftHand.getSlot(0), invSlot) != 0; + } + + if (!success) { + success = Items.pour(handSlot, invSlot) != 0; + } + + if (!success) { + success = Items.swap(handSlot, invSlot); } - requestReassembly(); + if (success) { + requestReassembly(); + } }; } + private void pickAll(ItemSlot handSlot) { + for (DecoratedSlotComponent component : mainInventory.getSlots()) { + + Items.pour(component.getSlot(), handSlot); + if (handSlot.isEmpty()) { + break; + } + + } + } + private InputListener createRMBAction(ItemSlot invSlot, Supplier handSlotChooser) { return input -> { @@ -201,5 +181,27 @@ public class InventoryScreen extends Component { }; } + + + private InputListener createWheelAction(ItemSlot invSlot, Supplier handSlotChooser) { + return input -> { + + if (!input.hasVerticalMovement()) { + return false; + } + + ItemSlot handSlot = handSlotChooser.get(); + + ItemSlot from = input.isDown() ? handSlot : invSlot; + ItemSlot into = input.isDown() ? invSlot : handSlot; + + if (Items.pour(from, into, 1) != 0) { + requestReassembly(); + } + + return true; + + }; + } }