Added a visual indicator of selected hand and added scroll actions
This commit is contained in:
parent
040a4f7fd7
commit
e7d0e8fe40
110
src/main/java/ru/windcorp/progressia/test/inv/HandSlots.java
Normal file
110
src/main/java/ru/windcorp/progressia/test/inv/HandSlots.java
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,66 +22,47 @@ import java.util.function.Supplier;
|
|||||||
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import glm.vec._2.i.Vec2i;
|
|
||||||
import ru.windcorp.progressia.client.graphics.Colors;
|
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.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.BasicButton;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Component;
|
import ru.windcorp.progressia.client.graphics.gui.Component;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Components;
|
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.Panel;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
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.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.entity.EntityDataPlayer;
|
||||||
import ru.windcorp.progressia.common.world.item.ItemContainer;
|
import ru.windcorp.progressia.common.world.item.ItemContainer;
|
||||||
import ru.windcorp.progressia.common.world.item.ItemSlot;
|
import ru.windcorp.progressia.common.world.item.ItemSlot;
|
||||||
import ru.windcorp.progressia.common.world.item.Items;
|
import ru.windcorp.progressia.common.world.item.Items;
|
||||||
|
|
||||||
public class InventoryScreen extends Component {
|
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 static boolean isLeftHandSelected() {
|
||||||
|
return InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)
|
||||||
public CursorFollower(Component child) {
|
|| InputTracker.isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL);
|
||||||
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();
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ItemContainer leftHand;
|
private final ItemContainer leftHand;
|
||||||
private final ItemContainer rightHand;
|
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) {
|
public InventoryScreen(String name, InventoryComponent mainInventory, EntityDataPlayer player) {
|
||||||
super(name);
|
super(name);
|
||||||
|
|
||||||
|
this.mainInventory = mainInventory;
|
||||||
|
this.leftHand = player.getLeftHand();
|
||||||
|
this.rightHand = player.getRightHand();
|
||||||
|
|
||||||
setLayout(new LayoutFill(0));
|
setLayout(new LayoutFill(0));
|
||||||
|
|
||||||
addChild(new Panel(name + ".Background", new LayoutAlign(10), Colors.toVector(0x66000000), null));
|
addChild(new Panel(name + ".Background", new LayoutAlign(10), Colors.toVector(0x66000000), null));
|
||||||
@ -90,47 +71,14 @@ public class InventoryScreen extends Component {
|
|||||||
mainInventoryPanel.addChild(mainInventory);
|
mainInventoryPanel.addChild(mainInventory);
|
||||||
addChild(Components.center(mainInventoryPanel));
|
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 leftComponent = new SlotComponent(name + ".HandLeft", leftHand, 0);
|
||||||
SlotComponent rightComponent = new SlotComponent(name + ".HandRight", rightHand, 0);
|
SlotComponent rightComponent = new SlotComponent(name + ".HandRight", rightHand, 0);
|
||||||
|
|
||||||
|
addChild(new HandSlots(name + ".Hands", leftComponent, rightComponent));
|
||||||
|
|
||||||
final int gap = 15;
|
addListeners(mainInventory);
|
||||||
final int offset = 60;
|
|
||||||
|
mainInventory.focusNext();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addListeners(InventoryComponent mainInventory) {
|
private void addListeners(InventoryComponent mainInventory) {
|
||||||
@ -138,18 +86,14 @@ public class InventoryScreen extends Component {
|
|||||||
ItemSlot left = leftHand.getSlot(0);
|
ItemSlot left = leftHand.getSlot(0);
|
||||||
ItemSlot right = rightHand.getSlot(0);
|
ItemSlot right = rightHand.getSlot(0);
|
||||||
|
|
||||||
Supplier<ItemSlot> handSlot = () -> {
|
Supplier<ItemSlot> handSlot = () -> isLeftHandSelected() ? left : right;
|
||||||
boolean hasCtrl = InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL)
|
|
||||||
|| InputTracker.isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL);
|
|
||||||
|
|
||||||
return hasCtrl ? left : right;
|
|
||||||
};
|
|
||||||
|
|
||||||
Consumer<BasicButton> pickAll = createPickAllAction(handSlot);
|
Consumer<BasicButton> pickAll = createPickAllAction(handSlot);
|
||||||
|
|
||||||
for (DecoratedSlotComponent component : mainInventory.getSlots()) {
|
for (DecoratedSlotComponent component : mainInventory.getSlots()) {
|
||||||
component.addAction(pickAll);
|
component.addAction(pickAll);
|
||||||
component.addListener(KeyEvent.class, createRMBAction(component.getSlot(), handSlot));
|
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 handSlot = handSlotChooser.get();
|
||||||
ItemSlot invSlot = ((DecoratedSlotComponent) button).getSlot();
|
ItemSlot invSlot = ((DecoratedSlotComponent) button).getSlot();
|
||||||
|
|
||||||
if (Items.pour(handSlot, invSlot) == 0) {
|
boolean success = false;
|
||||||
if (!Items.swap(handSlot, invSlot)) {
|
|
||||||
return;
|
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<KeyEvent> createRMBAction(ItemSlot invSlot, Supplier<ItemSlot> handSlotChooser) {
|
private InputListener<KeyEvent> createRMBAction(ItemSlot invSlot, Supplier<ItemSlot> handSlotChooser) {
|
||||||
return input -> {
|
return input -> {
|
||||||
|
|
||||||
@ -201,5 +181,27 @@ public class InventoryScreen extends Component {
|
|||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private InputListener<WheelScrollEvent> createWheelAction(ItemSlot invSlot, Supplier<ItemSlot> 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;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user