Compare commits

...
This repository has been archived on 2022-10-09. You can view files and clone it, but cannot push or open issues or pull requests.

23 Commits

Author SHA1 Message Date
38021852d0
Made inventory windows draggable 2021-12-22 01:41:55 +03:00
9c85164ed1
Fixed inventory hiding and improved HUD event handling 2021-12-21 23:28:19 +03:00
ce573b51ce
Merge branch 'master' into add-items 2021-12-21 17:12:45 +03:00
a028f6f3c7
Merge branch 'master' into add-items
If I ever become a supervillain, I will torture my victims by forcing
them to merge branches in Progressia

Does not work properly, waiting for an additional patch from master
2021-12-20 23:36:55 +03:00
9b67897896
Moved window layout into a separate file 2021-12-07 14:40:31 +03:00
3641c4130b
Rewrote ItemContainers from scratch
- ItemContainers have been entirely rewritten
  - ItemSlots are now wrappers, not owners
- Added InventoryOwners
  - Not yet used
- Added ItemDataWithContainers
- Added some basic windowing functionality to WindowedHUD
2021-11-16 23:49:17 +03:00
be6203719a
Attempted to make slots dynamic and failed 2021-10-31 15:26:48 +03:00
9885a1ca42
Visual improvements
- Added a custom font for item amount displays
- Added an indicator for openable items
- Changed open item indicator to be more obvious
- Fixed ExponentAnimation overshooting target during FPS spikes
2021-10-17 15:10:24 +03:00
c6e6dc6851
Added the concept of inventories
I'll end myself if I spend any more time on this neverending commit
2021-10-17 14:02:54 +03:00
30464febf6 Added item containers
- Added ItemDataContainer. It is an item that has a container
  - Added Test:CardboardBackpack
- Added back inventory GUIs
  - Now in window form!
    - *not interactive (don't sue us)
- Hand background icons now only render when an inventory is open
- Removed intrinsic player inventory because we're hardcore
2021-09-10 23:11:15 +03:00
782b3ef553
Hotfixed custom renderables and Hider
Man, flat render code is a huge mess
2021-09-10 20:38:42 +03:00
05b1c73fbc
Rewrote inventory GUI code to be more coherent. WIP
- Added an equipment slot
- Added equipment slot displays
- Added HUDManager and refactored layer management
- Moved most files from .test.inv to .client.graphics.world.hud
- Added component hider
  - Which is broken because z-index
    - Why do I use depth stencils in flat layers again?
- Refactored KeyMatcher: removed Builders, added action selection
2021-09-10 00:27:12 +03:00
IvanZ
bd6442318d
Added hand icons by IvanZ 2021-09-05 21:04:48 +03:00
6cd812f7c3 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
2021-09-05 11:16:54 +03:00
73ee339dcc
Working toward player species? I think?
- Left/right hand can now be permanently switched by tapping ctrl
- Moved LayerHUD into .hud subpackage
  - Extracted PermanentHUD out of LayerHUD
- Fixed the issuer of NewLocalEntityEvent
2021-09-04 17:12:54 +03:00
4749be6c60
Added hand item indicators, placeholder art included 2021-09-02 22:54:25 +03:00
a4b731e8a5
Added mass/volume limit enforcement and displays
- Also added Test:RedGraniteCobblestone
2021-08-30 23:36:13 +03:00
e7d0e8fe40
Added a visual indicator of selected hand and added scroll actions 2021-08-30 21:33:01 +03:00
040a4f7fd7
Added stack merging, RMB actions, Sticks and fixed an unholy bug
- Added Test:Stick
- When left-clicking, stacks that can merge will
- Added right-clicking behavior straight from Minecraft
- hooooooly sh*t man StatefulObject.equals was totally broken omfg how
tf i did notice :o
2021-08-30 18:09:08 +03:00
b3ac7b6afe
Item stack size is now defined by ItemSlot, not ItemData 2021-08-28 21:07:08 +03:00
2fe84dc59e
Working on item management 2021-08-28 17:50:45 +03:00
00ea4a6281
Added player hand containers
- Added EntityDataPlayer.{left,right}Hand
  - No in-world effect yet
- List-like functionality of ItemContainer extracted into
ItemContainerMixed
- Reworked implementation of Inventory screen
- Added optional arguments for Group
- Added Components.center()
2021-08-28 00:26:35 +03:00
f186fc602d
Added ItemData, ItemRender and related stuff
- Added ItemData, ItemSlot, ItemContainer
- Added ItemRender
- Added a player inventory
- Added temporary inventory display
- Font now has a scale setting
2021-08-26 18:56:32 +03:00
113 changed files with 6264 additions and 178 deletions

View File

@ -18,21 +18,42 @@
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.GUI;
import ru.windcorp.progressia.client.graphics.world.Camera;
import ru.windcorp.progressia.client.graphics.world.EntityAnchor;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.graphics.world.hud.HUDManager;
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;
import ru.windcorp.progressia.test.LayerAbout;
import ru.windcorp.progressia.test.LayerDebug;
import ru.windcorp.progressia.test.LayerTestUI;
public class Client {
private final WorldRender world;
private final LayerWorld layerWorld = new LayerWorld(this);
private final LayerTestUI layerTestUI = new LayerTestUI();
private final LayerAbout layerAbout = new LayerAbout();
private final LayerDebug layerDebug = new LayerDebug();
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 HUDManager hudManager = new HUDManager(this);
private final ServerCommsChannel comms;
@ -41,6 +62,22 @@ public class Client {
this.comms = comms;
comms.addListener(new DefaultClientCommsListener(this));
subscribe(this);
}
public void install() {
GUI.addBottomLayer(layerWorld);
GUI.addTopLayer(layerTestUI);
hudManager.install();
GUI.addTopLayer(layerAbout);
}
public void remove() {
GUI.removeLayer(layerWorld);
GUI.removeLayer(layerTestUI);
hudManager.remove();
GUI.removeLayer(layerAbout);
GUI.removeLayer(layerDebug);
}
public WorldRender getWorld() {
@ -62,18 +99,45 @@ public class Client {
public ServerCommsChannel getComms() {
return comms;
}
public HUDManager getHUD() {
return hudManager;
}
public void onLocalPlayerEntityChanged(EntityData entity, EntityData lastKnownEntity) {
if (entity == null) {
public void toggleDebugLayer() {
if (GUI.getLayers().contains(layerDebug)) {
GUI.removeLayer(layerDebug);
} else {
GUI.addTopLayer(layerDebug);
}
}
@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);
}
}

View File

@ -27,6 +27,7 @@ import ru.windcorp.progressia.client.graphics.font.GNUUnifontLoader;
import ru.windcorp.progressia.client.graphics.font.Typefaces;
import ru.windcorp.progressia.client.graphics.texture.Atlases;
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
import ru.windcorp.progressia.client.graphics.world.hud.HUDTextures;
import ru.windcorp.progressia.client.localization.Localizer;
import ru.windcorp.progressia.common.resource.ResourceManager;
import ru.windcorp.progressia.common.util.crash.CrashReports;
@ -47,6 +48,7 @@ public class ClientProxy implements Proxy {
() -> Typefaces
.setDefault(GNUUnifontLoader.load(ResourceManager.getResource("assets/unifont-13.0.03.hex.gz")))
);
RenderTaskQueue.waitAndInvoke(HUDTextures::loadItemAmountTypeface);
} catch (InterruptedException e) {
throw CrashReports.report(e, "ClientProxy failed");
}

View File

@ -20,14 +20,10 @@ 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.Layer;
import ru.windcorp.progressia.client.graphics.world.LayerWorld;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
import ru.windcorp.progressia.server.ServerState;
import ru.windcorp.progressia.test.LayerAbout;
import ru.windcorp.progressia.test.LayerTestText;
import ru.windcorp.progressia.test.LayerTestUI;
import ru.windcorp.progressia.test.TestContent;
public class ClientState {
@ -70,24 +66,14 @@ public class ClientState {
if (client != null && client.getLocalPlayer().hasEntity()) {
GUI.removeLayer(layer);
// TODO refactor, this shouldn't be here
LayerWorld layerWorld = new LayerWorld(client);
LayerTestUI layerUI = new LayerTestUI();
LayerAbout layerAbout = new LayerAbout();
GUI.addBottomLayer(layerWorld);
GUI.addTopLayer(layerUI);
GUI.addTopLayer(layerAbout);
client.install();
}
}));
}
public static void disconnectFromLocalServer() {
getInstance().getComms().disconnect();
for (Layer layer : GUI.getLayers()) {
GUI.removeLayer(layer);
}
getInstance().remove();
}
private ClientState() {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}
}

View File

@ -55,6 +55,25 @@ public class Colors {
output = new Vec4();
return color.mul(multiplier, multiplier, multiplier, 1, output);
}
public static Vec4 mix(Vec4 zero, Vec4 one, float t, Vec4 output) {
if (output == null) {
output = new Vec4();
}
if (t <= 0) {
return output.set(zero);
} else if (t >= 1) {
return output.set(one);
}
return output.set(
zero.x * (1 - t) + one.x * t,
zero.y * (1 - t) + one.y * t,
zero.z * (1 - t) + one.z * t,
zero.w * (1 - t) + one.w * t
);
}
public static Vec4 toVector(int argb, Vec4 output) {
output.w = ((argb & 0xFF000000) >>> 24) / (float) 0xFF; // Alpha

View File

@ -0,0 +1,61 @@
/*
* 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));
float newDifference = value - target;
if (difference * newDifference < 0) {
// Whoops, we've overshot
value = target;
}
return value;
}
public float updateForFrame(float target) {
return update(target, GraphicsInterface.getFrameLength());
}
}

View File

@ -65,6 +65,12 @@ public class GUI {
layer.onRemoved();
});
}
public static void updateLayer(Layer layer) {
modify(layers -> {
// Do nothing
});
}
private static void modify(LayerStackModification mod) {
MODIFICATION_QUEUE.add(mod);

View File

@ -191,7 +191,8 @@ public class RenderTarget {
assembleCurrentClipFromFaces();
float depth = this.depth--;
Mat4 transform = new Mat4().translate(0, 0, depth).mul(getTransform());
final float kostyl = 1e-2f;
Mat4 transform = new Mat4().translate(0, 0, depth).scale(1, 1, kostyl).mul(getTransform());
assembled.add(new Clip(maskStack, transform, renderable));
}

View File

@ -32,20 +32,23 @@ public class Font {
private final int style;
private final float align;
private final Vec4 color;
private final int scale;
public Font(Typeface typeface, int style, float align, Vec4 color) {
public Font(Typeface typeface, int style, float align, Vec4 color, int scale) {
this.typeface = typeface;
this.style = style;
this.align = align;
this.color = color;
this.scale = scale;
}
public Font(Typeface typeface, int style, float align, int color) {
this(typeface, style, align, Colors.toVector(color));
public Font(Typeface typeface, int style, float align, int color, int scale) {
this(typeface, style, align, Colors.toVector(color), scale);
}
public Font(Typeface typeface) {
this(typeface, Typeface.Style.PLAIN, Typeface.ALIGN_LEFT, Colors.WHITE);
this(typeface, Typeface.Style.PLAIN, Typeface.ALIGN_LEFT, Colors.WHITE, 2);
}
public Font() {
@ -67,31 +70,63 @@ public class Font {
public Vec4 getColor() {
return color;
}
public Renderable assemble(
CharSequence chars,
float maxWidth
) {
return typeface.assembleStatic(chars, style, align, maxWidth, color);
public int getScale() {
return scale;
}
private Renderable applyScale(Renderable unscaled) {
if (scale == 1) {
return unscaled;
}
return renderer -> {
renderer.pushTransform().scale(scale);
unscaled.render(renderer);
renderer.popTransform();
};
}
public Renderable assembleDynamic(
Supplier<CharSequence> supplier,
float maxWidth
) {
return typeface.assembleDynamic(supplier, style, align, maxWidth, color);
public Renderable assemble(CharSequence chars, float maxWidth) {
return applyScale(typeface.assembleStatic(chars, style, align, maxWidth, color));
}
public Renderable assembleDynamic(Supplier<CharSequence> supplier, float maxWidth) {
return applyScale(typeface.assembleDynamic(supplier, style, align, maxWidth, color));
}
public Renderable assemble(CharSequence chars) {
return assemble(chars, Float.POSITIVE_INFINITY);
}
public Renderable assembleDynamic(Supplier<CharSequence> supplier) {
return assembleDynamic(supplier, Float.POSITIVE_INFINITY);
}
public int getWidth(CharSequence chars, float maxWidth) {
return typeface.getWidth(chars, style, align, maxWidth);
return scale * typeface.getWidth(chars, style, align, maxWidth);
}
public int getHeight(CharSequence chars, float maxWidth) {
return typeface.getHeight(chars, style, align, maxWidth);
return scale * typeface.getHeight(chars, style, align, maxWidth);
}
public Vec2i getSize(CharSequence chars, float maxWidth, Vec2i result) {
return typeface.getSize(chars, style, align, maxWidth, result);
result = typeface.getSize(chars, style, align, maxWidth, result);
result.mul(scale);
return result;
}
public int getWidth(CharSequence chars) {
return getWidth(chars, Float.POSITIVE_INFINITY);
}
public int getHeight(CharSequence chars) {
return getHeight(chars, Float.POSITIVE_INFINITY);
}
public Vec2i getSize(CharSequence chars, Vec2i result) {
return getSize(chars, Float.POSITIVE_INFINITY, result);
}
public boolean supports(char c) {
@ -106,7 +141,7 @@ public class Font {
* @return the new font
*/
public Font withStyle(int style) {
return new Font(getTypeface(), style, getAlign(), getColor());
return new Font(getTypeface(), style, getAlign(), getColor(), getScale());
}
public Font deriveBold() {
@ -158,15 +193,19 @@ public class Font {
}
public Font withAlign(float align) {
return new Font(getTypeface(), getStyle(), align, getColor());
return new Font(getTypeface(), getStyle(), align, getColor(), getScale());
}
public Font withColor(Vec4 color) {
return new Font(getTypeface(), getStyle(), getAlign(), color);
return new Font(getTypeface(), getStyle(), getAlign(), color, getScale());
}
public Font withColor(int color) {
return new Font(getTypeface(), getStyle(), getAlign(), color);
return new Font(getTypeface(), getStyle(), getAlign(), color, getScale());
}
public Font withScale(int scale) {
return new Font(getTypeface(), getStyle(), getAlign(), getColor(), scale);
}
}

View File

@ -48,7 +48,9 @@ public abstract class BasicButton extends Component {
this.label = label;
setLayout(new LayoutAlign(10));
addChild(this.label);
if (label != null) {
addChild(this.label);
}
setFocusable(true);
reassembleAt(ARTrigger.HOVER, ARTrigger.FOCUS, ARTrigger.ENABLE);
@ -151,5 +153,9 @@ public abstract class BasicButton extends Component {
public Label getLabel() {
return label;
}
public boolean hasLabel() {
return label != null;
}
}

View File

@ -25,6 +25,9 @@ import ru.windcorp.progressia.client.graphics.Colors;
public class Button extends BasicButton {
public static final int MARGIN = 2;
public static final int BORDER = 2;
public Button(String name, String label, Font labelFont) {
super(name, label, labelFont);
}
@ -60,15 +63,23 @@ public class Button extends BasicButton {
} else {
backgroundColor = Colors.WHITE;
}
target.fill(getX() + 2, getY() + 2, getWidth() - 4, getHeight() - 4, backgroundColor);
target.fill(
getX() + MARGIN,
getY() + MARGIN,
getWidth() - 2 * MARGIN,
getHeight() - 2 * MARGIN,
backgroundColor
);
}
// Change label font color
if (isPressed()) {
getLabel().setFont(getLabel().getFont().withColor(Colors.WHITE));
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
if (hasLabel()) {
if (isPressed()) {
getLabel().setFont(getLabel().getFont().withColor(Colors.WHITE));
} else {
getLabel().setFont(getLabel().getFont().withColor(Colors.BLACK));
}
}
}

View File

@ -30,7 +30,9 @@ import com.google.common.eventbus.Subscribe;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
import ru.windcorp.progressia.client.graphics.flat.AssembledFlatRenderHelper;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget.Clip;
import ru.windcorp.progressia.client.graphics.gui.event.ChildAddedEvent;
import ru.windcorp.progressia.client.graphics.gui.event.ChildRemovedEvent;
import ru.windcorp.progressia.client.graphics.gui.event.EnableEvent;
@ -43,6 +45,7 @@ import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.common.util.Named;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.util.crash.ReportingEventBus;
@ -557,9 +560,7 @@ public class Component extends Named {
}
public void removeInputListener(InputListener<?> listener) {
if (inputBus != null) {
inputBus.unregister(listener);
}
inputBus.unregister(listener);
}
protected boolean passInputToChildren(InputEvent e) {
@ -761,6 +762,20 @@ public class Component extends Named {
}
}
public Renderable assembleToRenderable() {
RenderTarget target = new RenderTarget();
assemble(target);
Clip[] clips = target.assemble();
return renderer -> {
for (Clip clip : clips) {
clip.render((AssembledFlatRenderHelper) renderer);
}
};
}
// /**
// * Returns a component that displays this component in its center.

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.client.graphics.gui;
import java.util.function.BooleanSupplier;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
public class Components {
public static Component center(Component c) {
return new Group(c.getName() + ".Centerer", new LayoutAlign(), c);
}
public static Component hide(Component c, BooleanSupplier shouldHide) {
return new Hider(c.getName() + ".Hider", c, shouldHide);
}
private Components() {
}
}

View File

@ -33,7 +33,7 @@ public class DynamicLabel extends Component {
super(name);
this.font = font;
this.contents = contents;
setPreferredSize(width, font.getHeight("", Float.POSITIVE_INFINITY) * 2);
setPreferredSize(width, font.getHeight("", Float.POSITIVE_INFINITY));
}
public Font getFont() {
@ -46,7 +46,7 @@ public class DynamicLabel extends Component {
@Override
protected void assembleSelf(RenderTarget target) {
target.pushTransform(new Mat4().identity().translate(getX(), getY(), -1000).scale(2));
target.pushTransform(new Mat4().identity().translate(getX(), getY(), -1000));
target.addCustomRenderer(font.assembleDynamic(getContentSupplier(), Float.POSITIVE_INFINITY));
target.popTransform();
}

View File

@ -24,5 +24,13 @@ public class Group extends Component {
super(name);
setLayout(layout);
}
public Group(String name, Layout layout, Component... children) {
this(name, layout);
for (Component child : children) {
addChild(child);
}
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.gui;
import java.util.function.BooleanSupplier;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.model.Renderable;
public class Hider extends Component {
private final BooleanSupplier shouldHide;
private final Component contents;
public Hider(String name, Component contents, BooleanSupplier shouldHide) {
super(name);
this.contents = contents;
this.shouldHide = shouldHide;
setLayout(new LayoutFill());
addChild(contents);
}
@Override
protected boolean passInputToChildren(InputEvent e) {
return !shouldHide.getAsBoolean();
}
@Override
public synchronized Component findFocused() {
if (shouldHide.getAsBoolean()) {
return null;
}
return super.findFocused();
}
@Override
protected void assembleChildren(RenderTarget target) {
Renderable renderable = contents.assembleToRenderable();
target.addCustomRenderer(renderer -> {
if (!shouldHide.getAsBoolean()) {
renderable.render(renderer);
}
});
}
}

View File

@ -70,7 +70,7 @@ public class Label extends Component {
public void update() {
currentText = contents.get();
currentSize = font.getSize(currentText, maxWidth, null).mul(2);
currentSize = font.getSize(currentText, maxWidth, null);
requestReassembly();
}
@ -99,12 +99,8 @@ public class Label extends Component {
protected void assembleSelf(RenderTarget target) {
float startX = getX() + font.getAlign() * (getWidth() - currentSize.x);
target.pushTransform(
new Mat4().identity().translate(startX, getY(), 0).scale(2)
);
target.pushTransform(new Mat4().identity().translate(startX, getY(), 0));
target.addCustomRenderer(font.assemble(currentText, maxWidth));
target.popTransform();
}

View File

@ -125,6 +125,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 {
@ -315,4 +345,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

@ -15,20 +15,25 @@
* 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;
import org.apache.logging.log4j.LogManager;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.events.ClientEvent;
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;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class LocalPlayer {
private final Client client;
private long entityId = EntityData.NULL_ENTITY_ID;
private EntityData lastKnownEntity = null;
private EntityDataPlayer lastKnownEntity = null;
private final Selection selection = new Selection();
@ -59,19 +64,32 @@ public class LocalPlayer {
return getEntity() != null;
}
public EntityData getEntity() {
public EntityDataPlayer getEntity() {
if (!hasEntityId()) {
return null;
}
EntityData entity = getClient().getWorld().getData().getEntity(getEntityId());
EntityDataPlayer playerEntity;
if (entity != lastKnownEntity) {
getClient().onLocalPlayerEntityChanged(entity, lastKnownEntity);
this.lastKnownEntity = entity;
if (entity == null || entity instanceof EntityDataPlayer) {
playerEntity = (EntityDataPlayer) entity;
} else {
LogManager.getLogger().warn(
"Entity ID of local player is {}, but the entity is not an EntityDataPlayer: {}",
EntityData.formatEntityId(getEntityId()),
entity
);
playerEntity = null;
}
return entity;
if (playerEntity != lastKnownEntity) {
ClientEvent event = new NewLocalEntityEvent.Immutable(getClient(), playerEntity, lastKnownEntity);
this.lastKnownEntity = playerEntity;
getClient().postEvent(event);
}
return playerEntity;
}
public Selection getSelection() {

View File

@ -0,0 +1,113 @@
/*
* 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 glm.vec._3.Vec3;
import glm.vec._4.Vec4;
import ru.windcorp.jputil.functions.FloatSupplier;
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.gui.Component;
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.ShapeRenderHelper;
public class Bar extends Component {
private static final int THICKNESS = 5;
private final boolean isVertical;
private final FloatSupplier value;
private final FloatSupplier maxValue;
private final Vec4 color;
private final Vec4 backgroundColor;
private static Renderable unitSquare = null;
public Bar(String name, boolean isVertical, Vec4 color, FloatSupplier value, FloatSupplier maxValue) {
super(name);
this.isVertical = isVertical;
this.value = value;
this.maxValue = maxValue;
this.color = color;
this.backgroundColor = Colors.mix(color, Colors.WHITE, 0.75f, null);
if (unitSquare == null) {
unitSquare = new Shape(
Usage.STATIC,
FlatRenderProgram.getDefault(),
ShapeParts.createRectangle(
FlatRenderProgram.getDefault(),
null,
Colors.WHITE,
new Vec3(0, 0, 0),
new Vec3(1, 0, 0),
new Vec3(0, 1, 0),
false
)
);
}
setPreferredSize(THICKNESS, THICKNESS);
}
@Override
protected void assembleSelf(RenderTarget target) {
target.addCustomRenderer(this::renderSelf);
}
private void renderSelf(ShapeRenderHelper renderer) {
renderer.pushTransform()
.translate(getX(), getY(), 0)
.scale(getWidth(), getHeight(), 1);
float length = value.getAsFloat() / maxValue.getAsFloat();
if (length < 0) {
length = 0;
} else if (length > 1) {
length = 1;
}
// TODO why is the order reverse????
renderRectangle(renderer, color, length);
renderRectangle(renderer, backgroundColor, 1);
renderer.popTransform();
}
private void renderRectangle(ShapeRenderHelper renderer, Vec4 color, float length) {
renderer.pushColorMultiplier().mul(color);
if (length != 1) {
renderer.pushTransform().scale(isVertical ? 1 : length, isVertical ? length : 1, 1);
}
unitSquare.render(renderer);
if (length != 1) {
renderer.popTransform();
}
renderer.popColorMultiplier();
}
}

View File

@ -0,0 +1,151 @@
/*
* 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 glm.vec._2.i.Vec2i;
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.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.inventory.ItemContainerHand;
public class CursorHUD extends Component {
private class CursorBoundSlot {
private final SlotComponent component;
private final Renderable renderable;
/**
* 0 is not selected, 1 is selected
*/
private final ExponentAnimation selection = new ExponentAnimation(10, 0);
private final double angle;
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 = CursorHUD.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();
}
}
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 CursorHUD(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) {
target.addCustomRenderer(renderer -> {
renderer.pushTransform().translate(
(float) InputTracker.getCursorX(),
(float) InputTracker.getCursorY(),
0
);
for (CursorBoundSlot slot : slots) {
slot.render(renderer);
}
renderer.popTransform();
});
}
}

View File

@ -0,0 +1,125 @@
/*
* 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 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;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponent;
import ru.windcorp.progressia.client.world.item.inventory.InventoryRender;
import ru.windcorp.progressia.client.world.item.inventory.InventoryRenderRegistry;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryClosingEvent;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryOpenedEvent;
public class HUDManager implements HUDWorkspace {
private final Client client;
private final LayerHUD layer;
public HUDManager(Client client) {
this.client = client;
this.layer = new LayerHUD(this);
client.subscribe(new Object() {
@Subscribe
public void onLocalEntityChanged(NewLocalEntityEvent e) {
if (e.getNewEntity() != null) {
e.getNewEntity().subscribe(HUDManager.this);
}
if (e.getPreviousEntity() != null) {
e.getPreviousEntity().unsubscribe(HUDManager.this);
}
}
});
}
public void install() {
GUI.addTopLayer(layer);
}
public void remove() {
GUI.removeLayer(layer);
}
@Override
public void openInventory(InventoryComponent component) {
InventoryWindow window = new InventoryWindow("Window", component, this);
layer.getWindowManager().addWindow(window);
}
public void closeEverything() {
System.err.println("closeEverything NYI");
}
public boolean isHidden() {
return layer.isHidden();
}
public void setHidden(boolean hide) {
layer.setHidden(hide);
}
public boolean isInventoryShown() {
return layer.isInventoryShown();
}
public void setInventoryShown(boolean showInventory) {
layer.setInventoryShown(showInventory);
}
@Override
public Client getClient() {
return client;
}
@Subscribe
private void onInventoryOpened(InventoryOpenedEvent event) {
Inventory inventory = event.getInventory();
InventoryRender render = InventoryRenderRegistry.getInstance().get(inventory.getId());
if (render == null) {
throw CrashReports.report(null, "InventoryRender not found for ID %s", inventory.getId());
}
try {
InventoryComponent component = render.createComponent(inventory, this);
openInventory(component);
} catch (Exception e) {
throw CrashReports.report(e, "Could not open inventory %s", inventory.getId());
}
}
@Subscribe
private void onInventoryClosing(InventoryClosingEvent event) {
Inventory inventory = event.getInventory();
for (Component component : layer.getWindowManager().getChildren()) {
if (component instanceof InventoryWindow) {
InventoryWindow window = (InventoryWindow) component;
if (window.getContent().getInventory() == inventory) {
layer.getWindowManager().closeWindow(window);
}
}
}
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.io.IOException;
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;
import ru.windcorp.progressia.common.util.crash.CrashReports;
public class HUDTextures {
private static final AtlasGroup HUD_ATLAS_GROUP = new AtlasGroup("HUD", 1 << 12);
private static ItemAmountTypeface itemAmountTypeface = null;
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;
}
public static ItemAmountTypeface getItemAmountTypeface() {
return itemAmountTypeface;
}
public static void loadItemAmountTypeface() {
try {
itemAmountTypeface = new ItemAmountTypeface();
} catch (IOException e) {
throw CrashReports.report(e, "Could not load item amount typeface");
}
}
}

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.client.graphics.world.hud;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.graphics.world.LocalPlayer;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponent;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerHand;
public interface HUDWorkspace {
Client getClient();
default LocalPlayer getPlayer() {
return getClient().getLocalPlayer();
}
default EntityDataPlayer getPlayerEntity() {
return getClient().getLocalPlayer().getEntity();
}
default ItemContainerHand getHand() {
return getClient().getLocalPlayer().getEntity().getSelectedHand();
}
void openInventory(InventoryComponent component);
}

View File

@ -0,0 +1,134 @@
/*
* 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.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;
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 = manager.getHand() == 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 HUDManager manager;
public HandsHUD(String name, HUDManager manager) {
super(name);
this.manager = manager;
EntityDataPlayer entity = manager.getPlayerEntity();
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 manager.isInventoryShown();
}
}

View File

@ -0,0 +1,181 @@
/*
* 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 glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.gui.Button;
import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.input.WheelScrollEvent;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.world.item.ItemDataContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemSlot;
import ru.windcorp.progressia.common.world.item.inventory.Items;
public class InteractiveSlotComponent extends Button {
private static final double MIN_PICK_ALL_DELAY = Units.get("0.5 s");
private double lastMainAction = Double.NEGATIVE_INFINITY;
private final SlotComponent slotComponent;
private final HUDWorkspace workspace;
public InteractiveSlotComponent(String name, ItemContainer container, int index, HUDWorkspace workspace) {
this(name, new SlotComponent(name, container, index), workspace);
}
public InteractiveSlotComponent(SlotComponent component, HUDWorkspace workspace) {
this(component.getName() + ".Interactive", component, workspace);
}
public InteractiveSlotComponent(String name, SlotComponent component, HUDWorkspace workspace) {
super(name, (Label) null);
this.slotComponent = component;
this.workspace = workspace;
Vec2i size = slotComponent.getPreferredSize().add(2 * BORDER);
setPreferredSize(size);
addChild(this.slotComponent);
setLayout(new LayoutFill(MARGIN));
addListeners();
}
private void addListeners() {
addAction(button -> onMainAction());
addKeyListener(KeyMatcher.RMB, this::onAltAction);
addInputListener(WheelScrollEvent.class, event -> {
if (event.hasVerticalMovement()) {
onSingleMoveAction(event.isDown());
event.consume();
}
});
}
private void onMainAction() {
ItemSlot handSlot = workspace.getHand().slot();
ItemSlot invSlot = getSlot();
if (invSlot == null) {
return;
}
boolean success = false;
double now = GraphicsInterface.getTime();
if (now - lastMainAction < MIN_PICK_ALL_DELAY) {
lastMainAction = Double.NEGATIVE_INFINITY;
pickAll(handSlot);
success = true;
} else {
lastMainAction = now;
}
if (!success) {
success = Items.pour(handSlot, invSlot) != 0;
}
if (!success) {
success = Items.swap(handSlot, invSlot);
}
if (!success && handSlot.isEmpty()) {
success = Items.pour(invSlot, handSlot) != 0;
}
if (success) {
requestReassembly();
}
}
private void pickAll(ItemSlot handSlot) {
int maxIndex = getSlot().getContainer().getMaxIndex();
for (int index = 0; index < maxIndex; ++index) {
Items.pour(new ItemSlot(getSlot().getContainer(), index), handSlot);
if (handSlot.isEmpty()) {
break;
}
}
}
private void onAltAction(KeyEvent e) {
ItemSlot handSlot = workspace.getHand().slot();
ItemSlot invSlot = getSlot();
if (invSlot == null) {
return;
}
boolean success = false;
if (handSlot.isEmpty()) {
success = tryToOpen(invSlot);
}
if (!success && handSlot.isEmpty()) {
success = Items.pour(invSlot, handSlot, invSlot.getCount() / 2) != 0;
}
if (!success) {
success = Items.pour(handSlot, invSlot, 1) != 0;
}
if (!success) {
success = Items.swap(handSlot, invSlot);
}
if (success) {
requestReassembly();
}
}
private boolean tryToOpen(ItemSlot invSlot) {
if (invSlot.getCount() != 1) {
return false;
}
if (!(invSlot.getItem() instanceof ItemDataContainer)) {
return false;
}
ItemDataContainer item = (ItemDataContainer) invSlot.getItem();
return item.open(workspace.getPlayerEntity()) != null;
}
private void onSingleMoveAction(boolean fromHand) {
ItemSlot handSlot = workspace.getHand().slot();
ItemSlot invSlot = getSlot();
ItemSlot from = fromHand ? handSlot : invSlot;
ItemSlot into = fromHand ? invSlot : handSlot;
if (Items.pour(from, into, 1) != 0) {
requestReassembly();
}
}
public ItemSlot getSlot() {
return slotComponent.getSlot();
}
}

View File

@ -0,0 +1,88 @@
/*
* 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 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.LayoutVertical;
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.EquipmentSlot;
public class InventoryHUD extends Component {
public enum Side {
LEFT("Left", LayoutBorderHorizontal.LEFT),
RIGHT("Right", LayoutBorderHorizontal.RIGHT);
private final String ccName;
private final Object lbhHint;
private Side(String ccName, Object lbhHint) {
this.ccName = ccName;
this.lbhHint = lbhHint;
}
}
public InventoryHUD(String name, HUDWorkspace workspace) {
super(name);
setLayout(new LayoutBorderHorizontal());
EntityDataPlayer entity = workspace.getPlayerEntity();
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 LayoutVertical(15))
);
for (int i = 0; i < entity.getEquipmentCount(); ++i) {
EquipmentSlot slot = entity.getSpecies().getEquipmentSlots().get(i);
SlotComponent display = new SlotComponent(name + "." + slot.getName(), entity.getEquipmentSlot(i), 0)
.setBackground(speciesRender.getEquipmentSlotBackground(slot))
.setScale(2);
InteractiveSlotComponent interactiveDisplay = new InteractiveSlotComponent(
display,
workspace
);
containers.get(speciesRender.getEquipmentSlotSide(slot)).addChild(interactiveDisplay);
}
containers.forEach((side, comp) -> {
addChild(
new Group(name + "." + side.ccName + ".Aligner", new LayoutAlign(0, 1, 0), comp)
.setLayoutHint(side.lbhHint)
);
});
}
}

View File

@ -0,0 +1,154 @@
/*
* 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 com.google.common.eventbus.Subscribe;
import glm.vec._2.Vec2;
import glm.vec._4.Vec4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
import ru.windcorp.progressia.client.graphics.font.Font;
import ru.windcorp.progressia.client.graphics.font.Typeface;
import ru.windcorp.progressia.client.graphics.gui.Button;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.DragManager;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.Label;
import ru.windcorp.progressia.client.graphics.gui.Panel;
import ru.windcorp.progressia.client.graphics.gui.event.DragEvent;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderHorizontal;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
import ru.windcorp.progressia.client.localization.MutableString;
import ru.windcorp.progressia.client.localization.MutableStringConcat;
import ru.windcorp.progressia.client.localization.MutableStringLocalized;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponent;
public class InventoryWindow extends Panel {
private static final String CLOSE_CHAR = "\u2715";
private static final String HANDLE_CHAR = "\u2800";
private static final Vec4 CLOSE_BUTTON_IDLE = Colors.toVector(0xFFBC1515);
private static final Vec4 CLOSE_BUTTON_HOVER = Colors.toVector(0xFFFA6464);
private static final Vec4 CLOSE_BUTTON_PRESSED = Colors.BLACK;
private final InventoryComponent content;
private final HUDWorkspace workspace;
private final Vec2 relativePosition = new Vec2(Float.NaN);
public InventoryWindow(String name, InventoryComponent component, HUDWorkspace workspace) {
super(name, new LayoutVertical(15, 15));
this.content = component;
this.workspace = workspace;
Group titleBar = new Group(getName() + ".TitleBar", new LayoutBorderHorizontal());
titleBar.addChild(createLabel(component).setLayoutHint(LayoutBorderHorizontal.CENTER));
titleBar.addChild(createCloseButton(component).setLayoutHint(LayoutBorderHorizontal.RIGHT));
new DragManager().install(titleBar);
titleBar.addListener(new Object() {
@Subscribe
public void onWindowDragged(DragEvent e) {
Vec2 change = new Vec2((float) e.getCurrentChangeX(), (float) e.getCurrentChangeY());
change.div(getParent().getWidth(), getParent().getHeight());
relativePosition.add(change);
requestReassembly();
}
});
addChild(titleBar);
addChild(component);
}
private Label createLabel(InventoryComponent component) {
String translationKey = "Inventory." + component.getInventory().getId() + ".Title";
MutableString titleText = new MutableStringConcat(
HANDLE_CHAR + " ",
new MutableStringLocalized(translationKey)
);
Font titleFont = new Font().deriveBold().withColor(Colors.BLACK).withAlign(Typeface.ALIGN_LEFT);
return new Label(getName() + ".Title", titleFont, titleText);
}
private WindowedHUD getManager() {
Component parent = getParent();
if (parent instanceof WindowedHUD) {
return (WindowedHUD) parent;
}
return null;
}
/**
* @return the relativePosition
*/
public Vec2 getRelativePosition() {
return relativePosition;
}
private Component createCloseButton(InventoryComponent component) {
Button button = new Button(getName() + ".CloseButton", CLOSE_CHAR) {
@Override
protected void assembleSelf(RenderTarget target) {
Vec4 color = CLOSE_BUTTON_IDLE;
if (isPressed()) {
color = CLOSE_BUTTON_PRESSED;
} else if (isHovered()) {
color = CLOSE_BUTTON_HOVER;
}
if (hasLabel()) {
getLabel().setFont(getLabel().getFont().withColor(color));
}
}
};
button.addAction(b -> {
content.getInventory().close(workspace.getPlayerEntity());
WindowedHUD manager = getManager();
if (manager == null) {
return;
}
manager.closeWindow(this);
});
button.setLayout(new LayoutFill());
int height = button.getLabel().getFont().getHeight(button.getLabel().getCurrentText());
button.setPreferredSize(height, height);
return button;
}
public InventoryComponent getContent() {
return content;
}
}

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.client.graphics.world.hud;
import java.io.IOException;
import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram;
import ru.windcorp.progressia.client.graphics.font.SpriteTypeface;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
import ru.windcorp.progressia.client.graphics.texture.ComplexTexture;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.client.graphics.texture.TextureLoader;
import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive;
import ru.windcorp.progressia.client.graphics.texture.TextureSettings;
import ru.windcorp.progressia.common.resource.ResourceManager;
public class ItemAmountTypeface extends SpriteTypeface {
public static final int HEIGHT = 5;
public static final int WIDTH = 3;
private final Texture[] textures = new Texture[10];
public ItemAmountTypeface() throws IOException {
super("ItemAmount", HEIGHT, 1);
ComplexTexture atlas = new ComplexTexture(new TexturePrimitive(
TextureLoader.loadPixels(
ResourceManager.getTextureResource("gui/ItemAmountTypeface"),
new TextureSettings(false)
).getData()
), 30, 5);
for (int i = 0; i <= 9; ++i) {
textures[i] = atlas.get(i * WIDTH, 0, WIDTH, HEIGHT);
}
}
@Override
public Texture getTexture(char c) {
if (!supports(c))
return textures[0];
return textures[c - '0'];
}
@Override
public ShapeRenderProgram getProgram() {
return FlatRenderProgram.getDefault();
}
@Override
public boolean supports(char c) {
return c >= '0' && c <= '9';
}
}

View File

@ -0,0 +1,153 @@
/*
* 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 com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.client.events.NewLocalEntityEvent;
import ru.windcorp.progressia.client.graphics.GUI;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Components;
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
import ru.windcorp.progressia.client.graphics.gui.Group;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.input.InputEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.client.graphics.input.bus.InputBus;
import ru.windcorp.progressia.test.controls.TestPlayerControls;
public class LayerHUD extends GUILayer {
private final HUDManager manager;
private WindowedHUD windowManager = null;
private boolean showInventory = false;
private boolean isHidden = false;
public LayerHUD(HUDManager manager) {
super("LayerHUD", new LayoutFill(15));
this.manager = manager;
setCursorPolicy(CursorPolicy.INDIFFERENT);
manager.getClient().subscribe(this);
getRoot().addKeyListener(new KeyMatcher("Escape"), e -> {
setInventoryShown(!showInventory);
e.consume();
}, InputBus.Option.IGNORE_FOCUS);
getRoot().addKeyListener(new KeyMatcher("E"), e -> {
setInventoryShown(!showInventory);
e.consume();
}, InputBus.Option.IGNORE_FOCUS);
getRoot().addKeyListener(new KeyMatcher("Left Control"), e -> {
TestPlayerControls.getInstance().getInventoryControls().switchHandsWithCtrl(e);
e.consume();
}, InputBus.Option.IGNORE_ACTION, InputBus.Option.IGNORE_FOCUS);
getRoot().addKeyListener(new KeyMatcher("Right Control"), e -> {
TestPlayerControls.getInstance().getInventoryControls().switchHandsWithCtrl(e);
e.consume();
}, InputBus.Option.IGNORE_ACTION, InputBus.Option.IGNORE_FOCUS);
}
@Subscribe
private void onEntityChanged(NewLocalEntityEvent e) {
while (!getRoot().getChildren().isEmpty()) {
getRoot().removeChild(getRoot().getChild(0));
}
if (e.getNewEntity() == null) {
getRoot().requestReassembly();
return;
}
getRoot().addChild(new PermanentHUD(getName() + ".Permanent", manager));
Component inventoryGroup = new Group(getName() + ".InventoryGroup", new LayoutFill());
inventoryGroup.addChild(new InventoryHUD(getName() + ".Equipment", manager));
windowManager = new WindowedHUD(getName() + ".Windows");
inventoryGroup.addChild(windowManager);
inventoryGroup.addChild(new CursorHUD(getName() + ".Cursor", manager.getPlayerEntity()));
getRoot().addChild(Components.hide(inventoryGroup, () -> !showInventory));
getRoot().requestReassembly();
}
public WindowedHUD getWindowManager() {
return windowManager;
}
public boolean isInventoryShown() {
return showInventory;
}
public void setInventoryShown(boolean showInventory) {
this.showInventory = showInventory;
updateCursorPolicy();
}
public boolean isHidden() {
return isHidden;
}
public void setHidden(boolean isHidden) {
this.isHidden = isHidden;
updateCursorPolicy();
}
private void updateCursorPolicy() {
if (showInventory && !isHidden) {
setCursorPolicy(CursorPolicy.REQUIRE);
} else {
setCursorPolicy(CursorPolicy.INDIFFERENT);
}
GUI.updateLayer(this);
}
@Override
public void handleInput(InputEvent event) {
if (isHidden) {
return;
}
super.handleInput(event);
if (getCursorPolicy() == CursorPolicy.REQUIRE) {
event.consume();
}
}
@Override
protected void doRender() {
if (isHidden) {
return;
}
super.doRender();
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutBorderVertical;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class PermanentHUD extends Component {
public PermanentHUD(String name, HUDManager manager) {
super(name);
setLayout(new LayoutBorderVertical());
EntityDataPlayer entity = manager.getPlayerEntity();
if (entity == null) {
throw new IllegalStateException("Player " + manager.getPlayer() + " does not have an associated entity");
}
addChild(new HandsHUD(name + ".Hands", manager).setLayoutHint(LayoutBorderVertical.UP));
}
}

View File

@ -0,0 +1,217 @@
/*
* 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.function.BooleanSupplier;
import glm.mat._4.Mat4;
import ru.windcorp.progressia.client.graphics.Colors;
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
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.ShapeRenderHelper;
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;
import ru.windcorp.progressia.common.world.item.ItemData;
import ru.windcorp.progressia.common.world.item.ItemDataContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemSlot;
public class SlotComponent extends Component {
static final float TEXTURE_SIZE = 24;
private static boolean drawVirtualSlots;
static {
String key = SlotComponent.class.getName() + ".drawVirtualSlots";
drawVirtualSlots = Boolean.parseBoolean(System.getProperty(key, "true"));
}
private static Renderable containerOpenDecoration = null;
private static Renderable containerOpenableDecoration = null;
private final ItemSlot slot;
private float scale = 2;
private ItemRenderable itemRenderer = null;
private int amountDisplayInt = 0;
private String amountDisplayString = "";
private Renderable background = null;
private BooleanSupplier backgroundCondition = null;
public SlotComponent(String name, ItemContainer container, int index) {
super(name);
this.slot = new ItemSlot(container, index);
setScale(2);
Font sizeFont = new Font(HUDTextures.getItemAmountTypeface()).deriveOutlined().withScale(2);
addChild(new DynamicLabel(getName() + ".Size", sizeFont, () -> amountDisplayString, getPreferredSize().x));
setLayout(new LayoutAlign(0, 0, 0));
if (containerOpenDecoration == null) {
containerOpenDecoration = new PgmBuilder(
FlatRenderProgram.getDefault(),
HUDTextures.getHUDTexture("DecorationContainerOpen")
).setSize(TEXTURE_SIZE + 2).setOrigin(-1, -1, 0).create();
}
if (containerOpenableDecoration == null) {
containerOpenableDecoration = new PgmBuilder(
FlatRenderProgram.getDefault(),
HUDTextures.getHUDTexture("DecorationContainerOpenable")
).setSize(TEXTURE_SIZE + 2).setOrigin(-1, -1, 0).create();
}
}
/**
* @return the container
*/
public ItemContainer getSlotContainer() {
return slot.getContainer();
}
/**
* @return the index
*/
public int getSlotIndex() {
return slot.getIndex();
}
public ItemSlot getSlot() {
return slot;
}
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, BooleanSupplier when) {
background = new PgmBuilder(FlatRenderProgram.getDefault(), texture).setSize(TEXTURE_SIZE).create();
setBackgroundDisplayCondition(when);
return this;
}
public SlotComponent setBackground(Texture texture) {
return setBackground(texture, null);
}
public SlotComponent setBackgroundDisplayCondition(BooleanSupplier backgroundCondition) {
this.backgroundCondition = backgroundCondition;
return this;
}
@Override
protected void assembleSelf(RenderTarget target) {
super.assembleSelf(target);
assembleItem(target);
}
private void updateItemRenderer() {
ItemData contents;
contents = slot.getItem();
if (contents == null) {
itemRenderer = null;
amountDisplayInt = 0;
amountDisplayString = "";
} else {
if (itemRenderer == null || itemRenderer.getData() != contents) {
itemRenderer = ItemRenderRegistry.getInstance().get(contents.getId()).createRenderable(contents);
}
int newAmount = slot.getCount();
if (newAmount != amountDisplayInt) {
amountDisplayInt = newAmount;
amountDisplayString = newAmount == 1 ? "" : Integer.toString(newAmount);
}
}
}
private void assembleItem(RenderTarget target) {
target.pushTransform(new Mat4().translate(getX(), getY(), 0).scale(scale, scale, 1));
target.addCustomRenderer(renderer -> {
updateItemRenderer();
if (drawVirtualSlots && getSlot() == null) {
renderer.pushColorMultiplier().set(Colors.DEBUG_GREEN);
containerOpenDecoration.render(renderer);
renderer.popColorMultiplier();
return;
}
if (itemRenderer != null) {
itemRenderer.render(renderer);
renderDecorations(renderer);
} else if (background != null) {
if (backgroundCondition == null || backgroundCondition.getAsBoolean()) {
background.render(renderer);
}
}
});
target.popTransform();
}
private void renderDecorations(ShapeRenderHelper renderer) {
ItemData contents = getSlot().getItem();
if (contents instanceof ItemDataContainer) {
ItemDataContainer asContainer = (ItemDataContainer) contents;
if (asContainer.isOpen()) {
renderer.pushColorMultiplier().mul(Colors.BLUE);
containerOpenDecoration.render(renderer);
renderer.popColorMultiplier();
} else {
double dx = InputTracker.getCursorX() - (getX() + getWidth() / 2);
double dy = InputTracker.getCursorY() - (getY() + getHeight() / 2);
double distanceToCursorSquared = dx*dx + dy*dy;
final double maxDistanceSquared = (scale * TEXTURE_SIZE * 4) * (scale * TEXTURE_SIZE * 4);
float opacity = (float) (1 - distanceToCursorSquared / maxDistanceSquared);
if (opacity > 0) {
renderer.pushColorMultiplier().mul(Colors.BLUE.x, Colors.BLUE.y, Colors.BLUE.z, opacity);
containerOpenableDecoration.render(renderer);
renderer.popColorMultiplier();
}
}
}
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.gui.Component;
public class WindowedHUD extends Component {
public WindowedHUD(String name) {
super(name);
setLayout(new WindowedLayout());
}
public void addWindow(InventoryWindow window) {
addChild(window);
window.setSize(window.getPreferredSize());
centerWindow(window);
}
public void closeWindow(InventoryWindow window) {
removeChild(window);
requestReassembly();
}
private void centerWindow(InventoryWindow window) {
window.setPosition(
(getWidth() - window.getWidth()) / 2,
(getHeight() - window.getHeight()) / 2
);
}
}

View File

@ -0,0 +1,64 @@
/*
* 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 glm.Glm;
import glm.vec._2.Vec2;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.gui.Layout;
public class WindowedLayout implements Layout {
@Override
public void layout(Component c) {
for (Component component : c.getChildren()) {
InventoryWindow window = (InventoryWindow) component;
Vec2i size = new Vec2i(c.getWidth(), c.getHeight());
Glm.min(window.getPreferredSize(), size, size);
window.setSize(size);
Vec2 relPos = window.getRelativePosition();
if (Float.isNaN(relPos.x) || Float.isNaN(relPos.y)) {
relPos.x = 0.5f;
relPos.y = 2 / 3.0f;
} else {
float minPosX = 0;
float minPosY = window.getHeight() / (float) c.getHeight();
float maxPosX = 1;
float maxPosY = 1;
relPos.x = Glm.clamp(relPos.x, minPosX, maxPosX);
relPos.y = Glm.clamp(relPos.y, minPosY, maxPosY);
}
window.setPosition(
(int) (relPos.x * c.getWidth() - window.getWidth() / 2.0f),
(int) (relPos.y * c.getHeight() - window.getHeight())
);
}
}
@Override
public Vec2i calculatePreferredSize(Component c) {
throw new UnsupportedOperationException();
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
public abstract class UpdatingRenderable implements Renderable {
private long stateComputedForFrame = -1;
/**
* Updates the state of this model. This method is invoked exactly once per
* renderable per frame before this model is queried for the first time.
*/
protected void update() {
// Do nothing
}
protected void updateIfNecessary() {
if (stateComputedForFrame != GraphicsInterface.getFramesRendered()) {
update();
stateComputedForFrame = GraphicsInterface.getFramesRendered();
}
}
@Override
public final void render(ShapeRenderHelper renderer) {
updateIfNecessary();
doRender(renderer);
}
protected abstract void doRender(ShapeRenderHelper 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

@ -19,45 +19,18 @@
package ru.windcorp.progressia.client.world.entity;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
import ru.windcorp.progressia.client.graphics.model.Renderable;
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
import ru.windcorp.progressia.client.world.UpdatingRenderable;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.generic.EntityGeneric;
public abstract class EntityRenderable implements Renderable, EntityGeneric {
public abstract class EntityRenderable extends UpdatingRenderable implements EntityGeneric {
private final EntityData data;
private long stateComputedForFrame = -1;
public EntityRenderable(EntityData data) {
this.data = data;
}
/**
* Updates the state of this model. This method is invoked exactly once per
* renderable per frame before this entity is queried for the first time.
*/
protected void update() {
// Do nothing
}
private void updateIfNecessary() {
if (stateComputedForFrame != GraphicsInterface.getFramesRendered()) {
update();
stateComputedForFrame = GraphicsInterface.getFramesRendered();
}
}
@Override
public final void render(ShapeRenderHelper renderer) {
updateIfNecessary();
doRender(renderer);
}
protected abstract void doRender(ShapeRenderHelper renderer);
public EntityData getData() {
return data;
}

View File

@ -0,0 +1,52 @@
/*
* 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.InventoryHUD;
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 abstract InventoryHUD.Side getEquipmentSlotSide(EquipmentSlot equipmentSlot);
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

@ -0,0 +1,31 @@
/*
* 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.item;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.item.ItemData;
public abstract class ItemRender extends Namespaced {
public ItemRender(String id) {
super(id);
}
public abstract ItemRenderable createRenderable(ItemData data);
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item;
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;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
public class ItemRenderRegistry extends NamespacedInstanceRegistry<ItemRender> {
private static final ItemRenderRegistry INSTANCE = new ItemRenderRegistry();
private static final AtlasGroup ITEMS_ATLAS_GROUP = new AtlasGroup("Items", 1 << 12);
public static ItemRenderRegistry getInstance() {
return INSTANCE;
}
public static Texture getItemTexture(String name) {
return new SimpleTexture(
Atlases.getSprite(
ResourceManager.getTextureResource("items/" + name),
ITEMS_ATLAS_GROUP
)
);
}
public static AtlasGroup getItemsAtlasGroup() {
return ITEMS_ATLAS_GROUP;
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.client.world.item;
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.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.ShapeRenderHelper;
import ru.windcorp.progressia.client.graphics.texture.Texture;
import ru.windcorp.progressia.common.world.item.ItemData;
public class ItemRenderSimple extends ItemRender {
private Texture texture;
private Renderable renderable;
public ItemRenderSimple(String id, Texture texture) {
super(id);
this.texture = texture;
this.renderable = 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
)
);
}
public Texture getTexture() {
return texture;
}
@Override
public ItemRenderable createRenderable(ItemData data) {
return new ItemRenderable(data) {
@Override
protected void doRender(ShapeRenderHelper renderer) {
renderable.render(renderer);
}
};
}
}

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.item;
import ru.windcorp.progressia.client.world.UpdatingRenderable;
import ru.windcorp.progressia.common.world.item.ItemData;
public abstract class ItemRenderable extends UpdatingRenderable {
private final ItemData data;
public ItemRenderable(ItemData data) {
this.data = data;
}
public ItemData getData() {
return data;
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.item.inventory;
import java.util.Collection;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.client.graphics.world.hud.InteractiveSlotComponent;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
public abstract class ContainerComponent extends Component {
public ContainerComponent(String name) {
super(name);
}
public abstract ItemContainer getContainer();
public abstract Collection<InteractiveSlotComponent> getSlots();
}

View File

@ -0,0 +1,137 @@
/*
* 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.item.inventory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.eventbus.Subscribe;
import glm.vec._2.i.Vec2i;
import ru.windcorp.progressia.client.graphics.Colors;
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.gui.layout.LayoutGrid;
import ru.windcorp.progressia.client.graphics.world.hud.Bar;
import ru.windcorp.progressia.client.graphics.world.hud.HUDWorkspace;
import ru.windcorp.progressia.client.graphics.world.hud.InteractiveSlotComponent;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.event.ItemSlotChangedEvent;
public class ContainerComponentSimple extends ContainerComponent {
private final Group slots = new Group("Inventory.Slots", new LayoutGrid(0, 15));
private final List<InteractiveSlotComponent> slotCollection = new ArrayList<>();
private final ItemContainer container;
private final HUDWorkspace workspace;
private int tmp__getSlotsPerRow() {
return 6;
}
public ContainerComponentSimple(ItemContainer container, HUDWorkspace workspace) {
super("Inventory");
this.container = container;
this.workspace = workspace;
if (container.getInventory() != null) {
container.getInventory().subscribe(this);
}
setLayout(new LayoutBorderHorizontal(15));
Bar massBar = new Bar(
"MassBar",
true,
Colors.toVector(0xFF44AAAA),
container::getMass,
container::getMassLimit
);
Bar volumeBar = new Bar(
"VolumeBar",
false,
Colors.toVector(0xFFAA4444),
container::getVolume,
container::getVolumeLimit
);
Component slotsAndVolumeBar = new Group(
"SlotsAndVolumeBar",
new LayoutBorderVertical(15),
slots.setLayoutHint(LayoutBorderVertical.CENTER),
volumeBar.setLayoutHint(LayoutBorderVertical.UP)
);
addChild(slotsAndVolumeBar.setLayoutHint(LayoutBorderHorizontal.CENTER));
addChild(massBar.setLayoutHint(LayoutBorderHorizontal.LEFT));
onSlotChanged(null);
}
private void addSlot(int index) {
final int maxX = tmp__getSlotsPerRow();
InteractiveSlotComponent component = new InteractiveSlotComponent("Inventory.Slot." + index, container, index, workspace);
Vec2i pos = new Vec2i(index % maxX, index / maxX);
slots.addChild(component.setLayoutHint(pos));
slotCollection.add(component);
}
private void removeSlot(int index) {
slots.removeChild(slotCollection.remove(index));
}
@Override
public ItemContainer getContainer() {
return container;
}
@Override
public Collection<InteractiveSlotComponent> getSlots() {
return slotCollection;
}
@Subscribe
private void onSlotChanged(ItemSlotChangedEvent e) {
if (e != null && e.getContainer() != container) {
return;
}
int wantSlots = container.getLastFilledSlot();
int slotsPerRow = tmp__getSlotsPerRow();
wantSlots = (wantSlots / slotsPerRow + 2) * slotsPerRow;
if (wantSlots < slotsPerRow) {
wantSlots = slotsPerRow;
}
while (wantSlots > slotCollection.size()) {
addSlot(slotCollection.size());
}
while (wantSlots < slotCollection.size()) {
removeSlot(slotCollection.size() - 1);
}
}
}

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.item.inventory;
import java.util.Collection;
import ru.windcorp.progressia.client.graphics.gui.Component;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
public abstract class InventoryComponent extends Component {
public InventoryComponent(String name) {
super(name);
}
public abstract Inventory getInventory();
public abstract Collection<? extends ContainerComponent> getContainers();
}

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.world.item.inventory;
import java.util.ArrayList;
import java.util.Collection;
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill;
import ru.windcorp.progressia.client.graphics.world.hud.HUDWorkspace;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
public class InventoryComponentSimple extends InventoryComponent {
private final Inventory inventory;
private final Collection<ContainerComponentSimple> containers = new ArrayList<>();
public InventoryComponentSimple(String name, Inventory inventory, HUDWorkspace workspace) {
super(name);
setLayout(new LayoutFill());
this.inventory = inventory;
for (ItemContainer container : inventory.getContainers()) {
ContainerComponentSimple component = new ContainerComponentSimple(container, workspace);
containers.add(component);
addChild(component);
}
}
@Override
public Inventory getInventory() {
return inventory;
}
@Override
public Collection<? extends ContainerComponent> getContainers() {
return containers;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.item.inventory;
import ru.windcorp.progressia.client.graphics.world.hud.HUDWorkspace;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
public abstract class InventoryRender extends Namespaced {
public InventoryRender(String id) {
super(id);
}
public abstract InventoryComponent createComponent(Inventory inventory, HUDWorkspace workspace);
}

View File

@ -0,0 +1,30 @@
/*
* 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.item.inventory;
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
public class InventoryRenderRegistry extends NamespacedInstanceRegistry<InventoryRender> {
private static final InventoryRenderRegistry INSTANCE = new InventoryRenderRegistry();
public static InventoryRenderRegistry getInstance() {
return INSTANCE;
}
}

View File

@ -65,6 +65,8 @@ public class Units {
// Volume
public static final float CUBIC_CENTIMETERS = CENTIMETERS * CENTIMETERS * CENTIMETERS;
@RegisteredUnit("L")
public static final float LITERS = (10 * CENTIMETERS) * (10 * CENTIMETERS) * (10 * CENTIMETERS);
public static final float CUBIC_METERS = METERS * METERS * METERS;
public static final float CUBIC_MILLIMETERS = MILLIMETERS * MILLIMETERS * MILLIMETERS;
public static final float CUBIC_KILOMETERS = KILOMETERS * KILOMETERS * KILOMETERS;

View File

@ -0,0 +1,87 @@
/*
* 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.state;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class BooleanStateField extends StateField {
public BooleanStateField(
String id,
boolean isLocal,
int index
) {
super(id, isLocal, index);
}
public boolean get(StatefulObject object) {
return object.getStorage().getBoolean(getIndex());
}
public void setNow(StatefulObject object, boolean value) {
object.getStorage().setBoolean(getIndex(), value);
}
public void set(StateChanger changer, boolean value) {
changer.setBoolean(this, value);
}
@Override
public void read(
StatefulObject object,
DataInput input,
IOContext context
)
throws IOException {
object.getStorage().setBoolean(getIndex(), input.readBoolean());
}
@Override
public void write(
StatefulObject object,
DataOutput output,
IOContext context
)
throws IOException {
output.writeBoolean(object.getStorage().getBoolean(getIndex()));
}
@Override
public void copy(StatefulObject from, StatefulObject to) {
setNow(to, get(from));
}
@Override
public int computeHashCode(StatefulObject object) {
return Boolean.hashCode(get(object));
}
@Override
public boolean areEqual(StatefulObject a, StatefulObject b) {
return get(a) == get(b);
}
@Override
public void setDefault(StateStorage storage) {
storage.setBoolean(getIndex(), false);
}
}

View File

@ -22,10 +22,13 @@ import gnu.trove.map.TIntIntMap;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntIntHashMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
public class HashMapStateStorage extends StateStorage {
private final TIntIntMap ints = new TIntIntHashMap();
private final TIntSet booleans = new TIntHashSet();
private final TIntObjectMap<Object> objects = new TIntObjectHashMap<>();
@Override
@ -38,6 +41,20 @@ public class HashMapStateStorage extends StateStorage {
ints.put(index, value);
}
@Override
public boolean getBoolean(int index) {
return booleans.contains(index);
}
@Override
public void setBoolean(int index, boolean value) {
if (value) {
booleans.add(index);
} else {
booleans.remove(index);
}
}
@Override
public Object getObject(int index) {
return objects.get(index);

View File

@ -85,6 +85,21 @@ public class InspectingStatefulObjectLayout
}
private class Boolean implements StateFieldBuilder.Boolean {
@Override
public BooleanStateField build() {
return registerField(
new BooleanStateField(
id,
isLocal,
fieldIndexCounters.getBooleansThenIncrement()
)
);
}
}
private class Obj<T> implements StateFieldBuilder.Obj<T> {
private final ObjectCodec<T> codec;
@ -123,6 +138,11 @@ public class InspectingStatefulObjectLayout
return new Int();
}
@Override
public Boolean ofBoolean() {
return new Boolean();
}
@Override
public <T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue) {
return new Obj<T>(codec, defaultValue);

View File

@ -21,11 +21,13 @@ package ru.windcorp.progressia.common.state;
public class OptimizedStateStorage extends StateStorage {
private final int[] ints;
private final boolean[] booleans;
private final Object[] objects;
public OptimizedStateStorage(PrimitiveCounters sizes) {
this.ints = new int[sizes.getInts()];
this.objects = new Object[sizes.getObjects()];
this.ints = sizes.getInts() == 0 ? null : new int[sizes.getInts()];
this.booleans = sizes.getBooleans() == 0 ? null : new boolean[sizes.getBooleans()];
this.objects = sizes.getObjects() == 0 ? null : new Object[sizes.getObjects()];
}
@Override
@ -38,6 +40,16 @@ public class OptimizedStateStorage extends StateStorage {
ints[index] = value;
}
@Override
public boolean getBoolean(int index) {
return booleans[index];
}
@Override
public void setBoolean(int index, boolean value) {
booleans[index] = value;
}
@Override
public Object getObject(int index) {
return objects[index];

View File

@ -75,6 +75,16 @@ public class OptimizedStatefulObjectLayout
};
}
@Override
public Boolean ofBoolean() {
return new Boolean() {
@Override
public BooleanStateField build() {
return (BooleanStateField) result;
}
};
}
@Override
public <T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue) {
return new Obj<T>() {

View File

@ -21,6 +21,7 @@ package ru.windcorp.progressia.common.state;
class PrimitiveCounters {
private int ints = 0;
private int booleans = 0;
private int objects = 0;
public PrimitiveCounters() {
@ -28,6 +29,7 @@ class PrimitiveCounters {
public PrimitiveCounters(PrimitiveCounters copyFrom) {
this.ints = copyFrom.ints;
this.booleans = copyFrom.booleans;
this.objects = copyFrom.objects;
}
@ -39,6 +41,14 @@ class PrimitiveCounters {
return this.ints++;
}
public int getBooleans() {
return booleans;
}
public int getBooleansThenIncrement() {
return this.booleans++;
}
public int getObjects() {
return objects;
}

View File

@ -21,6 +21,7 @@ package ru.windcorp.progressia.common.state;
public interface StateChanger {
void setInt(IntStateField field, int value);
void setBoolean(BooleanStateField field, boolean value);
<T> void setObject(ObjectStateField<T> field, T value);
}

View File

@ -29,12 +29,18 @@ public interface StateFieldBuilder {
IntStateField build();
}
public static interface Boolean {
BooleanStateField build();
}
public static interface Obj<T> {
ObjectStateField<T> build();
}
Int ofInt();
Boolean ofBoolean();
<T> Obj<T> of(ObjectCodec<T> codec, Supplier<T> defaultValue);
default <T> Obj<T> of(Class<T> clazz, Supplier<T> defaultValue) {

View File

@ -24,6 +24,10 @@ public abstract class StateStorage {
public abstract void setInt(int index, int value);
public abstract boolean getBoolean(int index);
public abstract void setBoolean(int index, boolean value);
public abstract Object getObject(int index);
public abstract void setObject(int index, Object object);

View File

@ -42,6 +42,16 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
*/
T build();
}
@FunctionalInterface
public static interface IdFactory<T> {
/**
* Initializes a new, independent instance of the stateful object.
*
* @return the created object
*/
T build(String id);
}
protected static class Type<T> extends Namespaced {
@ -110,5 +120,9 @@ public class StatefulObjectRegistry<T extends StatefulObject> {
public void register(String id, Factory<T> factory) {
registry.register(new Type<>(id, factory));
}
public void register(String id, IdFactory<T> factory) {
register(id, () -> factory.build(id));
}
}

View File

@ -0,0 +1,102 @@
/*
* 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 com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ru.windcorp.progressia.common.state.IntStateField;
import ru.windcorp.progressia.common.state.ObjectStateField;
import ru.windcorp.progressia.common.util.crash.ReportingEventBus;
import ru.windcorp.progressia.common.world.item.inventory.InventoryUser;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerEquipment;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerHand;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryClosingEvent;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryOpenedEvent;
public class EntityDataPlayer extends EntityData implements InventoryUser {
private final ObjectStateField<SpeciesDatalet> speciesDatalet = field("Core:SpeciesDatalet").setShared()
.of(SpeciesDataRegistry.getInstance().getCodec()).build();
private final IntStateField selectedHand = field("Core:SelectedHand").setShared().ofInt().build();
private final EventBus eventBus = ReportingEventBus.create("EntityDataPlayer");
public EntityDataPlayer(String id, SpeciesData species) {
super(id);
setSpecies(species);
}
private void setSpecies(SpeciesData species) {
speciesDatalet.setNow(this, species.createDatalet());
setCollisionModel(species.getCollisionModel());
}
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 ItemContainerEquipment getEquipmentSlot(int index) {
return speciesDatalet.get(this).getEquipment()[index];
}
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());
}
@Subscribe
private void onInventoryOpened(InventoryOpenedEvent event) {
eventBus.post(event);
}
@Subscribe
private void onInventoryClosed(InventoryClosingEvent event) {
eventBus.post(event);
}
public void subscribe(Object listener) {
eventBus.register(listener);
}
public void unsubscribe(Object listener) {
eventBus.unregister(listener);
}
}

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.inventory.ItemContainerEquipment;
import ru.windcorp.progressia.common.world.item.inventory.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,24 @@
/*
* 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.generic;
public interface ItemGeneric {
String getId();
}

View File

@ -0,0 +1,48 @@
/*
* 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.state.StatefulObject;
import ru.windcorp.progressia.common.world.generic.ItemGeneric;
/**
* An item identified by its ID and properties, able to reside in a slot.
*/
public abstract class ItemData extends StatefulObject implements ItemGeneric {
public ItemData(String id) {
super(ItemDataRegistry.getInstance(), id);
}
/**
* Computes and returns the mass of a single unit (single item) of this
* item.
*
* @return the mass of this item
*/
public abstract float getMass();
/**
* Computes and returns the volume of a single unit (single item) of this
* item stack.
*
* @return the volume of this item
*/
public abstract float getVolume();
}

View File

@ -0,0 +1,113 @@
/*
* 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.util.Iterator;
import com.google.common.collect.Iterators;
import ru.windcorp.progressia.common.state.ObjectStateField;
import ru.windcorp.progressia.common.world.item.inventory.InventoryOwner;
import ru.windcorp.progressia.common.world.item.inventory.InventorySimple;
import ru.windcorp.progressia.common.world.item.inventory.InventoryUser;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainerMixedSimple;
public class ItemDataContainer extends ItemData implements InventoryOwner, ItemDataWithContainers {
private final ObjectStateField<InventorySimple> inventory = field("Core:Contents").setShared().def(this::createInventory)
.build();
private final float ownMass;
private final float containerMassLimit;
private final float ownVolume;
private final float containerVolumeLimit;
private final boolean containerContributesVolume;
public ItemDataContainer(
String id,
float ownMass,
float containerMassLimit,
float ownVolume,
float containerVolumeLimit,
boolean containerContributesVolume
) {
super(id);
this.ownMass = ownMass;
this.containerMassLimit = containerMassLimit;
this.ownVolume = ownVolume;
this.containerVolumeLimit = containerVolumeLimit;
this.containerContributesVolume = containerContributesVolume;
}
protected InventorySimple createInventory() {
return new InventorySimple(
getId(),
this,
new ItemContainerMixedSimple(getId(), containerMassLimit, containerVolumeLimit)
);
}
public InventorySimple getInventory() {
return inventory.get(this);
}
@Override
public Iterator<? extends ItemContainer> getAllContainers() {
return Iterators.forArray(getInventory().getContainers());
}
public boolean isOpen() {
return !getInventory().getUsers().isEmpty();
}
public boolean canOpen(InventoryUser user) {
return true;
}
public synchronized InventorySimple open(InventoryUser user) {
if (isOpen()) {
return null;
} else if (!canOpen(user)) {
return null;
} else {
getInventory().open(user);
return getInventory();
}
}
public synchronized void close() {
getInventory().closeAll();
}
@Override
public float getMass() {
return ownMass + getInventory().getMass();
}
@Override
public float getVolume() {
if (containerContributesVolume) {
return ownVolume + getInventory().getVolume();
} else {
return ownVolume;
}
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.state.StatefulObjectRegistry;
public class ItemDataRegistry extends StatefulObjectRegistry<ItemData> {
private static final ItemDataRegistry INSTANCE = new ItemDataRegistry();
public static ItemDataRegistry getInstance() {
return INSTANCE;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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;
public class ItemDataSimple extends ItemData {
private final float mass;
private final float volume;
public ItemDataSimple(String id, float mass, float volume) {
super(id);
this.mass = mass;
this.volume = volume;
}
@Override
public float getMass() {
return mass;
}
@Override
public float getVolume() {
return volume;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.util.Iterator;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
public interface ItemDataWithContainers {
Iterator<? extends ItemContainer> getAllContainers();
}

View File

@ -0,0 +1,75 @@
/*
* 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.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemSlot;
/**
* A generalization of mass and volume. Not to be extended by mods.
*/
public enum LinearItemProperty {
MASS,
VOLUME;
public float get(ItemData item) {
switch (this) {
case MASS:
return item.getMass();
case VOLUME:
return item.getVolume();
default:
throw new AssertionError();
}
}
public float get(ItemSlot slot) {
switch (this) {
case MASS:
return slot.getMass();
case VOLUME:
return slot.getVolume();
default:
throw new AssertionError();
}
}
public float get(ItemContainer container) {
switch (this) {
case MASS:
return container.getMass();
case VOLUME:
return container.getVolume();
default:
throw new AssertionError();
}
}
public float getLimit(ItemContainer container) {
switch (this) {
case MASS:
return container.getMassLimit();
case VOLUME:
return container.getVolumeLimit();
default:
throw new AssertionError();
}
}
}

View File

@ -0,0 +1,155 @@
/*
* 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.inventory;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import com.google.common.eventbus.EventBus;
import ru.windcorp.progressia.common.state.Encodable;
import ru.windcorp.progressia.common.state.IOContext;
import ru.windcorp.progressia.common.util.crash.ReportingEventBus;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryClosingEvent;
import ru.windcorp.progressia.common.world.item.inventory.event.InventoryOpenedEvent;
public class Inventory extends Namespaced implements Encodable {
private final InventoryOwner owner;
private final ItemContainer[] containers;
private final List<InventoryUser> users = new ArrayList<>();
private EventBus eventBus = null;
public Inventory(String id, InventoryOwner owner, ItemContainer... containers) {
super(id);
this.owner = owner;
this.containers = containers;
for (ItemContainer container : containers) {
container.setInventory(this);
}
}
public InventoryOwner getOwner() {
return owner;
}
public synchronized void open(InventoryUser user) {
users.add(user);
subscribe(user);
eventBus.post(new InventoryOpenedEvent(this, user));
}
public synchronized void close(InventoryUser user) {
if (eventBus != null) {
eventBus.post(new InventoryClosingEvent(this, user));
}
users.remove(user);
unsubscribe(user);
}
public synchronized void subscribe(Object listener) {
if (eventBus == null) {
eventBus = ReportingEventBus.create("Inventory " + getId());
}
eventBus.register(listener);
}
public synchronized void unsubscribe(Object listener) {
if (eventBus == null) {
return;
}
eventBus.unregister(listener);
}
/**
* @return the eventBus
*/
public EventBus getEventBus() {
return eventBus;
}
public synchronized boolean isUser(InventoryUser user) {
return users.contains(user);
}
public synchronized void forEachUser(Consumer<? super InventoryUser> action) {
users.forEach(action);
}
public Collection<InventoryUser> getUsers() {
return users;
}
public synchronized void closeAll() {
while (!users.isEmpty()) {
close(users.get(0));
}
}
public ItemContainer[] getContainers() {
return containers;
}
public synchronized float getMass() {
float sum = 0;
for (ItemContainer container : containers) {
sum += container.getMass();
}
return sum;
}
public synchronized float getVolume() {
float sum = 0;
for (ItemContainer container : containers) {
sum += container.getVolume();
}
return sum;
}
@Override
public synchronized void read(DataInput input, IOContext context) throws IOException {
for (ItemContainer container : containers) {
container.read(input, context);
}
}
@Override
public synchronized void write(DataOutput output, IOContext context) throws IOException {
for (ItemContainer container : containers) {
container.write(output, context);
}
}
@Override
public synchronized void copy(Encodable destination) {
Inventory inventory = (Inventory) destination;
assert inventory.containers.length == containers.length;
for (int i = 0; i < containers.length; ++i) {
containers[i].copy(inventory.containers[i]);
}
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.inventory;
public interface InventoryOwner {
}

View File

@ -0,0 +1,30 @@
/*
* 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.inventory;
public class InventorySimple extends Inventory {
public InventorySimple(String id, InventoryOwner owner, ItemContainer container) {
super(id, owner, container);
}
public ItemContainer getContainer() {
return getContainers()[0];
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.inventory;
public interface InventoryUser {
default void open(Inventory inventory) {
inventory.open(this);
}
default void close(Inventory inventory) {
inventory.close(this);
}
default boolean isUsing(Inventory inventory) {
return inventory.isUser(this);
}
}

View File

@ -0,0 +1,525 @@
/*
* 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.inventory;
import java.util.Iterator;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import ru.windcorp.progressia.common.state.Encodable;
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.item.ItemData;
import ru.windcorp.progressia.common.world.item.ItemDataWithContainers;
import ru.windcorp.progressia.common.world.item.inventory.event.ItemSlotChangedEvent;
/**
* The base class of item containers. An item container is a set of slots with
* defined limits on mass and volume. Containers are typically grouped into an
* {@link Inventory} by in-game function.
* <p>
* A container contains some number of slots indexed from 0. Users may inspect
* the contents of any slot. Slots may contain an {@link ItemData} and an item
* count. Item count is zero iff the {@link ItemData} is {@code null}, in which
* case the slot is considered empty.
* <p>
* Item containers also implement mass and volume calculation. Both properties
* are determined by the corresponding {@link ItemData} methods, and combine
* linearly:
* {@code mass(2 * stick + 5 * apple) = 2 * mass(stick) + 5 * mass(apple)},
* empty slots do not contribute any mass of volume. Users may query these
* computed values. In addition, a limit may be imposed on both properties.
* Containers will refuse operations which would lead to the violation of these
* limits. However, containers cannot detect excess mass or volume resulting
* from
* a implementation-triggered change of limits.
* <p>
* As a safeguard against memory leaks, all containers must be provided with a
* maximum possible size. Item containers will refuse operations on slots with
* indices exceeding or equal to the maximum possible size.
* <p>
* Implementors will typically find the following subclasses useful:
* <ul>
* <li>{@link ItemContainerMixed} for containers allowing an arbitrary amount of
* slots
* <ul>
* <li>see {@link ItemContainerMixedSimple} for a complete implementation</li>
* </ul>
* </li>
* <li>{@link ItemContainerSingle} for containers providing only one slot</li>
* </ul>
*/
public abstract class ItemContainer extends Namespaced implements Encodable {
private Inventory inventory;
private final int maxPossibleSize;
private final TIntSet subContainersCache = new TIntHashSet();
public ItemContainer(String id, int maxPossibleSize) {
super(id);
this.maxPossibleSize = maxPossibleSize;
}
/**
* @return the inventory
*/
public Inventory getInventory() {
return inventory;
}
/**
* @param inventory the inventory to set
*/
void setInventory(Inventory inventory) {
this.inventory = inventory;
}
/**
* Retrieves the item in the slot at the specified index.
*
* @param index the index of the slot to query
* @return the item or {@code null} if the slot is empty.
*/
public abstract ItemData getItem(int index);
/**
* Retrieves the item count in the slot at the specified index.
*
* @param index the index of the slot to query
* @return the item count or {@code 0} if the slot is empty
*/
public abstract int getCount(int index);
/**
* Returns a slot index strictly greater than all indices containing
* items. Use this index as upper bound when iterating the container's
* items. The slot at this index is empty.
*
* @return a strict upper bound on the indices of filled slots
*/
public abstract int getMaxIndex();
/**
* Computes and returns the slot index of the last non-empty slot in this
* container. This operation is potentially slower than
* {@link #getMaxIndex()}, which is less precise.
*
* @return the index of the last filled slot, or -1 if no such slot exists
*/
public int getLastFilledSlot() {
int index = getMaxIndex();
while (isEmpty(index) && index >= 0) {
index--;
}
return index;
}
/**
* Determines whether items of the specified kind could be inserted into
* this container assuming a slot is available and mass and volume
* requirements of the container are satisfied.
* <p>
* This method should only be a function of the configuration of the
* container and of the item; it must not depend on the contents of the
* container.
*
* @param item the item to check, not null
* @return {@code true} iff the item is allowed in this container
*/
protected boolean canAdd(ItemData item) {
if (item == null) {
return false;
}
if (containsRecursively(item, this)) {
return false;
}
return true;
}
private boolean containsRecursively(ItemData haystack, ItemContainer needle) {
if (!(haystack instanceof ItemDataWithContainers)) {
return false;
}
ItemDataWithContainers haystackWithTastyHay = (ItemDataWithContainers) haystack;
Iterator<? extends ItemContainer> iterator = haystackWithTastyHay.getAllContainers();
while (iterator.hasNext()) {
ItemContainer container = iterator.next();
if (container == needle) {
return true;
}
TIntIterator indexIterator = container.subContainersCache.iterator();
while (indexIterator.hasNext()) {
int subHaystackIndex = indexIterator.next();
ItemData subHaystackOrMaybeNot = container.getItem(subHaystackIndex);
if (containsRecursively(subHaystackOrMaybeNot, needle)) {
return true;
}
}
}
return false;
}
/**
* Determines whether inventory users are allowed to manually remove items
* from this container.
* <p>
* This method should only be a function of the configuration of the
* container; it must not depend on the contents of the container.
*
* @return {@code true} iff items could be removed from this container
*/
protected boolean isRemovingAllowed() {
return true;
}
/**
* Determines whether the items can be added at the specified index. The
* slot must already contain items equal to {@code item} parameter or be
* empty. Neither total mass nor volume of the added items may exceed the
* available mass and volume of the container. Additional restrictions may
* apply.
* <p>
* When index is valid, {@code item == null} and {@code count == 0}, this
* method returns {@code true}. Otherwise, when {@code count <= 0}, this
* method returns {@code false}.
*
* @param index the index of the slot to query
* @param item the item type
* @param count the amount of items to add
* @return {@code true} iff the operation is possible
*/
public boolean canAdd(int index, ItemData item, int count) {
if (index < 0 || index >= maxPossibleSize) {
return false;
}
if (item == null && count == 0) {
return true;
}
if (count < 0) {
return false;
}
ItemData currentItem = getItem(index);
if (currentItem == null) {
// Pass
} else if (currentItem.equals(item)) {
// Pass
} else {
return false;
}
if (!canAdd(item)) {
return false;
}
float addedMass = item.getMass() * count;
if (getMass() + addedMass > getMassLimit()) {
return false;
}
float addedVolume = item.getVolume() * count;
if (getVolume() + addedVolume > getVolumeLimit()) {
return false;
}
return true;
}
/**
* Determines whether the items can be removed from the specified index. The
* slot must already contain items equal to {@code item} parameter. The item
* count must be no lower than {@code count} parameter. Additional
* restrictions may apply.
* <p>
* When index is valid, {@code item == null} and {@code count == 0}, this
* method returns {@code true}. Otherwise, when {@code count <= 0}, this
* method returns {@code false}.
*
* @param index the index of the slot to query
* @param item the item type
* @param count the amount of items to remove
* @return {@code true} iff the operation is possible
*/
public boolean canRemove(int index, ItemData item, int count) {
if (index < 0 || index >= maxPossibleSize) {
return false;
}
if (item == null && count == 0) {
return true;
}
if (count < 0) {
return false;
}
if (!isRemovingAllowed()) {
return false;
}
ItemData currentItem = getItem(index);
if (currentItem == null) {
return false;
} else if (currentItem.equals(item)) {
// Pass
} else {
return false;
}
if (getCount(index) < count) {
return false;
}
return true;
}
/**
* Attempts to add the provided items to the specified slot in the
* container. This method modifies the data structure directly; use an
* appropriate {@link Items} method to add items in a safe and convenient
* way.
* <p>
* A {@link #canAdd(int, ItemData, int)} check is performed. If the check
* fails, the method does not alter the contents of the container and
* returns {@code false}. If the check succeeds, the item type and item
* count of the referenced slot are altered appropriately and {@code true}
* is returned.
* <p>
* When {@code item == null} or {@code count <= 0}, this method returns
* {@code false}.
*
* @param index the index of the slot to alter
* @param item the item type
* @param count the amount of items to add
* @return {@code true} iff the container was changed as the result of this
* operation
*/
protected abstract boolean add(int index, ItemData item, int count);
/**
* Attempts to remove the provided items from the specified slot in the
* container. This method modifies the data structure directly; use an
* appropriate {@link Items} method to remove items in a safe and convenient
* way.
* <p>
* A {@link #canRemove(int, ItemData, int)} check is performed. If the check
* fails, the method does not alter the contents of the container and
* returns {@code false}. If the check succeeds, the item type and item
* count of the referenced slot are altered appropriately and {@code true}
* is returned.
* <p>
* When {@code item == null} or {@code count <= 0}, this method returns
* {@code false}.
*
* @param index the index of the slot to alter
* @param item the item type
* @param count the amount of items to remove
* @return {@code true} iff the container was changed as the result of this
* operation
*/
protected abstract boolean remove(int index, ItemData item, int count);
/**
* Computes and returns the mass limit that the container imposes.
*
* @return the maximum allowed total mass of the container's contents, or
* {@code Float.POSITIVE_INFINITY} to indicate that no upper
* boundary is set
*/
public abstract float getMassLimit();
/**
* Computes and returns the volume limit that the container imposes.
*
* @return the maximum allowed total volume of the container's contents, or
* {@code Float.POSITIVE_INFINITY} to indicate that no upper
* boundary is set
*/
public abstract float getVolumeLimit();
public synchronized float getMass() {
float sum = 0;
for (int i = 0; i < getMaxIndex(); ++i) {
ItemData data = getItem(i);
if (data == null) {
continue;
}
sum += data.getMass() * getCount(i);
}
return sum;
}
public synchronized float getVolume() {
float sum = 0;
for (int i = 0; i < getMaxIndex(); ++i) {
ItemData data = getItem(i);
if (data == null) {
continue;
}
sum += data.getVolume() * getCount(i);
}
return sum;
}
protected void fireSlotChangeEvent(int index) {
if (getItem(index) instanceof ItemDataWithContainers) {
subContainersCache.add(index);
} else {
subContainersCache.remove(index);
}
Inventory inventory = this.inventory;
if (inventory == null || inventory.getEventBus() == null) {
return;
}
inventory.getEventBus().post(new ItemSlotChangedEvent(this, index));
}
/**
* Checks class invariants and throws an {@link IllegalStateException} in
* case of discrepancies.
*/
protected synchronized void checkState() {
int maxIndex = getMaxIndex();
for (int index = 0; index < maxIndex; ++index) {
ItemData item = getItem(index);
int count = getCount(index);
if ((item == null) != (count == 0)) {
if (item == null) {
throw new IllegalStateException("Item is null but count (" + count + ") != 0 in slot " + index);
} else {
throw new IllegalStateException("Item is " + item + " but count is zero in slot " + index);
}
}
if (count < 0) {
throw new IllegalStateException("count is negative: " + count + " in slot " + index);
}
boolean isContainer = item instanceof ItemDataWithContainers;
if (isContainer != subContainersCache.contains(index)) {
if (!isContainer) {
throw new IllegalStateException(
"subContainersCache is invalid: item in slot " + index + " (" + item
+ ") is cached as a container"
);
} else {
throw new IllegalStateException(
"subContainersCache is invalid: item in slot " + index + " (" + item
+ ") is not cached as a container"
);
}
}
if (isContainer) {
if (containsRecursively(item, this)) {
throw new IllegalStateException("Recursion detected in slot " + index);
}
}
}
// Using negation in following checks to trigger errors if any value is
// NaN
// (since all comparisons return false if any operand is NaN)
float mass = getMass();
if (!(mass >= 0)) {
throw new IllegalStateException("Mass is negative: " + mass);
}
float massLimit = getMassLimit();
if (!(mass <= massLimit)) {
throw new IllegalStateException("Mass is greater than mass limit: " + mass + " > " + massLimit);
}
float volume = getVolume();
if (!(volume >= 0)) {
throw new IllegalStateException("Volume is negative: " + volume);
}
float volumeLimit = getVolumeLimit();
if (!(volume <= volumeLimit)) {
throw new IllegalStateException("Volume is greater than volume limit: " + volume + " > " + volumeLimit);
}
}
@FunctionalInterface
public interface SlotConsumer {
void accept(ItemData item, int count);
}
/**
* Invokes the provided action for each slot in this container. The action
* is run for empty slots, in which case call is invoked with parameters
* {@code (null, 0)} for each empty slot. The exact amount of invocations is
* determined by {@link #getMaxIndex()}.
*
* @param action the action to run
* @see #forEachItem(SlotConsumer)
*/
public synchronized void forEachSlot(SlotConsumer action) {
int maxIndex = getMaxIndex();
for (int i = 0; i < maxIndex; ++i) {
action.accept(getItem(i), getCount(i));
}
}
/**
* Invokes the provided action for each non-empty slot in this container.
*
* @param action the action to run
* @see #forEachSlot(SlotConsumer)
*/
public synchronized void forEachItem(SlotConsumer action) {
int maxIndex = getMaxIndex();
for (int i = 0; i < maxIndex; ++i) {
int count = getCount(i);
if (count != 0) {
action.accept(getItem(i), count);
}
}
}
public boolean isEmpty(int index) {
return getCount(index) == 0;
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.inventory;
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);
this.equipmentSlot = equipmentSlot;
}
@Override
public float getMassLimit() {
return EQUIP_MASS_LIMIT;
}
@Override
public float getVolumeLimit() {
return EQUIP_VOLUME_LIMIT;
}
public SpeciesData.EquipmentSlot getEquipmentSlot() {
return equipmentSlot;
}
}

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.common.world.item.inventory;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.world.entity.SpeciesData;
import ru.windcorp.progressia.common.world.entity.SpeciesData.Hand;
public class ItemContainerHand extends ItemContainerSingle {
private static final float HAND_MASS_LIMIT = Units.get("10 kg");
private static final float HAND_VOLUME_LIMIT = Units.get("5 kg");
private final SpeciesData.Hand hand;
public ItemContainerHand(String id, Hand hand) {
super(id);
this.hand = hand;
}
@Override
public float getMassLimit() {
return HAND_MASS_LIMIT;
}
@Override
public float getVolumeLimit() {
return HAND_VOLUME_LIMIT;
}
public SpeciesData.Hand getHand() {
return hand;
}
}

View File

@ -0,0 +1,233 @@
/*
* 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.inventory;
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.item.ItemData;
import ru.windcorp.progressia.common.world.item.ItemDataRegistry;
public abstract class ItemContainerMixed extends ItemContainer {
public static final int MAX_SLOTS = 10000;
private static final int GROWTH_STEP = 10;
private static final int MINIMUM_CAPACITY = 10;
private ItemData[] items = new ItemData[MINIMUM_CAPACITY];
private int[] counts = new int[MINIMUM_CAPACITY];
public ItemContainerMixed(String id) {
super(id, MAX_SLOTS);
}
protected void setCapacity(int minimumCapacity) {
if (minimumCapacity < 0) {
return;
}
int newCapacity = ((minimumCapacity - MINIMUM_CAPACITY - 1) / GROWTH_STEP + 1) * GROWTH_STEP + MINIMUM_CAPACITY;
ItemData[] newItems = new ItemData[newCapacity];
int[] newCounts = new int[newCapacity];
int length = Math.min(this.items.length, newItems.length);
System.arraycopy(this.items, 0, newItems, 0, length);
System.arraycopy(this.counts, 0, newCounts, 0, length);
this.items = newItems;
this.counts = newCounts;
}
protected void ensureCapacity(int minimumCapacity) {
if (items.length >= minimumCapacity) {
return;
}
setCapacity(minimumCapacity);
}
@Override
public synchronized void read(DataInput input, IOContext context) throws IOException {
int size = input.readInt();
ensureCapacity(size);
for (int index = 0; index < size; ++index) {
ItemData item;
int count = input.readInt();
if (count != 0) {
String id = input.readUTF();
item = ItemDataRegistry.getInstance().create(id);
item.read(input, context);
} else {
item = null;
}
items[index] = item;
counts[index] = count;
fireSlotChangeEvent(index);
}
checkState();
}
@Override
public synchronized void write(DataOutput output, IOContext context) throws IOException {
int size = items.length;
output.writeInt(size);
for (int index = 0; index < size; ++index) {
output.writeInt(counts[index]);
ItemData item = items[index];
if (item != null) {
output.writeUTF(item.getId());
item.write(output, context);
}
}
}
@Override
public void copy(Encodable destination) {
ItemContainerMixed other = (ItemContainerMixed) destination;
int myLength = this.items.length;
synchronized (this) {
synchronized (other) {
other.setCapacity(myLength);
System.arraycopy(this.counts, 0, other.counts, 0, myLength);
for (int i = 0; i < myLength; ++i) {
ItemData myItem = this.items[i];
ItemData otherItem;
if (myItem == null) {
otherItem = null;
} else {
otherItem = ItemDataRegistry.getInstance().create(myItem.getId());
myItem.copy(otherItem);
}
other.items[i] = otherItem;
other.fireSlotChangeEvent(i);
}
}
}
}
@Override
public ItemData getItem(int index) {
if (index < 0 || index >= items.length) {
return null;
}
return items[index];
}
@Override
public int getCount(int index) {
if (index < 0 || index >= counts.length) {
return 0;
}
return counts[index];
}
@Override
public int getMaxIndex() {
return items.length;
}
@Override
protected boolean add(int index, ItemData item, int count) {
if (!canAdd(index, item, count)) {
return false;
}
if (item != null) {
ensureCapacity(index + 1);
this.items[index] = item;
this.counts[index] += count;
fireSlotChangeEvent(index);
checkState();
}
return true;
}
@Override
protected boolean remove(int index, ItemData item, int count) {
if (!canRemove(index, item, count)) {
return false;
}
if (count != 0) {
this.counts[index] -= count;
if (this.counts[index] == 0) {
this.items[index] = null;
shrinkIfPossible();
}
fireSlotChangeEvent(index);
checkState();
}
return true;
}
protected void shrinkIfPossible() {
int upperBound;
for (upperBound = counts.length; upperBound > MINIMUM_CAPACITY; --upperBound) {
if (counts[upperBound - 1] != 0) {
break;
}
}
if (upperBound != counts.length) {
setCapacity(upperBound);
}
}
@Override
protected synchronized void checkState() {
super.checkState();
if (items.length > MAX_SLOTS) {
throw new IllegalStateException("Container has more than " + MAX_SLOTS + " slots (items): " + items.length);
}
if (counts.length > MAX_SLOTS) {
throw new IllegalStateException(
"Container has more than " + MAX_SLOTS + " slots (counts): " + counts.length
);
}
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.inventory;
public class ItemContainerMixedSimple extends ItemContainerMixed {
private final float massLimit;
private final float volumeLimit;
public ItemContainerMixedSimple(String id, float massLimit, float volumeLimit) {
super(id);
this.massLimit = massLimit;
this.volumeLimit = volumeLimit;
}
@Override
public float getMassLimit() {
return massLimit;
}
@Override
public float getVolumeLimit() {
return volumeLimit;
}
}

View File

@ -0,0 +1,151 @@
/*
* 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.inventory;
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.item.ItemData;
import ru.windcorp.progressia.common.world.item.ItemDataRegistry;
public abstract class ItemContainerSingle extends ItemContainer {
private ItemData item;
private int count;
private ItemSlot slot = new ItemSlot(this, 0);
public ItemContainerSingle(String id) {
super(id, 1);
}
@Override
public synchronized void read(DataInput input, IOContext context) throws IOException {
count = input.readInt();
if (count != 0) {
String id = input.readUTF();
item = ItemDataRegistry.getInstance().create(id);
item.read(input, context);
} else {
item = null;
}
fireSlotChangeEvent(0);
checkState();
}
@Override
public synchronized void write(DataOutput output, IOContext context) throws IOException {
output.writeInt(count);
if (item != null) {
output.writeUTF(item.getId());
item.write(output, context);
}
}
@Override
public void copy(Encodable destination) {
ItemContainerSingle other = (ItemContainerSingle) destination;
synchronized (this) {
synchronized (other) {
other.count = this.count;
if (this.item == null) {
other.item = null;
} else {
if (other.item == null || !other.item.isLike(this.item)) {
other.item = ItemDataRegistry.getInstance().create(this.item.getId());
}
this.item.copy(other.item);
other.fireSlotChangeEvent(0);
}
}
}
}
@Override
public ItemData getItem(int index) {
if (index != 0) {
return null;
}
return item;
}
public ItemData getItem() {
return item;
}
@Override
public int getCount(int index) {
if (index != 0) {
return 0;
}
return count;
}
public int getCount() {
return count;
}
public ItemSlot slot() {
return slot;
}
@Override
public int getMaxIndex() {
return 1;
}
@Override
protected boolean add(int index, ItemData item, int count) {
if (!canAdd(index, item, count)) {
return false;
}
if (item != null) {
this.item = item;
this.count += count;
fireSlotChangeEvent(0);
checkState();
}
return true;
}
@Override
protected boolean remove(int index, ItemData item, int count) {
if (!canRemove(index, item, count)) {
return false;
}
if (count != 0) {
this.count -= count;
if (this.count == 0) {
this.item = null;
}
fireSlotChangeEvent(0);
checkState();
}
return true;
}
}

View File

@ -0,0 +1,140 @@
/*
* 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.inventory;
import ru.windcorp.progressia.common.world.item.ItemData;
/**
* A reference to a slot in a container. The container and the index of an
* {@code ItemSlot} cannot be changed.
* <p>
* {@code ItemSlot}s are wrapper objects; there may be multiple objects
* referencing a single slot. Slot objects are considered
* {@linkplain #equals(Object) equal} iff their indices are equal and they refer
* to the same container.
* <p>
* This class provides public methods for fetching slot contents but not for
* changing them. To alter a slot, use an appropriate method from {@link Items}.
*/
public class ItemSlot {
private final ItemContainer container;
private final int index;
public ItemSlot(ItemContainer container, int index) {
this.container = container;
this.index = index;
}
/**
* @return the container
*/
public ItemContainer getContainer() {
return container;
}
/**
* @return the index
*/
public int getIndex() {
return index;
}
public Inventory getInventory() {
return container.getInventory();
}
public ItemData getItem() {
return container.getItem(index);
}
public int getCount() {
return container.getCount(index);
}
public boolean isEmpty() {
return container.isEmpty(index);
}
public boolean canAdd(ItemData item, int count) {
return container.canAdd(index, item, count);
}
public boolean canRemove(ItemData item, int count) {
return container.canRemove(index, item, count);
}
protected boolean add(ItemData item, int count) {
return container.add(index, item, count);
}
protected boolean remove(ItemData item, int count) {
return container.remove(index, item, count);
}
public float getMass() {
synchronized (container) {
int count = getCount();
if (count == 0) {
return 0;
}
return count * getItem().getMass();
}
}
public float getVolume() {
synchronized (container) {
int count = getCount();
if (count == 0) {
return 0;
}
return count * getItem().getVolume();
}
}
/*
* For purposes of equality checking, all container instances are considered
* different
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + System.identityHashCode(container);
result = prime * result + index;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ItemSlot other = (ItemSlot) obj;
if (container != other.container)
return false;
if (index != other.index)
return false;
return true;
}
}

View File

@ -0,0 +1,264 @@
/*
* 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.inventory;
import ru.windcorp.progressia.common.world.item.ItemData;
import ru.windcorp.progressia.common.world.item.LinearItemProperty;
public class Items {
private static int pour(ItemContainer from, int fromIndex, ItemContainer into, int intoIndex, int max) {
synchronized (from) {
synchronized (into) {
if (!from.isRemovingAllowed()) {
return 0;
}
ItemData item = from.getItem(fromIndex);
if (item == null) {
return 0;
}
if (!into.canAdd(item)) {
return 0;
}
if (!into.isEmpty(intoIndex) && !into.getItem(intoIndex).equals(item)) {
return 0;
}
int originalCount = from.getCount(fromIndex);
int transferCount = Math.min(originalCount, max);
for (LinearItemProperty prop : LinearItemProperty.values()) {
int canFitPropwise = (int) Math.floor((prop.getLimit(into) - prop.get(into)) / prop.get(item));
if (canFitPropwise < transferCount) {
transferCount = canFitPropwise;
}
}
if (transferCount == 0) {
return 0;
}
if (!into.canAdd(intoIndex, item, transferCount)) {
return 0;
}
if (!from.remove(fromIndex, item, transferCount)) {
return 0;
}
boolean success = into.add(intoIndex, item, transferCount);
assert success : "Wait, canAdd and canRemove promised the operation would be safe!";
return transferCount;
}
}
}
/**
* Attempts to transfer as many items as possible, but no more than
* {@code max}, between two slots. The origin of the items is {@code from},
* the destination is {@code into}. This method does nothing if the items
* could not be removed from origin or could not be inserted into
* destination, such as when {@code into} contains different items.
*
* @param from the slot to transfer item from
* @param into the destination of the items
* @param max the maximum amount of items to transfer. Use
* {@code Integer.MAX_VALUE} to remove the limit
* @return the actual amount of items moved between slots
*/
public static int pour(ItemSlot from, ItemSlot into, int max) {
return pour(from.getContainer(), from.getIndex(), into.getContainer(), into.getIndex(), max);
}
/**
* Attempts to transfer as many items as possible between two slots. The
* origin of the items is {@code from}, the destination is {@code into}.
* This method does nothing if the items could not be removed from origin or
* could not be inserted into destination, such as when {@code into}
* contains different items.
*
* @param from the slot to transfer item from
* @param into the destination of the items
* @return the actual amount of items moved between slots
*/
public static int pour(ItemSlot from, ItemSlot into) {
return pour(from.getContainer(), from.getIndex(), into.getContainer(), into.getIndex(), Integer.MAX_VALUE);
}
/**
* Attempts to swap the contents of the two slots. Swapping contents of two
* empty slots results in a no-op and is considered a success.
*
* @param a one of the slots
* @param b the other slot
* @return whether the swap succeeded
*/
public static boolean swap(ItemSlot a, ItemSlot b) {
synchronized (a.getContainer()) {
synchronized (b.getContainer()) {
if (a.isEmpty() && b.isEmpty()) {
return true;
}
if (!a.isEmpty() && !a.getContainer().isRemovingAllowed()) {
return false;
}
if (!b.isEmpty() && !b.getContainer().isRemovingAllowed()) {
return false;
}
ItemData aItem = a.getItem();
int aCount = a.getCount();
ItemData bItem = b.getItem();
int bCount = b.getCount();
a.remove(aItem, aCount);
b.remove(bItem, bCount);
if (a.canAdd(bItem, bCount) && b.canAdd(aItem, aCount)) {
a.add(bItem, bCount);
b.add(aItem, aCount);
return true;
} else {
a.add(aItem, aCount);
b.add(bItem, bCount);
return false;
}
}
}
}
/**
* Attempts to place new items into the specified slot. Either all or none
* of the requested items will be spawned.
*
* @param into destination slot
* @param item the item to add
* @param count the item count
* @return whether the addition succeeded
*/
public static boolean spawn(ItemSlot into, ItemData item, int count) {
synchronized (into.getContainer()) {
return into.add(item, count);
}
}
/**
* Attempts to remove items from the specified slot. Either all or none of
* the requested items will be destroyed.
*
* @param from the slot
* @param item the item to remove
* @param count the item count
* @return whether the removal succeeded
*/
public static boolean destroy(ItemSlot from, ItemData item, int count) {
synchronized (from.getContainer()) {
return from.remove(item, count);
}
}
public static int pour(ItemSlot from, ItemContainer into, int max) {
synchronized (from.getContainer()) {
synchronized (into) {
int totalPoured = 0;
for (int index = 0; max > 0 && index <= into.getMaxIndex(); ++index) {
int poured = pour(from.getContainer(), from.getIndex(), into, index, max);
max -= poured;
totalPoured += poured;
}
return totalPoured;
}
}
}
public static int pour(ItemSlot from, ItemContainer into) {
return pour(from, into, Integer.MAX_VALUE);
}
public static boolean spawn(ItemContainer into, ItemData item, int count) {
synchronized (into) {
if (item == null && count == 0) {
return true;
}
if (count < 0) {
return false;
}
if (!into.canAdd(item)) {
return false;
}
for (LinearItemProperty prop : LinearItemProperty.values()) {
float requested = prop.get(item) * count;
float available = prop.getLimit(into) - prop.get(into);
if (requested > available) {
return false;
}
}
int compatibleSlot = -1;
int firstEmptySlot = -1;
for (int index = 0; index <= into.getMaxIndex(); ++index) {
ItemData inSlot = into.getItem(index);
if (inSlot == null) {
if (firstEmptySlot == -1) {
firstEmptySlot = index;
}
} else if (inSlot.equals(item)) {
compatibleSlot = index;
break;
}
}
if (compatibleSlot == -1) {
compatibleSlot = firstEmptySlot;
}
if (compatibleSlot == -1) {
// Means the inventory is full due to slot limit
return false;
}
return into.add(compatibleSlot, item, count);
}
}
private Items() {
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.inventory.event;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
import ru.windcorp.progressia.common.world.item.inventory.InventoryUser;
public class InventoryClosingEvent extends InventoryEvent {
private final InventoryUser closingUser;
public InventoryClosingEvent(Inventory inventory, InventoryUser closingUser) {
super(inventory);
this.closingUser = closingUser;
}
public InventoryUser getClosingUser() {
return closingUser;
}
}

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.item.inventory.event;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
public abstract class InventoryEvent {
private final Inventory inventory;
public InventoryEvent(Inventory inventory) {
this.inventory = inventory;
}
/**
* @return the inventory
*/
public Inventory getInventory() {
return inventory;
}
}

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.inventory.event;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
import ru.windcorp.progressia.common.world.item.inventory.InventoryUser;
public class InventoryOpenedEvent extends InventoryEvent {
private final InventoryUser newUser;
public InventoryOpenedEvent(Inventory inventory, InventoryUser newUser) {
super(inventory);
this.newUser = newUser;
}
/**
* @return the newUser
*/
public InventoryUser getNewUser() {
return newUser;
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.inventory.event;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
public abstract class ItemContainerEvent extends InventoryEvent {
private final ItemContainer container;
public ItemContainerEvent(ItemContainer container) {
super(container.getInventory());
this.container = container;
}
/**
* @return the container
*/
public ItemContainer getContainer() {
return container;
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.inventory.event;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemSlot;
public class ItemSlotChangedEvent extends ItemSlotEvent {
public ItemSlotChangedEvent(ItemSlot slot) {
super(slot);
}
public ItemSlotChangedEvent(ItemContainer container, int index) {
super(container, index);
}
}

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.common.world.item.inventory.event;
import ru.windcorp.progressia.common.world.item.inventory.ItemContainer;
import ru.windcorp.progressia.common.world.item.inventory.ItemSlot;
public abstract class ItemSlotEvent extends ItemContainerEvent {
private ItemSlot slot = null;
private final int index;
public ItemSlotEvent(ItemContainer container, int index) {
super(container);
this.index = index;
}
public ItemSlotEvent(ItemSlot slot) {
this(slot.getContainer(), slot.getIndex());
this.slot = slot;
}
/**
* @return the index
*/
public int getIndex() {
return index;
}
/**
* @return the slot
*/
public ItemSlot getSlot() {
if (slot == null) {
slot = new ItemSlot(getContainer(), index);
}
return slot;
}
}

View File

@ -20,11 +20,13 @@ package ru.windcorp.progressia.server;
import glm.vec._3.Vec3;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
import ru.windcorp.progressia.common.world.item.ItemDataRegistry;
import ru.windcorp.progressia.common.world.item.inventory.Items;
import ru.windcorp.progressia.server.comms.ClientPlayer;
import ru.windcorp.progressia.server.events.PlayerJoinedEvent;
import ru.windcorp.progressia.server.events.PlayerLeftEvent;
import ru.windcorp.progressia.test.TestContent;
import java.util.ArrayList;
import java.util.Collection;
@ -59,21 +61,22 @@ public class PlayerManager {
Player player = getServer().getWorld().getContainer().loadPlayer(login, clientPlayer, getServer());
if (player == null) { // create new player
EntityData entity = spawnPlayerEntity(clientPlayer, login);
EntityData entity = spawnPlayerEntity(login);
player = new Player(entity, getServer(), clientPlayer);
}
getServer().getWorld().getData().addEntity(player.getEntity());
getServer().getWorld().spawnEntity(player.getEntity());
return player;
}
private EntityData spawnPlayerEntity(ClientPlayer clientPlayer, String login) {
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
private EntityDataPlayer spawnPlayerEntity(String login) {
EntityDataPlayer player = (EntityDataPlayer) EntityDataRegistry.getInstance().create("Core:Player");
Items.spawn(player.getHand(0).slot(), ItemDataRegistry.getInstance().create("Test:Stick"), 5);
Items.spawn(player.getEquipmentSlot(0).slot(), ItemDataRegistry.getInstance().create("Test:CardboardBackpack"), 1);
player.setEntityId(TestContent.PLAYER_ENTITY_ID);
player.setPosition(getServer().getWorld().getGenerator().suggestSpawnLocation());
player.setUpVector(new Vec3(0, 0, 1));
player.setLookingAt(new Vec3(2, 1, 0));

View File

@ -37,7 +37,6 @@ import ru.windcorp.progressia.server.Player;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.comms.ClientPlayer;
import ru.windcorp.progressia.server.world.io.WorldContainer;
import ru.windcorp.progressia.test.TestContent;
import java.io.*;
import java.nio.file.Files;
@ -125,7 +124,7 @@ public class RegionWorldContainer implements WorldContainer {
return null;
}
EntityData player = EntityDataRegistry.getInstance().create("Test:Player");
EntityData player = EntityDataRegistry.getInstance().create("Core:Player");
try (
DataInputStream dataInputStream = new DataInputStream(
new BufferedInputStream(
@ -136,7 +135,6 @@ public class RegionWorldContainer implements WorldContainer {
)
) {
player.read(dataInputStream, IOContext.SAVE);
player.setEntityId(TestContent.PLAYER_ENTITY_ID);
return new Player(player, server, clientPlayer);
} catch (IOException ioException) {
throw CrashReports.report(ioException, "Could not load player data: " + login);

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(new EquipmentSlot("Backpack", i -> true));
}
@Override
public CollisionModel getCollisionModel() {
return new AABB(0, 0, HEIGHT / 2, WIDTH, WIDTH, HEIGHT);
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package ru.windcorp.progressia.test;
import ru.windcorp.progressia.client.graphics.world.hud.InventoryHUD;
import ru.windcorp.progressia.client.graphics.world.hud.HandsHUD;
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.EquipmentSlot;
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 HandsHUD.Side getHandSide(Hand hand) {
return hand.getIndex() == 0 ? HandsHUD.Side.RIGHT : HandsHUD.Side.LEFT;
}
@Override
public InventoryHUD.Side getEquipmentSlotSide(EquipmentSlot equipmentSlot) {
return InventoryHUD.Side.LEFT;
}
}

View File

@ -21,6 +21,7 @@ import glm.vec._3.Vec3;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.EntityDataRegistry;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.block.TickableBlock;
import ru.windcorp.progressia.server.world.context.ServerBlockContext;
@ -36,7 +37,7 @@ public class TestBlockLogicStatieSpawner extends BlockLogic implements TickableB
@Override
public void tick(ServerBlockContext context) {
Vec3i loc = context.toAbsolute(context.getLocation(), null);
EntityData entity = new TestEntityDataStatie();
EntityData entity = EntityDataRegistry.getInstance().create("Test:Statie");
entity.setPosition(new Vec3(loc.x, loc.y, loc.z));
context.addEntity(entity);

View File

@ -20,6 +20,7 @@ package ru.windcorp.progressia.test;
import static ru.windcorp.progressia.client.world.block.BlockRenderRegistry.getBlockTexture;
import static ru.windcorp.progressia.client.world.tile.TileRenderRegistry.getTileTexture;
import static ru.windcorp.progressia.client.world.item.ItemRenderRegistry.getItemTexture;
import java.util.ArrayList;
import java.util.Arrays;
@ -27,21 +28,33 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import ru.windcorp.progressia.client.graphics.world.hud.HUDWorkspace;
import ru.windcorp.progressia.client.world.block.*;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSimple;
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface;
import ru.windcorp.progressia.client.world.entity.*;
import ru.windcorp.progressia.client.world.item.ItemRender;
import ru.windcorp.progressia.client.world.item.ItemRenderRegistry;
import ru.windcorp.progressia.client.world.item.ItemRenderSimple;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponent;
import ru.windcorp.progressia.client.world.item.inventory.InventoryComponentSimple;
import ru.windcorp.progressia.client.world.item.inventory.InventoryRender;
import ru.windcorp.progressia.client.world.item.inventory.InventoryRenderRegistry;
import ru.windcorp.progressia.client.world.tile.*;
import ru.windcorp.progressia.common.collision.AABB;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.collision.CollisionModel;
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory;
import ru.windcorp.progressia.common.state.StatefulObjectRegistry.IdFactory;
import ru.windcorp.progressia.common.world.GravityModelRegistry;
import ru.windcorp.progressia.common.world.block.*;
import ru.windcorp.progressia.common.world.entity.*;
import ru.windcorp.progressia.common.world.io.ChunkIO;
import ru.windcorp.progressia.common.world.item.ItemData;
import ru.windcorp.progressia.common.world.item.ItemDataContainer;
import ru.windcorp.progressia.common.world.item.ItemDataRegistry;
import ru.windcorp.progressia.common.world.item.ItemDataSimple;
import ru.windcorp.progressia.common.world.item.inventory.Inventory;
import ru.windcorp.progressia.common.world.tile.*;
import ru.windcorp.progressia.server.world.block.*;
import ru.windcorp.progressia.server.world.entity.*;
@ -57,8 +70,6 @@ import ru.windcorp.progressia.test.trees.BlockRenderLeavesPine;
public class TestContent {
public static final String PLAYER_LOGIN = "Sasha";
public static final long PLAYER_ENTITY_ID = 0x42;
public static final long STATIE_ENTITY_ID = 0xDEADBEEF;
public static final List<BlockData> PLACEABLE_BLOCKS = new ArrayList<>();
public static final List<TileData> PLACEABLE_TILES = new ArrayList<>();
@ -75,6 +86,7 @@ public class TestContent {
private static void registerWorldContent() {
registerBlocks();
registerTiles();
registerItems();
registerEntities();
}
@ -100,7 +112,7 @@ public class TestContent {
registerSimplestBlock("BrickWhite");
registerSimplestBlock("Concrete");
registerSimplestBlock("WoodenPlank");
registerRocks();
register(new BlockData("Test:Glass"));
@ -117,12 +129,12 @@ public class TestContent {
)
);
register(new BlockLogic("Test:Log"));
register(new BlockData("Test:TemporaryLeaves"));
register(new BlockRenderLeavesHazel("Test:TemporaryLeaves", getBlockTexture("LeavesHazel")));
// Sic, using Glass logic for leaves because Test
register(new TestBlockLogicGlass("Test:TemporaryLeaves"));
// Sic, using Glass logic for leaves because Test
register(new TestBlockLogicGlass("Test:TemporaryLeaves"));
register(new BlockData("Test:StatieSpawner"));
register(new BlockRenderOpaqueCube("Test:StatieSpawner", getBlockTexture("StatieSpawner")));
register(new TestBlockLogicStatieSpawner("Test:StatieSpawner"));
@ -213,7 +225,7 @@ public class TestContent {
registerHerb("Bush", 1);
registerHerb("Fern", 3);
TileDataRegistry.getInstance().values().forEach(PLACEABLE_TILES::add);
PLACEABLE_TILES.removeIf(b -> placeableBlacklist.contains(b.getId()));
PLACEABLE_TILES.sort(Comparator.comparing(TileData::getId));
@ -233,6 +245,26 @@ public class TestContent {
FLOWERS.registerAllFlowers();
}
private static void registerItems() {
registerSimplestItem("MoonTypeIceCream", Units.get("200 g"), Units.get("1 L"));
registerSimplestItem("Stick", Units.get("260 g"), Units.get("0.5 L"));
registerSimplestItem("RedGraniteCobblestone", Units.get("4 kg"), Units.get("1500 cm^3"));
registerItem(
"Test:CardboardBackpack",
s -> new ItemDataContainer(
"Test:CardboardBackpack",
Units.get("0.7 kg"), // Own mass
Units.get("5 kg"), // Container mass limit
Units.get("125 L"), // Own volume
Units.get("125 L"), // Container volume limit
false // Whether container contents contribute to item volume
)
);
register(new ItemRenderSimple("Test:CardboardBackpack", getItemTexture("CardboardBackpack")));
registerSimplestInventory("Test:CardboardBackpack");
}
private static void registerSimplestBlock(String name) {
String id = "Test:" + name;
@ -262,21 +294,41 @@ public class TestContent {
register(new HangingTileLogic(id));
}
private static void registerEntities() {
float scale = 1.8f / 8;
registerEntityData("Test:Player", e -> e.setCollisionModel(new AABB(0, 0, 4 * scale, 0.8f, 0.8f, 1.8f)));
register(new TestEntityRenderHuman("Test:Player"));
register(new EntityLogic("Test:Player"));
private static void registerSimplestItem(String name, float mass, float volume) {
String id = "Test:" + name;
registerItem(id, s -> new ItemDataSimple(s, mass, volume));
register(new ItemRenderSimple(id, getItemTexture(name)));
}
register("Test:Statie", TestEntityDataStatie::new);
private static void registerEntities() {
registerPlayer();
registerEntity("Test:Statie", TestEntityDataStatie::new);
register(new TestEntityRenderStatie("Test:Statie"));
register(new TestEntityLogicStatie("Test:Statie"));
}
private static void registerSimplestInventory(String id) {
InventoryRenderRegistry.getInstance().register(new InventoryRender(id) {
@Override
public InventoryComponent createComponent(Inventory inventory, HUDWorkspace workspace) {
return new InventoryComponentSimple(id, inventory, workspace);
}
});
}
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() {
TestPlayerControls.getInstance().registerControls();
}
private static void register(BlockData x) {
@ -287,25 +339,15 @@ public class TestContent {
TileDataRegistry.getInstance().register(x);
}
private static void register(
String id,
Factory<EntityData> factory
) {
EntityDataRegistry.getInstance().register(id, factory);
private static void registerItem(String id, IdFactory<ItemData> factory) {
ItemDataRegistry.getInstance().register(id, factory);
}
private static void registerEntityData(
private static void registerEntity(
String id,
Consumer<EntityData> transform
IdFactory<EntityData> factory
) {
EntityDataRegistry.getInstance().register(id, new Factory<EntityData>() {
@Override
public EntityData build() {
EntityData entity = new EntityData(id);
transform.accept(entity);
return entity;
}
});
EntityDataRegistry.getInstance().register(id, factory);
}
private static void register(BlockRender x) {
@ -316,6 +358,10 @@ public class TestContent {
TileRenderRegistry.getInstance().register(x);
}
private static void register(ItemRender x) {
ItemRenderRegistry.getInstance().register(x);
}
private static void register(EntityRender x) {
EntityRenderRegistry.getInstance().register(x);
}

View File

@ -26,10 +26,6 @@ public class TestEntityDataStatie extends EntityData {
private final IntStateField size = field("Test:Size").setShared().ofInt().build();
public TestEntityDataStatie() {
this("Test:Statie");
}
protected TestEntityDataStatie(String id) {
super(id);
setCollisionModel(new AABB(0, 0, 0, 1, 1, 1));

View File

@ -0,0 +1,141 @@
/*
* 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.controls;
import org.lwjgl.glfw.GLFW;
import ru.windcorp.progressia.client.Client;
import ru.windcorp.progressia.client.ClientState;
import ru.windcorp.progressia.client.comms.controls.ControlTriggerRegistry;
import ru.windcorp.progressia.client.comms.controls.ControlTriggers;
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
import ru.windcorp.progressia.common.Units;
import ru.windcorp.progressia.common.world.entity.EntityDataPlayer;
public class InventoryControls {
/**
* The minimum duration of a ctrl stroke for it to be consider holding the key down
*/
private static final double MIN_CTRL_HOLD_LENGTH = Units.get("200 ms");
private double lastCtrlPress;
{
reset();
}
public void reset() {
lastCtrlPress = Double.NEGATIVE_INFINITY;
}
public void registerControls() {
ControlTriggerRegistry triggers = ControlTriggerRegistry.getInstance();
triggers.register(
ControlTriggers.localOf(
"Test:ToggleInventory",
KeyEvent.class,
this::toggleInventory,
new KeyMatcher("E")::matches
)
);
triggers.register(
ControlTriggers.localOf(
"Test:HideHUD",
KeyEvent.class,
this::switchHUD,
new KeyMatcher("F1")::matches
)
);
triggers.register(
ControlTriggers.localOf(
"Test:SwitchHandsWithCtrl",
KeyEvent.class,
this::switchHandsWithCtrl,
e -> !e.isRepeat(),
e -> e.getKey() == GLFW.GLFW_KEY_LEFT_CONTROL || e.getKey() == GLFW.GLFW_KEY_RIGHT_CONTROL
)
);
}
private void toggleInventory() {
Client client = ClientState.getInstance();
if (client == null || !client.isReady())
return;
client.getHUD().setInventoryShown(!client.getHUD().isInventoryShown());
}
private void switchHUD() {
Client client = ClientState.getInstance();
if (client == null || !client.isReady())
return;
client.getHUD().setHidden(!client.getHUD().isHidden());
}
public void switchHandsWithCtrl(KeyEvent event) {
int change = 0;
if (event.isPress()) {
change = +1;
lastCtrlPress = event.getTime();
} else {
if (event.getTime() - lastCtrlPress > MIN_CTRL_HOLD_LENGTH) {
change = -1;
lastCtrlPress = Double.NEGATIVE_INFINITY;
}
}
if (event.hasShift()) {
change *= -1;
}
switchHands(change);
}
private void switchHands(int change) {
if (change == 0) {
return;
}
if (ClientState.getInstance() == null || !ClientState.getInstance().isReady()) {
return;
}
EntityDataPlayer entity = ClientState.getInstance().getLocalPlayer().getEntity();
int selected = entity.getSelectedHandIndex();
int maxSelected = entity.getHandCount() - 1;
selected += change;
if (selected < 0) {
selected = maxSelected;
} else if (selected > maxSelected) {
selected = 0;
}
entity.setSelectedHandIndexNow(selected);
}
}

Some files were not shown because too many files have changed in this diff Show More