From 4749be6c60dc12bbcb289e42c772374c2f133e3a Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Thu, 2 Sep 2021 22:54:25 +0300 Subject: [PATCH] Added hand item indicators, placeholder art included --- .../ru/windcorp/progressia/client/Client.java | 31 +++++++- .../progressia/client/ClientState.java | 2 + .../progressia/client/events/ClientEvent.java | 68 +++++++++++++++++ .../client/events/NewLocalEntityEvent.java | 51 +++++++++++++ .../client/graphics/world/LayerHUD.java | 69 ++++++++++++++++++ .../client/graphics/world/LocalPlayer.java | 3 +- .../test/inv/DecoratedSlotComponent.java | 6 +- .../progressia/test/inv/SlotComponent.java | 68 +++++++++++++---- .../assets/textures/gui/LeftHand.png | Bin 0 -> 4295 bytes .../assets/textures/gui/RightHand.png | Bin 0 -> 4874 bytes 10 files changed, 279 insertions(+), 19 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/client/events/ClientEvent.java create mode 100644 src/main/java/ru/windcorp/progressia/client/events/NewLocalEntityEvent.java create mode 100644 src/main/java/ru/windcorp/progressia/client/graphics/world/LayerHUD.java create mode 100644 src/main/resources/assets/textures/gui/LeftHand.png create mode 100644 src/main/resources/assets/textures/gui/RightHand.png diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index f356954..6bae089 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -18,14 +18,19 @@ package ru.windcorp.progressia.client; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; import ru.windcorp.progressia.client.comms.ServerCommsChannel; +import ru.windcorp.progressia.client.events.ClientEvent; +import ru.windcorp.progressia.client.events.NewLocalEntityEvent; import ru.windcorp.progressia.client.graphics.world.Camera; import ru.windcorp.progressia.client.graphics.world.EntityAnchor; import ru.windcorp.progressia.client.graphics.world.LocalPlayer; import ru.windcorp.progressia.client.world.WorldRender; +import ru.windcorp.progressia.common.util.crash.ReportingEventBus; import ru.windcorp.progressia.common.world.DefaultWorldData; -import ru.windcorp.progressia.common.world.entity.EntityData; public class Client { @@ -33,6 +38,8 @@ public class Client { private final LocalPlayer localPlayer = new LocalPlayer(this); private final Camera camera = new Camera((float) Math.toRadians(70)); + + private final EventBus eventBus = ReportingEventBus.create("ClientEvents"); private final ServerCommsChannel comms; @@ -41,6 +48,7 @@ public class Client { this.comms = comms; comms.addListener(new DefaultClientCommsListener(this)); + subscribe(this); } public WorldRender getWorld() { @@ -63,17 +71,32 @@ public class Client { return comms; } - public void onLocalPlayerEntityChanged(EntityData entity, EntityData lastKnownEntity) { - if (entity == null) { + @Subscribe + private void onLocalPlayerEntityChanged(NewLocalEntityEvent e) { + if (e.getNewEntity() == null) { getCamera().setAnchor(null); return; } getCamera().setAnchor( new EntityAnchor( - getWorld().getEntityRenderable(entity) + getWorld().getEntityRenderable(e.getNewEntity()) ) ); } + public void subscribe(Object object) { + eventBus.register(object); + } + + public void unsubscribe(Object object) { + eventBus.unregister(object); + } + + public void postEvent(ClientEvent event) { + event.setClient(this); + eventBus.post(event); + event.setClient(null); + } + } diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java index 36d36ed..5ba262b 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientState.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -20,6 +20,7 @@ package ru.windcorp.progressia.client; import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; import ru.windcorp.progressia.client.graphics.GUI; +import ru.windcorp.progressia.client.graphics.world.LayerHUD; import ru.windcorp.progressia.client.graphics.world.LayerWorld; import ru.windcorp.progressia.common.world.DefaultWorldData; import ru.windcorp.progressia.server.ServerState; @@ -56,6 +57,7 @@ public class ClientState { GUI.addBottomLayer(new LayerWorld(client)); GUI.addTopLayer(new LayerTestUI()); + GUI.addTopLayer(new LayerHUD(client)); TestInventoryGUIManager.setup(); GUI.addTopLayer(new LayerAbout()); diff --git a/src/main/java/ru/windcorp/progressia/client/events/ClientEvent.java b/src/main/java/ru/windcorp/progressia/client/events/ClientEvent.java new file mode 100644 index 0000000..965dbf4 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/events/ClientEvent.java @@ -0,0 +1,68 @@ +/* + * 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.client.events; + +import ru.windcorp.progressia.client.Client; + +/** + * An interface for all events issued by a {@link Client}. + */ +public interface ClientEvent { + + /** + * Returns the client instance that this event happened on. + * + * @return the client + */ + Client getClient(); + + /** + * Sets the client instance that the event is posted on. The value provided + * to this method must be returned by subsequent calls to + * {@link #getClient()}. Do not call this method when handling the event. + * + * @param client the client dispatching the event or {@code null} to unbind + * any previously bound client + */ + void setClient(Client client); + + /** + * A default implementation of {@link ClientEvent}. This is not necessarily + * extended by client events. + */ + public static abstract class Default implements ClientEvent { + + private Client client; + + public Default(Client client) { + this.client = client; + } + + @Override + public Client getClient() { + return client; + } + + @Override + public void setClient(Client client) { + this.client = client; + } + + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/events/NewLocalEntityEvent.java b/src/main/java/ru/windcorp/progressia/client/events/NewLocalEntityEvent.java new file mode 100644 index 0000000..6e77028 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/events/NewLocalEntityEvent.java @@ -0,0 +1,51 @@ +/* + * 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.client.events; + +import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.common.world.entity.EntityDataPlayer; + +public interface NewLocalEntityEvent extends ClientEvent { + + EntityDataPlayer getNewEntity(); + EntityDataPlayer getPreviousEntity(); + + public class Immutable extends ClientEvent.Default implements NewLocalEntityEvent { + + private final EntityDataPlayer newEntity; + private final EntityDataPlayer previousEntity; + + public Immutable(Client client, EntityDataPlayer newEntity, EntityDataPlayer previousEntity) { + super(client); + this.newEntity = newEntity; + this.previousEntity = previousEntity; + } + + @Override + public EntityDataPlayer getNewEntity() { + return newEntity; + } + + @Override + public EntityDataPlayer getPreviousEntity() { + return previousEntity; + } + + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerHUD.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerHUD.java new file mode 100644 index 0000000..eb3f8d5 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerHUD.java @@ -0,0 +1,69 @@ +/* + * 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.client.graphics.world; + +import com.google.common.eventbus.Subscribe; + +import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.events.NewLocalEntityEvent; +import ru.windcorp.progressia.client.graphics.gui.Component; +import ru.windcorp.progressia.client.graphics.gui.GUILayer; +import ru.windcorp.progressia.client.graphics.gui.Group; +import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderHorizontal; +import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderVertical; +import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill; +import ru.windcorp.progressia.client.graphics.texture.SimpleTextures; +import ru.windcorp.progressia.test.inv.SlotComponent; + +public class LayerHUD extends GUILayer { + + public LayerHUD(Client client) { + super("LayerHUD", new LayoutFill(15)); + setCursorPolicy(CursorPolicy.INDIFFERENT); + + client.subscribe(this); + } + + @Subscribe + private void onEntityChanged(NewLocalEntityEvent e) { + while (!getRoot().getChildren().isEmpty()) { + getRoot().removeChild(getRoot().getChild(0)); + } + + if (e.getNewEntity() == null) { + return; + } + + Component content = new Group(getName() + ".Content", new LayoutBorderVertical()); + + Group handDisplays = new Group( + getName() + ".Hands", + new LayoutBorderHorizontal(), + new SlotComponent(getName() + ".Hands.LeftHand", e.getNewEntity().getLeftHand(), 0) + .setBackground(SimpleTextures.get("gui/LeftHand")).setScale(4).setLayoutHint(LayoutBorderHorizontal.LEFT), + new SlotComponent(getName() + ".Hands.RightHand", e.getNewEntity().getRightHand(), 0) + .setBackground(SimpleTextures.get("gui/RightHand")).setScale(4).setLayoutHint(LayoutBorderHorizontal.RIGHT) + ); + + content.addChild(handDisplays.setLayoutHint(LayoutBorderVertical.UP)); + + getRoot().addChild(content); + getRoot().requestReassembly(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java index e24c5b9..79d88c2 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LocalPlayer.java @@ -21,6 +21,7 @@ package ru.windcorp.progressia.client.graphics.world; import org.apache.logging.log4j.LogManager; import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.events.NewLocalEntityEvent; import ru.windcorp.progressia.client.world.WorldRender; import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.common.world.entity.EntityData; @@ -82,7 +83,7 @@ public class LocalPlayer { } if (playerEntity != lastKnownEntity) { - getClient().onLocalPlayerEntityChanged(playerEntity, lastKnownEntity); + getClient().postEvent(new NewLocalEntityEvent.Immutable(getClient(), playerEntity, lastKnownEntity)); this.lastKnownEntity = playerEntity; } diff --git a/src/main/java/ru/windcorp/progressia/test/inv/DecoratedSlotComponent.java b/src/main/java/ru/windcorp/progressia/test/inv/DecoratedSlotComponent.java index 5880410..f0b88eb 100644 --- a/src/main/java/ru/windcorp/progressia/test/inv/DecoratedSlotComponent.java +++ b/src/main/java/ru/windcorp/progressia/test/inv/DecoratedSlotComponent.java @@ -28,8 +28,12 @@ public class DecoratedSlotComponent extends Button { private final SlotComponent slotComponent; public DecoratedSlotComponent(String name, ItemContainer container, int index) { + this(name, new SlotComponent(name, container, index)); + } + + public DecoratedSlotComponent(String name, SlotComponent component) { super(name, null, null); - this.slotComponent = new SlotComponent(name, container, index); + this.slotComponent = component; Vec2i size = slotComponent.getPreferredSize().add(2 * BORDER); setPreferredSize(size); diff --git a/src/main/java/ru/windcorp/progressia/test/inv/SlotComponent.java b/src/main/java/ru/windcorp/progressia/test/inv/SlotComponent.java index 2cb13ba..1260c7e 100644 --- a/src/main/java/ru/windcorp/progressia/test/inv/SlotComponent.java +++ b/src/main/java/ru/windcorp/progressia/test/inv/SlotComponent.java @@ -18,11 +18,19 @@ package ru.windcorp.progressia.test.inv; import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.Colors; +import ru.windcorp.progressia.client.graphics.backend.Usage; +import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram; import ru.windcorp.progressia.client.graphics.flat.RenderTarget; import ru.windcorp.progressia.client.graphics.font.Font; import ru.windcorp.progressia.client.graphics.gui.Component; import ru.windcorp.progressia.client.graphics.gui.DynamicLabel; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.Shape; +import ru.windcorp.progressia.client.graphics.model.ShapeParts; +import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.client.world.item.ItemRenderRegistry; import ru.windcorp.progressia.client.world.item.ItemRenderable; import ru.windcorp.progressia.common.world.item.ItemContainer; @@ -32,26 +40,28 @@ import ru.windcorp.progressia.common.world.item.ItemSlot; public class SlotComponent extends Component { static final float TEXTURE_SIZE = 24; - static final float SCALE = 2; private final ItemContainer container; private final int index; - + + private float scale = 2; + private ItemRenderable itemRenderer = null; private int amountDisplayInt = 0; private String amountDisplayString = ""; + private Renderable background = null; + public SlotComponent(String name, ItemContainer container, int index) { super(name); this.container = container; this.index = index; - int side = (int) (TEXTURE_SIZE * SCALE); - setPreferredSize(side, side); + setScale(2); Font sizeFont = new Font().deriveOutlined().withScale(1); - addChild(new DynamicLabel(name + ".Size", sizeFont, () -> amountDisplayString, side)); + addChild(new DynamicLabel(getName() + ".Size", sizeFont, () -> amountDisplayString, getPreferredSize().x)); setLayout(new LayoutAlign(0, 0, 0)); } @@ -60,12 +70,36 @@ public class SlotComponent extends Component { return container.getSlot(index); } + public SlotComponent setScale(float scale) { + this.scale = scale; + + int side = (int) (TEXTURE_SIZE * scale); + setPreferredSize(side, side); + invalidate(); + + return this; + } + + public SlotComponent setBackground(Texture texture) { + background = new Shape( + Usage.STATIC, + FlatRenderProgram.getDefault(), + ShapeParts.createRectangle( + FlatRenderProgram.getDefault(), + texture, + Colors.WHITE, + new Vec3(0, 0, 0), + new Vec3(24, 0, 0), + new Vec3(0, 24, 0), + false + ) + ); + return this; + } + @Override protected void assembleSelf(RenderTarget target) { super.assembleSelf(target); - - updateItemRenderer(); - assembleItem(target); } @@ -90,11 +124,19 @@ public class SlotComponent extends Component { } private void assembleItem(RenderTarget target) { - if (itemRenderer != null) { - target.pushTransform(new Mat4().translate(getX(), getY(), 0).scale(SCALE)); - target.addCustomRenderer(itemRenderer); - target.popTransform(); - } + target.pushTransform(new Mat4().translate(getX(), getY(), 0).scale(scale)); + target.addCustomRenderer(renderer -> { + + updateItemRenderer(); + + if (itemRenderer != null) { + itemRenderer.render(renderer); + } else if (background != null) { + background.render(renderer); + } + + }); + target.popTransform(); } } diff --git a/src/main/resources/assets/textures/gui/LeftHand.png b/src/main/resources/assets/textures/gui/LeftHand.png new file mode 100644 index 0000000000000000000000000000000000000000..febc326cd04dede137ad0113d355b3f88671a3d6 GIT binary patch literal 4295 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 HJbdym$MP(s literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/textures/gui/RightHand.png b/src/main/resources/assets/textures/gui/RightHand.png new file mode 100644 index 0000000000000000000000000000000000000000..55b5049cf3bb6f94eb16afec06fd8e245e0138ee GIT binary patch literal 4874 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