Added player species and refactored everything hands-related

- Players species is now a thing
  - Currently defines hands, equipment slots (unused), collision model
and appearance
  - EntityDataPlayer now contains a species-provided datalet
  - EntityRenderPlayer now searches for a species-defined delegate to
render players
- Hand count can now be arbitrary
- Split ItemContainerSingle into ItemContainerHand and
ItemContainerEquipment
- Added HUDTextures class
This commit is contained in:
OLEGSHA 2021-09-05 11:16:54 +03:00
parent 73ee339dcc
commit 6cd812f7c3
29 changed files with 1310 additions and 227 deletions

View File

@ -0,0 +1,55 @@
/*
* 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.client.graphics;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
public class ExponentAnimation {
private final float speed;
private float value;
public ExponentAnimation(float speed, float value) {
this.speed = speed;
this.value = value;
}
public float getSpeed() {
return speed;
}
public float getValue() {
return value;
}
public void setValue(float value) {
this.value = value;
}
public float update(float target, double timeStep) {
float difference = value - target;
value += difference * (1 - Math.exp(speed * timeStep));
return value;
}
public float updateForFrame(float target) {
return update(target, GraphicsInterface.getFrameLength());
}
}

View File

@ -123,6 +123,36 @@ public class Shapes {
return result;
}
public static Shape createParallelogram(
// Try saying that 10 times fast
ShapeRenderProgram program,
Vec3 origin,
Vec3 width,
Vec3 height,
Vec4 colorMultiplier,
Texture texture
) {
Shape result = new Shape(
Usage.STATIC,
program,
ShapeParts.createRectangle(
program,
texture,
colorMultiplier,
origin,
width,
height,
false
)
);
return result;
}
public static class PppBuilder {
@ -297,4 +327,108 @@ public class Shapes {
}
public static class PgmBuilder {
private final ShapeRenderProgram program;
private final Vec3 origin = new Vec3(0, 0, 0);
private final Vec3 width = new Vec3(1, 0, 0);
private final Vec3 height = new Vec3(0, 1, 0);
private final Vec4 colorMultiplier = new Vec4(1, 1, 1, 1);
private final Texture texture;
public PgmBuilder(ShapeRenderProgram program, Texture texture) {
this.program = program;
this.texture = texture;
}
public PgmBuilder setOrigin(Vec3 origin) {
this.origin.set(origin);
return this;
}
public PgmBuilder setOrigin(float x, float y, float z) {
this.origin.set(x, y, z);
return this;
}
public PgmBuilder setColorMultiplier(Vec4 colorMultiplier) {
this.colorMultiplier.set(colorMultiplier);
return this;
}
public PgmBuilder setColorMultiplier(float r, float g, float b) {
this.colorMultiplier.set(r, g, b, 1);
return this;
}
public PgmBuilder setColorMultiplier(float r, float g, float b, float a) {
this.colorMultiplier.set(r, g, b, a);
return this;
}
public PgmBuilder setWidth(Vec3 vector) {
this.width.set(vector);
return this;
}
public PgmBuilder setWidth(float x, float y, float z) {
this.width.set(x, y, z);
return this;
}
public PgmBuilder setWidth(float x) {
this.width.set(x, 0, 0);
return this;
}
public PgmBuilder setHeight(Vec3 vector) {
this.height.set(vector);
return this;
}
public PgmBuilder setHeight(float x, float y, float z) {
this.height.set(x, y, z);
return this;
}
public PgmBuilder setHeight(float y) {
this.height.set(0, y, 0);
return this;
}
public PgmBuilder setSize(float x, float y) {
return this.setWidth(x).setHeight(y);
}
public PgmBuilder setSize(float size) {
return this.setSize(size, size);
}
public PgmBuilder centerAt(float x, float y, float z) {
origin.set(x, y, z);
origin.mul(2);
origin.sub(width);
origin.div(2);
return this;
}
public Shape create() {
return createParallelogram(
program,
origin,
width,
height,
colorMultiplier,
texture
);
}
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.client.graphics.world.hud;
import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.texture.SimpleTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.texture.Atlases.AtlasGroup;
import ru.windcorp.progressia.common.resource.ResourceManager;
public class HUDTextures {
private static final AtlasGroup HUD_ATLAS_GROUP = new AtlasGroup("HUD", 1 << 12);
public static Texture getHUDTexture(String name) {
return new SimpleTexture(
Atlases.getSprite(
ResourceManager.getTextureResource("gui/" + name),
HUD_ATLAS_GROUP
)
);
}
public static AtlasGroup getHUDAtlasGroup() {
return HUD_ATLAS_GROUP;
}
}

View File

@ -0,0 +1,136 @@
/*
* 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.client.graphics.world.hud;
import java.util.Arrays;
import java.util.Map;
import com.google.common.collect.Maps;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.ExponentAnimation;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderHorizontal;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutHorizontal;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.world.entity.SpeciesRender;
import ru.windcorp.progressia.client.world.entity.SpeciesRenderRegistry;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
import ru.windcorp.progressia.test.inv.TestInventoryGUIManager;
public class HandsHUD extends Component {
public class ScaledSlotComponent extends Component {
private final SlotComponent slotComponent;
private final ExponentAnimation selected = new ExponentAnimation(10, 1);
public ScaledSlotComponent(SlotComponent component) {
super(component.getName() + ".Scaled");
this.slotComponent = component;
addChild(component);
Vec2i size = slotComponent.getPreferredSize();
setPreferredSize(size.x * 2, size.y * 2);
slotComponent.setBounds(-size.x / 2, 0, size);
}
@Override
protected void assembleChildren(RenderTarget target) {
Renderable renderable = slotComponent.assembleToRenderable();
target.addCustomRenderer(renderer -> {
float scale = player.getEntity().getSelectedHand() == slotComponent.getSlot().getContainer() ? 2 : 1;
renderer.pushTransform()
.translate(getX() + getWidth() / 2, getY(), 0)
.scale(selected.updateForFrame(scale));
renderable.render(renderer);
renderer.popTransform();
});
}
}
public enum Side {
LEFT("Left", LayoutBorderHorizontal.LEFT, 0.0),
RIGHT("Right", LayoutBorderHorizontal.RIGHT, 1.0),
CENTER("Center", LayoutBorderHorizontal.CENTER, 0.5);
private final String ccName;
private final Object lbhHint;
private final double align;
private Side(String ccName, Object lbhHint, double align) {
this.ccName = ccName;
this.lbhHint = lbhHint;
this.align = align;
}
}
private final LocalPlayer player;
public HandsHUD(String name, LocalPlayer player) {
super(name);
this.player = player;
EntityDataPlayer entity = player.getEntity();
String speciesId = entity.getSpecies().getId();
SpeciesRender speciesRender = SpeciesRenderRegistry.getInstance().get(speciesId);
Map<Side, Component> containers = Maps.toMap(
Arrays.asList(Side.values()),
side -> new Group(name + "." + side.ccName, new LayoutHorizontal(15))
);
for (int i = 0; i < entity.getHandCount(); ++i) {
Hand hand = entity.getSpecies().getHands().get(i);
SlotComponent display = new SlotComponent(name + "." + hand.getName(), entity.getHand(i), 0)
.setBackground(speciesRender.getHandBackground(hand), this::shouldRenderHandPlaceholder)
.setScale(2);
Component scaledDisplay = new ScaledSlotComponent(display);
containers.get(speciesRender.getHandSide(hand)).addChild(scaledDisplay);
}
setLayout(new LayoutBorderHorizontal());
containers.forEach((side, comp) -> {
addChild(
new Group(name + "." + side.ccName + ".Aligner", new LayoutAlign(side.align, 0.5, 0), comp)
.setLayoutHint(side.lbhHint)
);
});
}
private boolean shouldRenderHandPlaceholder() {
return TestInventoryGUIManager.layer.hasContainer();
}
}

View File

@ -18,35 +18,22 @@
package ru.windcorp.progressia.client.graphics.world.hud;
import ru.windcorp.progressia.client.graphics.gui.Component;
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.texture.SimpleTextures;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.test.inv.SlotComponent;
public class PermanentHUD extends Component {
public PermanentHUD(String name, LocalPlayer player) {
super(name);
setLayout(new LayoutBorderVertical());
EntityDataPlayer entity = player.getEntity();
if (entity == null) {
throw new IllegalStateException("Player " + player + " does not have an associated entity");
}
Group handDisplays = new Group(
getName() + ".Hands",
new LayoutBorderHorizontal(),
new SlotComponent(getName() + ".Hands.LeftHand", entity.getLeftHand(), 0)
.setBackground(SimpleTextures.get("gui/LeftHand")).setScale(4).setLayoutHint(LayoutBorderHorizontal.LEFT),
new SlotComponent(getName() + ".Hands.RightHand", entity.getRightHand(), 0)
.setBackground(SimpleTextures.get("gui/RightHand")).setScale(4).setLayoutHint(LayoutBorderHorizontal.RIGHT)
);
addChild(handDisplays.setLayoutHint(LayoutBorderVertical.UP));
addChild(new HandsHUD(name + ".Hands", player).setLayoutHint(LayoutBorderVertical.UP));
}
}

View File

@ -15,12 +15,10 @@
* 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;
package ru.windcorp.progressia.client.graphics.world.hud;
import java.util.function.BooleanSupplier;
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;
@ -28,8 +26,7 @@ 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.model.Shapes.PgmBuilder;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.world.item.ItemRenderRegistry;
import ru.windcorp.progressia.client.world.item.ItemRenderable;
@ -52,6 +49,7 @@ public class SlotComponent extends Component {
private String amountDisplayString = "";
private Renderable background = null;
private BooleanSupplier backgroundCondition = null;
public SlotComponent(String name, ItemContainer container, int index) {
super(name);
@ -80,20 +78,18 @@ public class SlotComponent extends Component {
return this;
}
public SlotComponent setBackground(Texture texture, BooleanSupplier when) {
background = new PgmBuilder(FlatRenderProgram.getDefault(), texture).setSize(TEXTURE_SIZE).create();
setBackgroundDisplayCondition(when);
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 setBackground(texture, null);
}
public SlotComponent setBackgroundDisplayCondition(BooleanSupplier backgroundCondition) {
this.backgroundCondition = backgroundCondition;
return this;
}
@ -132,7 +128,9 @@ public class SlotComponent extends Component {
if (itemRenderer != null) {
itemRenderer.render(renderer);
} else if (background != null) {
background.render(renderer);
if (backgroundCondition == null || backgroundCondition.getAsBoolean()) {
background.render(renderer);
}
}
});

View File

@ -0,0 +1,39 @@
/*
* 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.client.world.entity;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class EntityRenderPlayer extends EntityRender {
public EntityRenderPlayer(String id) {
super(id);
}
@Override
public EntityRenderable createRenderable(EntityData entity) {
EntityDataPlayer playerEntity = (EntityDataPlayer) entity;
String speciesId = playerEntity.getSpecies().getId();
SpeciesRender speciesRender = SpeciesRenderRegistry.getInstance().get(speciesId);
return speciesRender.createRenderable(playerEntity);
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.client.world.entity;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.world.hud.HUDTextures;
import ru.windcorp.progressia.client.graphics.world.hud.HandsHUD;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.EquipmentSlot;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
public abstract class SpeciesRender extends Namespaced {
public SpeciesRender(String id) {
super(id);
}
public abstract EntityRenderable createRenderable(EntityDataPlayer entity);
public abstract HandsHUD.Side getHandSide(Hand hand);
public Texture getTexture(String name) {
return HUDTextures.getHUDTexture(getNamespace() + "_" + getName() + "/" + name);
}
public Texture getHandBackground(Hand hand) {
return getTexture("Hand" + hand.getName());
}
public Texture getEquipmentSlotBackground(EquipmentSlot slot) {
return getTexture("EquipmentSlot" + slot.getName());
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.client.world.entity;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class SpeciesRenderRegistry extends NamespacedInstanceRegistry<SpeciesRender> {
private static final SpeciesRenderRegistry INSTANCE = new SpeciesRenderRegistry();
public static SpeciesRenderRegistry getInstance() {
return INSTANCE;
}
public SpeciesRender get(EntityDataPlayer player) {
return get(player.getSpecies().getId());
}
}

View File

@ -18,41 +18,63 @@
package ru.windcorp.progressia.common.world.entity;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.state.IntStateField;
import ru.windcorp.progressia.common.state.ObjectStateField;
import ru.windcorp.progressia.common.world.item.ItemContainerMixedSimple;
import ru.windcorp.progressia.common.world.item.ItemContainerHand;
import ru.windcorp.progressia.common.world.item.ItemContainerMixed;
import ru.windcorp.progressia.common.world.item.ItemContainerMixedSimple;
public class EntityDataPlayer extends EntityData {
private final ObjectStateField<SpeciesDatalet> speciesDatalet = field("Core:SpeciesDatalet").setShared()
.of(SpeciesDataRegistry.getInstance().getCodec()).build();
private final ObjectStateField<ItemContainerMixedSimple> inventory = field("Core:Inventory").setShared().def(
() -> new ItemContainerMixedSimple("Core:PlayerInventory", Units.get(15, "kg"), Units.get(50, "L"))
).build();
private final ObjectStateField<ItemContainerHand> leftHand = field("Core:LeftHand").setShared().def(
() -> new ItemContainerHand("Core:PlayerLeftHand", Units.get(10, "kg"), Units.get(5, "L"))
).build();
private final ObjectStateField<ItemContainerHand> rightHand = field("Core:RightHand").setShared().def(
() -> new ItemContainerHand("Core:PlayerRightHand", Units.get(10, "kg"), Units.get(5, "L"))
).build();
private final IntStateField selectedHand = field("Core:SelectedHand").setShared().ofInt().build();
public EntityDataPlayer(String id) {
public EntityDataPlayer(String id, SpeciesData species) {
super(id);
setCollisionModel(new AABB(0, 0, 1.8f / 2, 0.8f, 0.8f, 1.8f));
setSpecies(species);
}
public ItemContainerMixed getInventory() {
public ItemContainerMixedSimple getInventory() {
return inventory.get(this);
}
public ItemContainerHand getLeftHand() {
return leftHand.get(this);
private void setSpecies(SpeciesData species) {
speciesDatalet.setNow(this, species.createDatalet());
setCollisionModel(species.getCollisionModel());
}
public ItemContainerHand getRightHand() {
return rightHand.get(this);
public SpeciesData getSpecies() {
return speciesDatalet.get(this).getSpecies();
}
public ItemContainerHand getHand(int index) {
return speciesDatalet.get(this).getHands()[index];
}
public int getHandCount() {
return speciesDatalet.get(this).getHands().length;
}
public int getEquipmentCount() {
return speciesDatalet.get(this).getEquipment().length;
}
public int getSelectedHandIndex() {
return selectedHand.get(this);
}
public void setSelectedHandIndexNow(int index) {
selectedHand.setNow(this, index);
}
public ItemContainerHand getSelectedHand() {
return getHand(getSelectedHandIndex());
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.common.world.entity;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import ru.windcorp.progressia.common.state.IOContext;
import ru.windcorp.progressia.common.state.codec.ObjectCodec;
public class SpeciesCodec extends ObjectCodec<SpeciesDatalet> {
public SpeciesCodec() {
super(SpeciesDatalet.class);
}
@Override
protected SpeciesDatalet doRead(SpeciesDatalet previous, DataInput input, IOContext context) throws IOException {
String id = input.readUTF();
SpeciesDatalet result = previous;
if (result == null || !result.getSpecies().getId().equals(id)) {
SpeciesData species = SpeciesDataRegistry.getInstance().get(id);
if (species == null) {
throw new IOException("Unknown species ID " + species);
}
result = species.createDatalet();
}
result.read(input, context);
return result;
}
@Override
protected void doWrite(SpeciesDatalet obj, DataOutput output, IOContext context) throws IOException {
output.writeUTF(obj.getSpecies().getId());
obj.write(output, context);
}
@Override
public SpeciesDatalet copy(SpeciesDatalet object, SpeciesDatalet previous) {
SpeciesDatalet result = previous;
if (result == null || result.getSpecies() != object.getSpecies()) {
result = object.getSpecies().createDatalet();
}
object.copy(result);
return result;
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.common.world.entity;
import java.util.List;
import java.util.function.Predicate;
import com.google.common.collect.ImmutableList;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.util.Named;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.item.ItemData;
public abstract class SpeciesData extends Namespaced {
public static class Hand extends Named {
private int index = -1;
public Hand(String name) {
super(name);
}
public int getIndex() {
return index;
}
}
public static class EquipmentSlot extends Named {
private int index = -1;
private Predicate<ItemData> filter;
public EquipmentSlot(String name, Predicate<ItemData> filter) {
super(name);
this.filter = filter;
}
public int getIndex() {
return index;
}
public Predicate<ItemData> getFilter() {
return filter;
}
}
private List<Hand> hands;
private List<EquipmentSlot> equipmentSlots;
public SpeciesData(String id) {
super(id);
}
public void withHands(Hand... hands) {
if (this.hands != null) {
throw new IllegalStateException("Hands already set");
}
if (hands.length == 0) {
throw new IllegalArgumentException("At least one hand required");
}
this.hands = ImmutableList.copyOf(hands);
for (int i = 0; i < hands.length; ++i) {
hands[i].index = i;
}
}
public void withEquipmentSlots(EquipmentSlot... equipmentSlots) {
if (this.equipmentSlots != null) {
throw new IllegalStateException("Equipment slots already set");
}
this.equipmentSlots = ImmutableList.copyOf(equipmentSlots);
for (int i = 0; i < equipmentSlots.length; ++i) {
equipmentSlots[i].index = i;
}
}
public List<Hand> getHands() {
return hands;
}
public List<EquipmentSlot> getEquipmentSlots() {
return equipmentSlots;
}
public abstract CollisionModel getCollisionModel();
public SpeciesDatalet createDatalet() {
return new SpeciesDatalet(this);
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.common.world.entity;
import ru.windcorp.progressia.common.state.codec.ObjectCodec;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
public class SpeciesDataRegistry extends NamespacedInstanceRegistry<SpeciesData> {
private static final SpeciesDataRegistry INSTANCE = new SpeciesDataRegistry();
private final ObjectCodec<SpeciesDatalet> codec = new SpeciesCodec();
public static SpeciesDataRegistry getInstance() {
return INSTANCE;
}
public ObjectCodec<SpeciesDatalet> getCodec() {
return codec;
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.common.world.entity;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import ru.windcorp.progressia.common.state.Encodable;
import ru.windcorp.progressia.common.state.IOContext;
import ru.windcorp.progressia.common.world.entity.SpeciesData.EquipmentSlot;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
import ru.windcorp.progressia.common.world.item.ItemContainerEquipment;
import ru.windcorp.progressia.common.world.item.ItemContainerHand;
public class SpeciesDatalet implements Encodable {
private final SpeciesData species;
private final ItemContainerHand[] hands;
private final ItemContainerEquipment[] equipment;
public SpeciesDatalet(SpeciesData species) {
this.species = species;
this.hands = new ItemContainerHand[species.getHands().size()];
for (int i = 0; i < hands.length; ++i) {
Hand hand = species.getHands().get(i);
this.hands[i] = new ItemContainerHand(species.getId() + "Hand" + hand.getName(), hand);
}
this.equipment = new ItemContainerEquipment[species.getEquipmentSlots().size()];
for (int i = 0; i < equipment.length; ++i) {
EquipmentSlot equipmentSlot = species.getEquipmentSlots().get(i);
this.equipment[i] = new ItemContainerEquipment(
species.getId() + "EquipmentSlot" + equipmentSlot.getName(),
equipmentSlot
);
}
}
public SpeciesData getSpecies() {
return species;
}
@Override
public void read(DataInput input, IOContext context) throws IOException {
for (int i = 0; i < hands.length; ++i) {
hands[i].read(input, context);
}
for (int i = 0; i < equipment.length; ++i) {
equipment[i].read(input, context);
}
}
@Override
public void write(DataOutput output, IOContext context) throws IOException {
for (int i = 0; i < hands.length; ++i) {
hands[i].write(output, context);
}
for (int i = 0; i < equipment.length; ++i) {
equipment[i].write(output, context);
}
}
@Override
public void copy(Encodable destination) {
SpeciesDatalet other = (SpeciesDatalet) destination;
if (other.getSpecies() != getSpecies()) {
throw new IllegalArgumentException(
"Cannot copy datalet of species " + other.getSpecies() + " into datalet of species " + getSpecies()
);
}
for (int i = 0; i < hands.length; ++i) {
hands[i].copy(other.hands[i]);
}
for (int i = 0; i < equipment.length; ++i) {
equipment[i].copy(other.equipment[i]);
}
}
/**
* @return the hands
*/
public ItemContainerHand[] getHands() {
return hands;
}
/**
* @return the equipment
*/
public ItemContainerEquipment[] getEquipment() {
return equipment;
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.common.world.item;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.world.entity.SpeciesData;
public class ItemContainerEquipment extends ItemContainerSingle {
private static final float EQUIP_MASS_LIMIT = Units.get("15 kg");
private static final float EQUIP_VOLUME_LIMIT = Units.get("60 kg");
private final SpeciesData.EquipmentSlot equipmentSlot;
public ItemContainerEquipment(String id, SpeciesData.EquipmentSlot equipmentSlot) {
super(id, EQUIP_MASS_LIMIT, EQUIP_VOLUME_LIMIT);
this.equipmentSlot = equipmentSlot;
}
public SpeciesData.EquipmentSlot getEquipmentSlot() {
return equipmentSlot;
}
}

View File

@ -17,71 +17,24 @@
*/
package ru.windcorp.progressia.common.world.item;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.function.Consumer;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.world.entity.SpeciesData;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
import ru.windcorp.progressia.common.state.Encodable;
import ru.windcorp.progressia.common.state.IOContext;
public class ItemContainerHand extends ItemContainer {
public class ItemContainerHand extends ItemContainerSingle {
private final ItemSlot slot = new ItemSlot();
private static final float HAND_MASS_LIMIT = Units.get("10 kg");
private static final float HAND_VOLUME_LIMIT = Units.get("5 kg");
private final float massLimit;
private final float volumeLimit;
private final SpeciesData.Hand hand;
public ItemContainerHand(String id, float massLimit, float volumeLimit) {
super(id);
this.massLimit = massLimit;
this.volumeLimit = volumeLimit;
slot.setContainer(this);
public ItemContainerHand(String id, Hand hand) {
super(id, HAND_MASS_LIMIT, HAND_VOLUME_LIMIT);
this.hand = hand;
}
@Override
public void read(DataInput input, IOContext context) throws IOException {
slot.read(input, context);
}
@Override
public void write(DataOutput output, IOContext context) throws IOException {
slot.write(output, context);
}
@Override
public void copy(Encodable destination) {
slot.copy(((ItemContainerHand) destination).slot);
}
@Override
public ItemSlot getSlot(int index) {
if (index == 0) {
return slot;
} else {
return null;
}
}
@Override
public int getSlotCount() {
return 1;
}
@Override
public void forEach(Consumer<ItemSlot> action) {
action.accept(slot);
}
@Override
public float getMassLimit() {
return massLimit;
}
@Override
public float getVolumeLimit() {
return volumeLimit;
public SpeciesData.Hand getHand() {
return hand;
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.common.world.item;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.function.Consumer;
import ru.windcorp.progressia.common.state.Encodable;
import ru.windcorp.progressia.common.state.IOContext;
public abstract class ItemContainerSingle extends ItemContainer {
private final ItemSlot slot = new ItemSlot();
private final float massLimit;
private final float volumeLimit;
public ItemContainerSingle(String id, float massLimit, float volumeLimit) {
super(id);
this.massLimit = massLimit;
this.volumeLimit = volumeLimit;
slot.setContainer(this);
}
@Override
public void read(DataInput input, IOContext context) throws IOException {
slot.read(input, context);
}
@Override
public void write(DataOutput output, IOContext context) throws IOException {
slot.write(output, context);
}
@Override
public void copy(Encodable destination) {
slot.copy(((ItemContainerSingle) destination).slot);
}
@Override
public ItemSlot getSlot(int index) {
if (index == 0) {
return slot;
} else {
return null;
}
}
public ItemSlot slot() {
return slot;
}
@Override
public int getSlotCount() {
return 1;
}
@Override
public void forEach(Consumer<ItemSlot> action) {
action.accept(slot);
}
@Override
public float getMassLimit() {
return massLimit;
}
@Override
public float getVolumeLimit() {
return volumeLimit;
}
}

View File

@ -30,15 +30,14 @@ import ru.windcorp.progressia.client.graphics.texture.ComplexTexture;
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.world.entity.HumanoidModel;
import ru.windcorp.progressia.client.world.entity.EntityRender;
import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry;
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
import ru.windcorp.progressia.common.util.FloatMathUtil;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import static java.lang.Math.*;
public class TestEntityRenderHuman extends EntityRender {
public class HumanModelFactory {
private static final float SECOND_LAYER_OFFSET = 1 / 12f;
@ -51,9 +50,7 @@ public class TestEntityRenderHuman extends EntityRender {
private final TexturePrimitive skin;
public TestEntityRenderHuman(String id) {
super(id);
public HumanModelFactory() {
this.skin = fetchSkin();
ComplexTexture texture = new ComplexTexture(
@ -203,8 +200,7 @@ public class TestEntityRenderHuman extends EntityRender {
return b.build();
}
@Override
public EntityRenderable createRenderable(EntityData entity) {
public EntityRenderable createRenderable(EntityDataPlayer entity) {
return new HumanoidModel(
entity,
@ -237,7 +233,7 @@ public class TestEntityRenderHuman extends EntityRender {
0.0f
),
1.8f / (3 + 3 + 2)
SpeciesDataHuman.HEIGHT / (3 + 3 + 2)
)
.setWalkingArmSwing((float) toRadians(30))
.setWalkingLegSwing((float) toRadians(50))

View File

@ -0,0 +1,42 @@
/*
* 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;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.world.entity.SpeciesData;
public class SpeciesDataHuman extends SpeciesData {
public static final float HEIGHT = Units.get("180 cm");
public static final float WIDTH = Units.get("80 cm");
public SpeciesDataHuman(String id) {
super(id);
withHands(new Hand("Right"), new Hand("Left"));
withEquipmentSlots(/* ._. nope */);
}
@Override
public CollisionModel getCollisionModel() {
return new AABB(0, 0, HEIGHT / 2, WIDTH, WIDTH, HEIGHT);
}
}

View File

@ -0,0 +1,44 @@
/*
* 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;
import ru.windcorp.progressia.client.graphics.world.hud.HandsHUD.Side;
import ru.windcorp.progressia.client.world.entity.EntityRenderable;
import ru.windcorp.progressia.client.world.entity.SpeciesRender;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
public class SpeciesRenderHuman extends SpeciesRender {
private final HumanModelFactory modelFactory = new HumanModelFactory();
public SpeciesRenderHuman(String id) {
super(id);
}
@Override
public EntityRenderable createRenderable(EntityDataPlayer entity) {
return modelFactory.createRenderable(entity);
}
@Override
public Side getHandSide(Hand hand) {
return hand.getIndex() == 0 ? Side.RIGHT : Side.LEFT;
}
}

View File

@ -283,15 +283,23 @@ public class TestContent {
}
private static void registerEntities() {
registerEntity("Core:Player", EntityDataPlayer::new);
register(new TestEntityRenderHuman("Core:Player"));
register(new EntityLogic("Core:Player"));
registerPlayer();
registerEntity("Test:Statie", TestEntityDataStatie::new);
register(new TestEntityRenderStatie("Test:Statie"));
register(new TestEntityLogicStatie("Test:Statie"));
}
private static void registerPlayer() {
SpeciesData human = new SpeciesDataHuman("Core:Human");
SpeciesDataRegistry.getInstance().register(human);
SpeciesRenderRegistry.getInstance().register(new SpeciesRenderHuman("Core:Human"));
registerEntity("Core:Player", id -> new EntityDataPlayer(id, human));
register(new EntityRenderPlayer("Core:Player"));
register(new EntityLogic("Core:Player"));
}
private static void regsiterControls() {
ControlDataRegistry data = ControlDataRegistry.getInstance();
ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance();

View File

@ -84,6 +84,7 @@ public class TestPlayerControls {
private double lastSpacePress = Double.NEGATIVE_INFINITY;
private double lastSprintPress = Double.NEGATIVE_INFINITY;
private double lastCtrlPress = Double.NEGATIVE_INFINITY;
private int selectedBlock = 0;
private int selectedTile = 0;
@ -250,6 +251,13 @@ public class TestPlayerControls {
return false;
switchPlacingMode();
break;
case GLFW.GLFW_KEY_LEFT_CONTROL:
case GLFW.GLFW_KEY_RIGHT_CONTROL:
if (event.isRepeat())
return false;
handleCtrl(event);
break;
default:
return false;
@ -435,8 +443,60 @@ public class TestPlayerControls {
isBlockSelected = !isBlockSelected;
updateGUI();
}
public void handleCtrlIfApplicable(Input input) {
if (input.getEvent() instanceof KeyEvent) {
KeyEvent ke = (KeyEvent) input.getEvent();
if (ke.isRepeat()) {
return;
}
if (ke.getKey() == GLFW.GLFW_KEY_LEFT_CONTROL || ke.getKey() == GLFW.GLFW_KEY_RIGHT_CONTROL) {
handleCtrl(ke);
input.consume();
}
}
}
public EntityData getEntity() {
private void handleCtrl(KeyEvent event) {
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
return;
}
double now = GraphicsInterface.getTime();
int change = 0;
if (event.isPress()) {
change = +1;
lastCtrlPress = now;
} else {
if (now - lastCtrlPress > Units.get("200 ms")) {
change = -1;
lastCtrlPress = Double.NEGATIVE_INFINITY;
}
}
if (change == 0) {
return;
}
if (event.hasShift()) {
change *= -1;
}
int selected = getEntity().getSelectedHandIndex();
int maxSelected = getEntity().getHandCount() - 1;
selected += change;
if (selected < 0) {
selected = maxSelected;
} else if (selected > maxSelected) {
selected = 0;
}
getEntity().setSelectedHandIndexNow(selected);
}
public EntityDataPlayer getEntity() {
return getPlayer().getEntity();
}

View File

@ -20,6 +20,7 @@ package ru.windcorp.progressia.test.inv;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.gui.Button;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.world.hud.SlotComponent;
import ru.windcorp.progressia.common.world.item.ItemContainer;
import ru.windcorp.progressia.common.world.item.ItemSlot;

View File

@ -18,93 +18,135 @@
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.ExponentAnimation;
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;
import ru.windcorp.progressia.client.graphics.world.hud.SlotComponent;
import ru.windcorp.progressia.client.world.entity.SpeciesRender;
import ru.windcorp.progressia.client.world.entity.SpeciesRenderRegistry;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
import ru.windcorp.progressia.common.world.item.ItemContainerHand;
public class HandSlots extends Component {
private final SlotComponent left;
private final SlotComponent right;
private class CursorBoundSlot {
/**
* Right is 0, left is 1
*/
private float selection = 0;
private static final float ANIMATION_SPEED = 10f;
private final SlotComponent component;
private final Renderable renderable;
public HandSlots(String name, SlotComponent left, SlotComponent right) {
super(name);
this.left = left;
this.right = right;
/**
* 0 is not selected, 1 is selected
*/
private final ExponentAnimation selection = new ExponentAnimation(10, 0);
private final double angle;
addChild(left);
addChild(right);
setLayout(null);
public CursorBoundSlot(SlotComponent component, double angle) {
this.component = component;
this.angle = angle;
Vec2i size = component.getPreferredSize();
component.setBounds(-size.x / 2, -size.y / 2, size);
this.renderable = component.assembleToRenderable();
if (player.getHandCount() == 1) {
// Disable opening animation hint
selection.setValue(1);
}
}
public void render(ShapeRenderHelper renderer) {
float target = player.getSelectedHand() == component.getSlot().getContainer() ? 1 : 0;
float sel = selection.updateForFrame(target);
float distance = HandSlots.this.distance * (1 - sel);
float x = (float) Math.cos(angle) * distance;
float y = (float) Math.sin(angle) * distance;
float scale = 0.5f + 0.5f * sel;
renderer.pushTransform().translate(x, y, 0).scale(scale);
boolean popColor = false;
if (sel > 0.5f && component.getSlot().isEmpty()) {
renderer.pushColorMultiplier().mul(1, 1, 1, 1 - 2 * (sel - 0.5f));
popColor = true;
}
renderable.render(renderer);
if (popColor) {
renderer.popColorMultiplier();
}
renderer.popTransform();
}
centerAtOrigin(left);
centerAtOrigin(right);
layoutSelf();
}
private static void centerAtOrigin(Component component) {
Vec2i size = component.getPreferredSize();
component.setBounds(-size.x / 2, -size.y / 2, size);
private final EntityDataPlayer player;
private final float distance = 50;
private final double startAngle = Math.PI / 4;
private final double endAngle = -3 * Math.PI / 4;
private final CursorBoundSlot[] slots;
public HandSlots(String name, EntityDataPlayer player) {
super(name);
this.player = player;
this.slots = new CursorBoundSlot[player.getHandCount()];
// This produces NaN when there is only one hand, but then it is unused
double angleStep = (endAngle - startAngle) / (slots.length - 1);
double angle = startAngle;
for (int i = 0; i < slots.length; ++i) {
SpeciesRender speciesRender = SpeciesRenderRegistry.getInstance().get(player);
ItemContainerHand container = player.getHand(i);
Hand hand = container.getHand();
SlotComponent component = new SlotComponent(name + ".Hand" + hand.getName(), container, 0)
.setBackground(speciesRender.getHandBackground(hand));
addChild(component);
slots[i] = new CursorBoundSlot(component, angle);
angle += angleStep;
}
setLayout(null);
}
@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);
for (CursorBoundSlot slot : slots) {
slot.render(renderer);
}
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()));
}
}

View File

@ -20,8 +20,6 @@ package ru.windcorp.progressia.test.inv;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.lwjgl.glfw.GLFW;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.gui.BasicButton;
@ -35,7 +33,6 @@ import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent;
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
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;
@ -43,16 +40,9 @@ public class InventoryScreen extends Component {
private static final double MIN_PICK_ALL_DELAY = Units.get("0.5 s");
private static boolean isLeftHandSelected = false;
private static double controlStart = Double.NEGATIVE_INFINITY;
public static boolean isLeftHandSelected() {
return isLeftHandSelected;
}
private final ItemContainer leftHand;
private final ItemContainer rightHand;
private final InventoryComponent mainInventory;
private final EntityDataPlayer player;
private double lastLeftClick = Double.NEGATIVE_INFINITY;
private ItemSlot lastLeftClickSlot = null;
@ -60,10 +50,9 @@ public class InventoryScreen extends Component {
public InventoryScreen(String name, InventoryComponent mainInventory, EntityDataPlayer player) {
super(name);
isLeftHandSelected = false;
this.mainInventory = mainInventory;
this.leftHand = player.getLeftHand();
this.rightHand = player.getRightHand();
this.player = player;
setLayout(new LayoutFill(0));
@ -73,39 +62,15 @@ public class InventoryScreen extends Component {
mainInventoryPanel.addChild(mainInventory);
addChild(Components.center(mainInventoryPanel));
SlotComponent leftComponent = new SlotComponent(name + ".HandLeft", leftHand, 0);
SlotComponent rightComponent = new SlotComponent(name + ".HandRight", rightHand, 0);
addChild(new HandSlots(name + ".Hands", leftComponent, rightComponent));
addChild(new HandSlots(name + ".Hands", player));
addListeners(mainInventory);
mainInventory.focusNext();
mainInventory.addListener(KeyEvent.class, input -> {
if (input.getKey() == GLFW.GLFW_KEY_LEFT_CONTROL || input.getKey() == GLFW.GLFW_KEY_RIGHT_CONTROL) {
double now = GraphicsInterface.getTime();
if (input.isPress()) {
isLeftHandSelected = !isLeftHandSelected;
controlStart = now;
} else if (input.isRelease()) {
if (now - controlStart > Units.get("200 ms")) {
isLeftHandSelected = !isLeftHandSelected;
controlStart = Double.NEGATIVE_INFINITY;
}
}
return true;
}
return false;
});
}
private void addListeners(InventoryComponent mainInventory) {
ItemSlot left = leftHand.getSlot(0);
ItemSlot right = rightHand.getSlot(0);
Supplier<ItemSlot> handSlot = () -> isLeftHandSelected() ? left : right;
Supplier<ItemSlot> handSlot = () -> player.getSelectedHand().slot();
Consumer<BasicButton> pickAll = createPickAllAction(handSlot);
@ -139,10 +104,6 @@ public class InventoryScreen extends Component {
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;
}

View File

@ -27,6 +27,7 @@ import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.bus.Input;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.item.ItemContainerMixed;
import ru.windcorp.progressia.test.TestPlayerControls;
public class TestInventoryGUILayer extends GUILayer {
@ -38,6 +39,10 @@ public class TestInventoryGUILayer extends GUILayer {
super("Inventory", new LayoutFill(0));
setCursorPolicy(CursorPolicy.INDIFFERENT);
}
public boolean hasContainer() {
return container != null;
}
public void setContainer(ItemContainerMixed container, EntityDataPlayer player) {
this.container = container;
@ -83,6 +88,8 @@ public class TestInventoryGUILayer extends GUILayer {
}
}
}
TestPlayerControls.getInstance().handleCtrlIfApplicable(input);
super.handleInput(input);
input.consume();

View File

@ -24,7 +24,7 @@ import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class TestInventoryGUIManager {
private static TestInventoryGUILayer layer;
public static TestInventoryGUILayer layer;
public static void setup() {
layer = new TestInventoryGUILayer();

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB