From 58618b97bc1fb70756376dbfe584d16283bfc64f Mon Sep 17 00:00:00 2001 From: Ivory Date: Sun, 15 Jan 2023 04:11:17 -0500 Subject: [PATCH] better jobs, quarry, immediateui --- .../java/xyz/valnet/engine/graphics/Font.java | 85 +++--- .../valnet/engine/graphics/ImmediateUI.java | 250 ++++++++++++++++++ .../java/xyz/valnet/engine/math/Vector4f.java | 11 + .../java/xyz/valnet/engine/math/Vector4i.java | 32 +++ .../engine/scenegraph/IMouseCaptureArea.java | 2 +- .../valnet/engine/scenegraph/SceneGraph.java | 2 +- .../xyz/valnet/hadean/gameobjects/Job.java | 10 +- .../valnet/hadean/gameobjects/JobBoard.java | 97 +++++-- .../hadean/gameobjects/SelectionUI.java | 218 +++++---------- .../gameobjects/inputlayer/BuildLayer.java | 2 +- .../inputlayer/SelectionLayer.java | 2 +- .../valnet/hadean/gameobjects/ui/Popup.java | 34 +++ .../hadean/gameobjects/ui/tabs/BuildTab.java | 2 +- .../hadean/gameobjects/worldobjects/Tree.java | 2 +- .../worldobjects/agents/Agent.java | 5 +- .../constructions/Construction.java | 123 +++++++++ .../worldobjects/constructions/Quarry.java | 50 ++-- .../gameobjects/worldobjects/items/Item.java | 2 +- .../gameobjects/worldobjects/pawn/Pawn.java | 5 + .../java/xyz/valnet/hadean/input/Button.java | 31 ++- .../valnet/hadean/input/IButtonListener.java | 5 +- .../valnet/hadean/interfaces/ISelectable.java | 3 + .../xyz/valnet/hadean/scenes/GameScene.java | 5 +- .../java/xyz/valnet/hadean/util/Assets.java | 9 +- .../java/xyz/valnet/hadean/util/Layers.java | 1 + 25 files changed, 709 insertions(+), 279 deletions(-) create mode 100644 src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java create mode 100644 src/main/java/xyz/valnet/hadean/gameobjects/ui/Popup.java create mode 100644 src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Construction.java diff --git a/src/main/java/xyz/valnet/engine/graphics/Font.java b/src/main/java/xyz/valnet/engine/graphics/Font.java index a794f71..76f136f 100644 --- a/src/main/java/xyz/valnet/engine/graphics/Font.java +++ b/src/main/java/xyz/valnet/engine/graphics/Font.java @@ -6,50 +6,57 @@ import xyz.valnet.engine.math.Vector4i; public class Font { - private Map charset; - private final int w, h; + private Map charset; + private final int w, h; - public Font(Map charset, int w, int h) { - this.charset = charset; - this.w = w; - this.h = h; - } + private final int scale; - public void drawString(String str, int x, int y) { - int cursorX = x; - int cursorY = y; + public Font(Map charset, int w, int h, int scale) { + this.charset = charset; + this.scale = scale; + this.w = w * scale; + this.h = h * scale; + } - Sprite s; + public void drawString(String str, int x, int y) { + int cursorX = x; + int cursorY = y; - for(char c : str.toCharArray()) { - if(c == '\n') { - cursorY += h; - cursorX = x; - continue; - } - if(c == '\r') continue; - if(c == '\t') { - cursorX += w * 4; - continue; - } - if(c == ' ' || !charset.containsKey(c)) { - cursorX += w; - continue; - } - s = charset.get(c); - Drawing.drawSprite(s, cursorX, cursorY); - cursorX += w; - } - } + Sprite s; - public Vector4i measure(String text) { - String[] lines = text.split("\n"); - int longest = 0; - int c = 0; - for(String line : lines) { - c = line.length(); - if(c > longest) longest = c; + for(char c : str.toCharArray()) { + if(c == '\n') { + cursorY += h; + cursorX = x; + continue; } - return new Vector4i(longest * w, lines.length * h, 0, 0); + if(c == '\r') continue; + if(c == '\t') { + cursorX += w * 4; + continue; + } + if(c == ' ' || !charset.containsKey(c)) { + cursorX += w; + continue; + } + s = charset.get(c); + Drawing.drawSprite(s, cursorX, cursorY, w, h); + cursorX += w; } + } + + public Vector4i measure(String text) { + String[] lines = text.split("\n"); + int longest = 0; + int c = 0; + for(String line : lines) { + c = line.length(); + if(c > longest) longest = c; + } + return new Vector4i(longest * w, lines.length * h, 0, 0); + } + + public int getLineHeight() { + return h; + } } diff --git a/src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java b/src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java new file mode 100644 index 0000000..ac6b5ec --- /dev/null +++ b/src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java @@ -0,0 +1,250 @@ +package xyz.valnet.engine.graphics; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import xyz.valnet.engine.math.Vector4f; +import xyz.valnet.engine.math.Vector4i; +import xyz.valnet.engine.scenegraph.GameObject; +import xyz.valnet.engine.scenegraph.IMouseCaptureArea; +import xyz.valnet.hadean.input.Button; +import xyz.valnet.hadean.util.Assets; +import xyz.valnet.hadean.util.Layers; + +public abstract class ImmediateUI extends GameObject implements IMouseCaptureArea { + + private boolean active; + private boolean mouseDown; + + public void render() { + begin(); + gui(); + end(); + } + + @Override + public void mouseEnter() { + active = true; + } + + @Override + public void mouseLeave() { + active = false; + } + + @Override + public void mouseDown(int button) { + if(button == 0) mouseDown = true; + } + + @Override + public void mouseUp(int button) { + if(button == 0) mouseDown = false; + } + + public abstract Vector4f getGuiBox(); + + @Override + public float getLayer() { + return Layers.GENERAL_UI_INTERACTABLE; + } + + protected abstract void gui(); + + private record StackingContext( + boolean fixedSize, + Vector4f box, + Vector4f occlusionBox + // layout manager? + ) {} + + private StackingContext context; + private Stack contextStack; + + private transient Map buttons = new HashMap(); + private Set usedButtonId = new HashSet(); + private transient Map clicks = new HashMap(); + private int buttonCount; + + private String genButtonId() { + buttonCount ++; + return "DefaultButton-" + buttonCount; + } + + private boolean hasButton(String id) { + usedButtonId.add(id); + return buttons.containsKey(id); + } + + private Button getButton(String id) { + if(hasButton(id)) return buttons.get(id); + System.out.println("Created new Button"); + Button btn = new Button(Assets.uiFrame, "", 0, 0, 0, 0, 0); + btn.registerClickListener((target) -> { + if(!clicks.containsKey(target)) clicks.put(target, 0); + clicks.put(target, clicks.get(target) + 1); + }); + buttons.put(id, add(btn)); + return btn; + } + + private boolean getClick(Button btn) { + if(clicks.containsKey(btn)) { + int clickCount = clicks.get(btn); + if(clickCount > 0) { + clicks.put(btn, clickCount - 1); + return true; + } + } + return false; + } + + private void adjustBox(float h) { + if(context.fixedSize) { + context.box.y += h; + context.box.w -= h; + } else { + context.box.w += h; + } + } + + // === ELEMENTS === + + protected void begin() { + buttonCount = 0; + usedButtonId.clear(); + Drawing.setLayer(Layers.GENERAL_UI_INTERACTABLE); + context = new StackingContext(true, getGuiBox(), getGuiBox()); + contextStack = new Stack(); + Assets.uiFrame.draw(context.occlusionBox); + pad(); + } + + protected boolean button(String text) { + return button(genButtonId(), text); + } + + protected boolean button(String id, String text) { + float h = 32; + Vector4f buttonBox = new Vector4f(context.box.x, context.box.y, context.box.z, h); + Button btn = getButton(id); + + btn.setText(text); + btn.setPosition((int) buttonBox.x, (int) buttonBox.y); + btn.setSize((int) buttonBox.z, (int) buttonBox.w); + btn.setLayer(Layers.GENERAL_UI_INTERACTABLE + contextStack.size()); + + // System.out.println("" + buttonBox); + + adjustBox(h); + + return getClick(btn); + } + + protected void horizontal(int columns) { + + } + + protected void horizontalEnd() { + + } + + protected void group() { + contextStack.push(context); + context = new StackingContext(false, + new Vector4f( + context.box.x, context.box.y, context.box.z, 0 + ), + context.occlusionBox.copy() + ); + pad(); + } + + protected void groupEnd() { + padEnd(); + Drawing.setLayer(getLayer() + contextStack.size() - 1); + float h = context.box.w; + Assets.uiFrame.draw(context.box); + context = contextStack.pop(); + adjustBox(h); + } + + protected void pad() { + contextStack.push(context); + if(context.fixedSize) { + context = new StackingContext(true, + new Vector4f( + context.box.x + 8, context.box.y + 8, context.box.z - 16, context.box.w - 16 + ), + context.occlusionBox.copy() + ); + } else { + context = new StackingContext(false, + new Vector4f( + context.box.x + 8, context.box.y + 8, context.box.z - 16, 0 + ), + context.occlusionBox.copy() + ); + } + } + + protected void padEnd() { + float h = context.box.w + 16; + context = contextStack.pop(); + adjustBox(h); + } + + protected void end() { + padEnd(); + + List buttonIdsToRemove = new ArrayList(); + + for(String id : this.buttons.keySet()) { + if(usedButtonId.contains(id)) continue; + buttonIdsToRemove.add(id); + } + + for(String id : buttonIdsToRemove) { + Button btn = buttons.get(id); + remove(btn); + buttons.remove(id); + if(clicks.containsKey(btn)) { + clicks.remove(btn); + } + } + } + + protected void text(String text) { + text(text, Assets.font); + } + + protected void header(String text) { + text(text, Assets.bigFont); + } + + protected void text(String text, Font font) { + Vector4i measured = font.measure(text); + Drawing.setLayer(getLayer() + contextStack.size()); + + // layout manager things... + if(context.fixedSize) { + font.drawString(text, + (int) context.box.x, + (int) context.box.y + ); + context.box.y += measured.y; + context.box.w -= measured.y; + } else { + font.drawString(text, + (int) context.box.x, + (int) context.box.y + (int) context.box.w + ); + context.box.w += measured.y; + } + } +} diff --git a/src/main/java/xyz/valnet/engine/math/Vector4f.java b/src/main/java/xyz/valnet/engine/math/Vector4f.java index a9a92f7..c362a17 100644 --- a/src/main/java/xyz/valnet/engine/math/Vector4f.java +++ b/src/main/java/xyz/valnet/engine/math/Vector4f.java @@ -58,4 +58,15 @@ public class Vector4f implements Serializable { return new Vector4i((int)x, (int)y, (int)z, (int)w); } + public Vector4f pad(float all) { + return new Vector4f(x + all, y + all, z - all * 2, w - all * 2); + } + + public Vector4f pad(float top, float bottom, float left, float right) { + return new Vector4f(x + left, y + top, z - left - right, w - top - bottom); + } + + public Vector4f copy() { + return new Vector4f(x, y, z, w); + } } diff --git a/src/main/java/xyz/valnet/engine/math/Vector4i.java b/src/main/java/xyz/valnet/engine/math/Vector4i.java index 0a7dad6..f1847bd 100644 --- a/src/main/java/xyz/valnet/engine/math/Vector4i.java +++ b/src/main/java/xyz/valnet/engine/math/Vector4i.java @@ -24,4 +24,36 @@ public class Vector4i implements Serializable { return new Vector2i(x, y); } + // WORKS WITH WIDTH HEIGHT NOT AABB + public Vector2i[] getInternals() { + int size = z * w; + Vector2i[] vecs = new Vector2i[size]; + for(int i = 0; i < z; i ++) { + for(int j = 0; j < w; j ++) { + vecs[i * z + j] = new Vector2i(x + i, y + j); + } + } + return vecs; + } + + // WORKS WITH WIDTH HEIGHT NOT AABB + public Vector2i[] getBorders() { + + int size = 2 * z + 2 * w; + Vector2i[] vecs = new Vector2i[size]; + + // top / bottom row + for(int i = 0; i < z; i ++) { + vecs[i] = new Vector2i(x + i, y - 1); + vecs[size - i - 1] = new Vector2i(x + i, y + w); + } + + // middle pillars + for(int i = 0; i < w; i ++) { + vecs[z + i * 2] = new Vector2i(x - 1, y + i); + vecs[z + i * 2 + 1] = new Vector2i(x + z, y + i); + } + + return vecs; + } } diff --git a/src/main/java/xyz/valnet/engine/scenegraph/IMouseCaptureArea.java b/src/main/java/xyz/valnet/engine/scenegraph/IMouseCaptureArea.java index 8f12d6e..a59c673 100644 --- a/src/main/java/xyz/valnet/engine/scenegraph/IMouseCaptureArea.java +++ b/src/main/java/xyz/valnet/engine/scenegraph/IMouseCaptureArea.java @@ -8,6 +8,6 @@ public interface IMouseCaptureArea { public void mouseDown(int button); public void mouseUp(int button); - public Vector4f getBox(); + public Vector4f getGuiBox(); public float getLayer(); } diff --git a/src/main/java/xyz/valnet/engine/scenegraph/SceneGraph.java b/src/main/java/xyz/valnet/engine/scenegraph/SceneGraph.java index 90381ed..b36ecc1 100644 --- a/src/main/java/xyz/valnet/engine/scenegraph/SceneGraph.java +++ b/src/main/java/xyz/valnet/engine/scenegraph/SceneGraph.java @@ -83,7 +83,7 @@ public abstract class SceneGraph implements IScene { } }); for(IMouseCaptureArea listener : mouseListeners) { - boolean currentlyEntered = listener.getBox().contains(App.mouseX, App.mouseY); + boolean currentlyEntered = listener.getGuiBox().contains(App.mouseX, App.mouseY); if(currentlyEntered) { if(listener != hoveredMouseListener) { if(hoveredMouseListener != null) { diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/Job.java b/src/main/java/xyz/valnet/hadean/gameobjects/Job.java index c46d4c7..f26ee67 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/Job.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/Job.java @@ -155,6 +155,7 @@ public class Job extends GameObject { private List steps; private String name; private int step; + private boolean hasClosed = false; public void reset() { step = 0; @@ -171,17 +172,14 @@ public class Job extends GameObject { public Vector2i[] getLocations() { if(steps.size() == 0) throw new Error("Cannot get location of job with no steps"); - JobStep step = steps.get(0); + JobStep step = getCurrentStep(); return step.getLocations(); } public void nextStep() { step ++; if(isCompleted()) { - get(JobBoard.class).completeJob(this); - for(Callback callback : closedListeners) { - callback.apply(); - } + close(); remove(this); } } @@ -205,6 +203,8 @@ public class Job extends GameObject { } public void close() { + if(hasClosed) return; + hasClosed = true; for(Callback callback : closedListeners) { callback.apply(); } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/JobBoard.java b/src/main/java/xyz/valnet/hadean/gameobjects/JobBoard.java index 81cf4eb..0278b80 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/JobBoard.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/JobBoard.java @@ -10,9 +10,15 @@ import java.util.Set; import java.util.stream.Stream; import xyz.valnet.engine.math.Vector2i; +import xyz.valnet.engine.math.Vector4f; import xyz.valnet.engine.scenegraph.GameObject; +import xyz.valnet.hadean.HadeanGame; import xyz.valnet.hadean.gameobjects.worldobjects.pawn.Pawn; +import xyz.valnet.hadean.interfaces.IItemPredicate; +import xyz.valnet.hadean.interfaces.IItemReceiver; import xyz.valnet.hadean.interfaces.IWorkable; +import xyz.valnet.hadean.util.Assets; +import xyz.valnet.hadean.util.Layers; import xyz.valnet.hadean.util.Pair; public class JobBoard extends GameObject { @@ -28,30 +34,68 @@ public class JobBoard extends GameObject { return job; } - public void postJob(Job job) { - availableJobs.add(job); + private Camera camera; + + @Override + public void connect() { + camera = get(Camera.class); } - public void rescindJob(Job job) { - if(allocations.values().contains(job)) { - List toFire = new ArrayList(); - - for(Pawn worker : allocations.keySet()) { - if(allocations.get(worker) == job) { - toFire.add(worker); + @Override + public void renderAlpha() { + super.render(); + if(HadeanGame.debugView) { + float opacity = 0.6f; + Assets.flat.pushColor(new Vector4f(1, 0.8f, 0, opacity)); + for(Job job : availableJobs) { + for(Vector2i position : job.getLocations()) { + if(job.isValid()) { + Assets.flat.swapColor(new Vector4f(1, 0.8f, 0, opacity)); + } else { + Assets.flat.swapColor(new Vector4f(1.0f, 0.2f, 0, opacity)); + } + camera.draw(Layers.GROUND_MARKERS, Assets.fillTile, position.asFloat()); } } - - for(Pawn worker : toFire) { - allocations.remove(worker); + Assets.flat.swapColor(new Vector4f(0.2f, 1.0f, 0, opacity)); + for(Job job : allocations.values()) { + for(Vector2i position : job.getLocations()) { + camera.draw(Layers.GROUND_MARKERS, Assets.fillTile, position.asFloat()); + } } + Assets.flat.popColor(); } + } - if(availableJobs.contains(job)) { - availableJobs.remove(job); - } + public Job postSimpleItemRequirementJob(String name, IItemPredicate predicate, IItemReceiver recv) { + Job job = add(new Job(name)); + job.addStep(job.new PickupItemByPredicate(predicate)); + job.addStep(job.new DropoffPredicateAtItemReceiver(recv, predicate)); + postJob(job); + return job; + } - job.close(); + public void postJob(Job job) { + job.registerClosedListener(() -> { + if(allocations.values().contains(job)) { + List toFire = new ArrayList(); + + for(Pawn worker : allocations.keySet()) { + if(allocations.get(worker) == job) { + toFire.add(worker); + } + } + + for(Pawn worker : toFire) { + allocations.remove(worker); + } + } + + if(availableJobs.contains(job)) { + availableJobs.remove(job); + } + }); + availableJobs.add(job); } public boolean jobsAvailableForWorker(Pawn worker) { @@ -97,10 +141,6 @@ public class JobBoard extends GameObject { return null; } - public void completeJob(Job job) { - this.rescindJob(job); - } - public void quitJob(Pawn worker, Job job) { if(!allocations.containsKey(worker)) return; Job foundJob = allocations.get(worker); @@ -137,17 +177,28 @@ public class JobBoard extends GameObject { String takenJobsString = ""; String availableJobsString = ""; + String impossibleJobsString = ""; + + int possibleJobs = 0; + int impossibleJobs = 0; for(Entry allocation : allocations.entrySet()) { takenJobsString += " " + allocation.getKey().getName() + ": " + allocation.getValue().getJobName() + "\n"; } for(Job job : availableJobs) { - availableJobsString += " " + job.getJobName() + "\n"; + if(job.isValid()) { + availableJobsString += " " + job.getJobName() + "\n"; + possibleJobs ++; + } else { + impossibleJobsString += " " + job.getJobName() + "\n"; + impossibleJobs ++; + } } - return "Available Jobs: " + availableJobs.size() + "\n" + availableJobsString + - "Taken Jobs: " + allocations.size() + "\n" + takenJobsString; + return "Available Jobs: " + possibleJobs + "\n" + availableJobsString + + "Taken Jobs: " + allocations.size() + "\n" + takenJobsString + + "Impossible Jobs: " + impossibleJobs + "\n" + impossibleJobsString; } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/SelectionUI.java b/src/main/java/xyz/valnet/hadean/gameobjects/SelectionUI.java index e7bbd25..62a4a57 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/SelectionUI.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/SelectionUI.java @@ -2,40 +2,29 @@ package xyz.valnet.hadean.gameobjects; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import xyz.valnet.engine.graphics.ImmediateUI; import xyz.valnet.engine.math.Vector4f; -import xyz.valnet.engine.scenegraph.GameObject; -import xyz.valnet.engine.scenegraph.IMouseCaptureArea; import xyz.valnet.engine.scenegraph.ITransient; import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer; -import xyz.valnet.hadean.input.Button; -import xyz.valnet.hadean.input.IButtonListener; -import xyz.valnet.hadean.input.SimpleButton; import xyz.valnet.hadean.interfaces.ISelectable; import xyz.valnet.hadean.interfaces.ISelectionChangeListener; -import xyz.valnet.hadean.util.Action; -import xyz.valnet.hadean.util.Assets; import xyz.valnet.hadean.util.Layers; import xyz.valnet.hadean.util.detail.Detail; -public class SelectionUI extends GameObject implements ISelectionChangeListener, IButtonListener, IMouseCaptureArea, ITransient { +public class SelectionUI extends ImmediateUI implements ISelectionChangeListener, ITransient { - private String name = ""; - private String genericName = ""; - private int count = 0; + private class SelectedByType extends HashMap, List> {} + + private int selectedCount = 0; + private String properName; + private String genericName; private List selected = new ArrayList(); - private HashMap selectedTypes = new HashMap(); - private HashMap narrowButtons = new HashMap(); - private HashMap> narrowBuckets = new HashMap>(); - - private static final Button[] ACTIONS_BUTTONS_NULL = new Button[] {}; - - private Button[] actionButtons = ACTIONS_BUTTONS_NULL; + private transient SelectedByType selectedByType = new SelectedByType(); private SelectionLayer selectionManager; + private final int width = 300, height = 200; private final int padding = 10; private final int actionButtonSize = 100; @@ -46,168 +35,44 @@ public class SelectionUI extends GameObject implements ISelectionChangeListener, // exception, where the buttons are attempting to // change while updating. // TODO this could be fixed by delaying button clicks to the next frame. - private List newSelection = null;; + private List newSelection = null; public void start() { selectionManager = get(SelectionLayer.class); selectionManager.subscribe(this); } - public void render() { - if(selected.isEmpty()) return; - - Assets.uiFrame.draw(10, 576 - BottomBar.bottomBarHeight - height - padding, width, height); - - if(selectedTypes.size() == 1) { - if(count == 1) { - Assets.font.drawString(name, 26, 576 - BottomBar.bottomBarHeight - height); - String details = Detail.renderDetails(selected.get(0).getDetails()); - Assets.font.drawString(details, 26, 576 - BottomBar.bottomBarHeight - height + 32); - } else { - Assets.font.drawString("" + count + "x " + genericName, 26, 576 - BottomBar.bottomBarHeight - height); - } - - } else { - } - - } - @Override public void update(float dTime) { if(newSelection != null) { selectionManager.updateSelection(newSelection); newSelection = null; } - - if(selectedTypes.size() == 1) { - } else { - } - } - - private HashMap buttonActionMap = new HashMap(); - - private void addNarrowButton(String str, Button btn) { - narrowButtons.put(str, btn); - add(btn); - } - - private void clearNarrowButtons() { - for(GameObject obj : narrowButtons.values()) { - remove(obj); - } - narrowButtons.clear(); } @Override - public void selectionChanged(List selected) { - this.selected = selected; + public void selectionChanged(List newSelection) { - selectedTypes.clear(); - clearNarrowButtons(); - narrowBuckets.clear(); - buttonActionMap.clear(); - setActionButtons(ACTIONS_BUTTONS_NULL); - for(ISelectable selectable : selected) { - String name = selectable.getClass().getName(); - String[] splitName = name.split("\\."); - String properName = selectable.getName(); - String shortName = splitName[splitName.length - 1]; + selected = newSelection; + selectedByType.clear(); + selectedCount = newSelection.size(); + + for(ISelectable selectable : newSelection) { + Class clazz = selectable.getClass(); - if(selectedTypes.containsKey(name)) { - selectedTypes.replace(name, selectedTypes.get(name) + 1); - Button btn = narrowButtons.get(name); - List items = narrowBuckets.get(btn); - items.add(selectable); - btn.setText("" + items.size() + "x " + shortName); - count ++; - } else { - Button btn = new SimpleButton(properName, 20, 576 - BottomBar.bottomBarHeight - height + 30 * selectedTypes.size(), width - padding * 2, 24, Layers.GENERAL_UI_INTERACTABLE); - btn.registerClickListener(this); - selectedTypes.put(name, 1); - addNarrowButton(name, btn); - List list = new ArrayList(); - list.add(selectable); - narrowBuckets.put(btn, list); - count = 1; - this.name = properName; - this.genericName = shortName; + properName = selectable.getName(); + genericName = selectable.getGenericName(); + + if(!selectedByType.containsKey(clazz)) { + selectedByType.put(clazz, new ArrayList()); } - } - if(selectedTypes.size() == 1) { - createActionButtons(); - } - if(selectedTypes.size() <= 1) { - clearNarrowButtons(); - } - } - private void createActionButtons() { - - buttonActionMap.clear(); - setActionButtons(ACTIONS_BUTTONS_NULL); - - Set actionSet = new HashSet(); - for(ISelectable selectable : selected) { - for(Action action : selectable.getActions()) { - actionSet.add(action); - } - } - - Action[] actions = new Action[actionSet.size()]; - actionSet.toArray(actions); - - Button[] actionButtons = new Button[actions.length]; - for(int i = 0; i < actions.length; i ++) { - actionButtons[i] = new SimpleButton(actions[i].name, width + padding * 2 + i * (actionButtonSize + padding), 576 - padding - actionButtonSize - BottomBar.bottomBarHeight, actionButtonSize, actionButtonSize, Layers.GENERAL_UI_INTERACTABLE); - actionButtons[i].registerClickListener(this); - buttonActionMap.put(actionButtons[i], actions[i]); - } - setActionButtons(actionButtons); - } - - private void setActionButtons(Button[] buttons) { - for(Button btn : this.actionButtons) { - remove(btn); - } - this.actionButtons = buttons; - for(Button btn : this.actionButtons) { - add(btn); + selectedByType.get(clazz).add(selectable); } } @Override - public void click(Button target) { - if(narrowBuckets.containsKey(target)) { - newSelection = narrowBuckets.get(target); - } else if(buttonActionMap.containsKey(target)) { - Action action = buttonActionMap.get(target); - for(ISelectable selectable : selected) { - selectable.runAction(action); - } - createActionButtons(); - } - } - - @Override - public void mouseEnter() { - } - - @Override - public void mouseLeave() { - } - - @Override - public void mouseDown(int button) { - return; - } - - @Override - public void mouseUp(int button) { - - } - - @Override - public Vector4f getBox() { + public Vector4f getGuiBox() { if(selected.isEmpty()) return Vector4f.zero; return new Vector4f(10, 576 - BottomBar.bottomBarHeight - height - padding, width, height); } @@ -216,4 +81,39 @@ public class SelectionUI extends GameObject implements ISelectionChangeListener, public float getLayer() { return Layers.GENERAL_UI; } + + @Override + protected void gui() { + if(selected.isEmpty()) return; + + if(selectedByType.size() == 1) { + if(selectedCount == 1) { + text(properName + "\n "); + group(); + Detail[] details = selected.get(0).getDetails(); + if(details.length == 0) { + text("No details available."); + } else for(Detail detail : details) { + text(detail.toString(15)); + } + groupEnd(); + } else { + text("" + selectedCount + "x " + genericName); + } + } else { + text(this.selected.size() + " items selected"); + text(""); + + for(var entry : selectedByType.entrySet()) { + List list = entry.getValue(); + int count = list.size(); + if(count <= 0) continue; + String name = list.get(0).getGenericName(); + + if(button(name, count + "x " + name)) { + newSelection = list; + } + } + } + } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/BuildLayer.java b/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/BuildLayer.java index 6629f7c..61bb9aa 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/BuildLayer.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/BuildLayer.java @@ -129,7 +129,7 @@ public class BuildLayer extends GameObject implements IMouseCaptureArea, ITransi } @Override - public Vector4f getBox() { + public Vector4f getGuiBox() { return active ? new Vector4f(0, 0, 1024, 576) : Vector4f.zero; } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/SelectionLayer.java b/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/SelectionLayer.java index 78c7d0d..614ad0d 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/SelectionLayer.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/SelectionLayer.java @@ -181,7 +181,7 @@ public class SelectionLayer extends GameObject implements IMouseCaptureArea, ITr } @Override - public Vector4f getBox() { + public Vector4f getGuiBox() { return new Vector4f(0, 0, 1000, 1000); } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/ui/Popup.java b/src/main/java/xyz/valnet/hadean/gameobjects/ui/Popup.java new file mode 100644 index 0000000..1c37266 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/gameobjects/ui/Popup.java @@ -0,0 +1,34 @@ +package xyz.valnet.hadean.gameobjects.ui; + +import xyz.valnet.engine.graphics.ImmediateUI; +import xyz.valnet.engine.math.Vector4f; + +public class Popup extends ImmediateUI { + + @Override + public Vector4f getGuiBox() { + return new Vector4f(256, 100, 512, 300); + } + + @Override + protected void gui() { + header(" Popup Test"); + + text("1\n1.5"); + text("2"); + + group(); + text("This should be in a frame!"); + text("And this!"); + groupEnd(); + + text("But not this..."); + + if(button("Click Me!")) { + System.out.println("The Event!"); + } + + text("This after button..."); + } + +} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java b/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java index acf356d..2136e91 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java @@ -289,7 +289,7 @@ public class BuildTab extends Tab implements ISelectionChangeListener, IMouseCap public void mouseUp(int button) {} @Override - public Vector4f getBox() { + public Vector4f getGuiBox() { return new Vector4f(padding, 576 - BottomBar.bottomBarHeight - padding - height, width, height); } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Tree.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Tree.java index 9a79062..7530796 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Tree.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Tree.java @@ -61,7 +61,7 @@ public class Tree extends WorldObject implements ITileThing, ISelectable, IWorka if(chopJob == null) { chopJob = get(JobBoard.class).postSimpleWorkJob("Chop Tree", this); } else { - get(JobBoard.class).rescindJob(chopJob); + chopJob.close(); chopJob = null; } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/Agent.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/Agent.java index 67b6653..7ff302f 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/Agent.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/Agent.java @@ -159,8 +159,8 @@ public abstract class Agent extends WorldObject implements ISelectable { @Override public void renderAlpha() { if(!HadeanGame.debugView) return; - Drawing.setLayer(Layers.GENERAL_UI); - Assets.flat.pushColor(Vector4f.opacity(0.4f)); + Drawing.setLayer(Layers.GROUND_MARKERS); + Assets.flat.pushColor(Vector4f.opacity(0.6f)); if(path != null) { int count = 0; for(Node node : path) { @@ -183,7 +183,6 @@ public abstract class Agent extends WorldObject implements ISelectable { glEnd(); count ++; } - Assets.flat.swapColor(Vector4f.opacity(0.6f)); Assets.selectionFrame.draw( camera.world2Screen( diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Construction.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Construction.java new file mode 100644 index 0000000..17a70af --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Construction.java @@ -0,0 +1,123 @@ +package xyz.valnet.hadean.gameobjects.worldobjects.constructions; + +import java.util.ArrayList; +import java.util.List; + +import xyz.valnet.engine.math.Vector2i; +import xyz.valnet.hadean.gameobjects.Job; +import xyz.valnet.hadean.gameobjects.JobBoard; +import xyz.valnet.hadean.gameobjects.worldobjects.Buildable; +import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder; +import xyz.valnet.hadean.gameobjects.worldobjects.items.Item; +import xyz.valnet.hadean.interfaces.IItemPredicate; +import xyz.valnet.hadean.interfaces.IItemReceiver; +import xyz.valnet.hadean.interfaces.IWorkable; + +public abstract class Construction extends Buildable implements IItemReceiver { + + private float work = 0; + + private List containedItems = new ArrayList(); + + protected abstract IItemPredicate getBuildingMaterial(); + protected abstract int getBuildingMaterialCount(); + protected Vector2i getDimensions() { + return new Vector2i(1, 1); + } + + private final boolean isBuildingMaterialSatisfied() { + return containedItems.size() >= getBuildingMaterialCount(); + } + + private void postNextJob() { + if(!isBuildingMaterialSatisfied()) { + Job job = get(JobBoard.class).postSimpleItemRequirementJob( + "Haul items to building", + getBuildingMaterial(), + this + ); + job.registerClosedListener(() -> { + postNextJob(); + }); + return; + } + if(!isBuilt()) { + Job job = get(JobBoard.class).postSimpleWorkJob( + "Build " + getName(), + new IWorkable() { + @Override + public boolean doWork(float dTime) { + work += dTime; + return isBuilt(); + } + + @Override + public Vector2i[] getWorkablePositions() { + return getWorldBox().toXYWH().asInt().getBorders(); + } + + @Override + public String getJobName() { + return "Build " + getName(); + } + } + ); + job.registerClosedListener(() -> { + postNextJob(); + }); + return; + } + } + + protected float getMaxWork() { + return 1000; + } + + public boolean isBuilt() { + return work >= getMaxWork(); + } + + @Override + public void create() { + super.create(); + postNextJob(); + } + + @Override + public abstract boolean isWalkable(); + + @Override + public boolean shouldRemove() { + return false; + } + + @Override + public void onRemove() { + + } + + @Override + public abstract String getName(); + + protected final boolean isBuilding() { + return isBuildingMaterialSatisfied(); + } + + protected final float getBuildProgress() { + return work / getMaxWork(); + } + + @Override + public boolean receive(Item item) { + if(item == null) return false; + if(!item.matches(Boulder.BOULDER_PREDICATE)) return false; + remove(item); + // boulders ++; + return true; + } + + @Override + public Vector2i[] getItemDropoffLocations() { + return getWorldBox().toXYWH().asInt().getBorders(); + } +} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Quarry.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Quarry.java index ef74e1f..601421a 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Quarry.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Quarry.java @@ -4,45 +4,19 @@ import xyz.valnet.engine.math.Vector2i; import xyz.valnet.engine.math.Vector4f; import xyz.valnet.hadean.gameobjects.Job; import xyz.valnet.hadean.gameobjects.JobBoard; -import xyz.valnet.hadean.gameobjects.worldobjects.Buildable; import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder; import xyz.valnet.hadean.interfaces.BuildableMetadata; +import xyz.valnet.hadean.interfaces.IItemPredicate; import xyz.valnet.hadean.interfaces.IWorkable; import xyz.valnet.hadean.util.Assets; import xyz.valnet.hadean.util.Layers; @BuildableMetadata(category = "Buildings", name = "Quarry", type = BuildableMetadata.Type.SINGLE) -public class Quarry extends Buildable { +public class Quarry extends Construction { private float work = 0; - private static float MAX_WORK = 10000; - private Job digJob = null; - @Override - public void create() { - super.create(); - get(JobBoard.class).postSimpleWorkJob("Build Quarry", new IWorkable() { - @Override - public boolean doWork(float dTime) { - work += dTime; - return isBuilt(); - } - - @Override - public Vector2i[] getWorkablePositions() { - return new Vector2i[] { - getWorldPosition().xy().south().east() - }; - } - - @Override - public String getJobName() { - return "Build Quarry"; - } - }); - } - @Override public void render() { if(isBuilt()) { @@ -53,14 +27,13 @@ public class Quarry extends Buildable { } } else { - float p = work / MAX_WORK; float b = 4; Assets.flat.pushColor(new Vector4f(b, b, b, 0.5f)); camera.draw(Layers.GROUND, Assets.quarry, getWorldPosition()); Assets.flat.popColor(); - camera.drawProgressBar(p, getWorldBox()); + camera.drawProgressBar(getBuildProgress(), getWorldBox()); } } @@ -76,6 +49,9 @@ public class Quarry extends Buildable { if (digJob != null) return; if (terrain.getTile(getWorldPosition().xy().south().east()).has(Boulder.class)) return; + + System.out.println("Dig job?"); + digJob = get(JobBoard.class) .postSimpleWorkJob("Mine at Quarry", new IWorkable() { @@ -118,10 +94,6 @@ public class Quarry extends Buildable { tryCreateDigJob(); } - private boolean isBuilt() { - return work >= MAX_WORK; - } - @Override public boolean isWalkable() { return true; @@ -140,4 +112,14 @@ public class Quarry extends Buildable { public String getName() { return "Quarry"; } + + @Override + protected IItemPredicate getBuildingMaterial() { + return null; + } + + @Override + protected int getBuildingMaterialCount() { + return 0; + } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Item.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Item.java index d49010a..c979196 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Item.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Item.java @@ -69,7 +69,7 @@ public abstract class Item extends WorldObject implements ISelectable, ITileThin private void cancelHaul() { if(haulJob == null) return; - jobboard.rescindJob(haulJob); + haulJob.close(); haulJob = null; } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/pawn/Pawn.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/pawn/Pawn.java index 193c4be..94195f5 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/pawn/Pawn.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/pawn/Pawn.java @@ -147,6 +147,11 @@ public class Pawn extends Agent { return name; } + @Override + public String getGenericName() { + return "Pawn"; + } + @Override public Vector4f getWorldBox() { Vector2f pos = getCalculatedPosition(); diff --git a/src/main/java/xyz/valnet/hadean/input/Button.java b/src/main/java/xyz/valnet/hadean/input/Button.java index ce388f3..d1f2dd2 100644 --- a/src/main/java/xyz/valnet/hadean/input/Button.java +++ b/src/main/java/xyz/valnet/hadean/input/Button.java @@ -53,22 +53,29 @@ public class Button extends GameObject implements IMouseCaptureArea, ITransient return text; } - public void setText(String text) { + public Button setText(String text) { + if(text == this.text) return this; this.text = text; Vector4i measuredText = Assets.font.measure(text); textWidth = measuredText.x; textHeight = measuredText.y; + return this; + } + + public Button setLayer(float layer) { + this.layer = layer; + return this; } @Override public void render() { Drawing.setLayer(layer); if(state == HOVER) { - frameHover.draw(box.x, box.y, box.z, box.w); + Assets.uiFrameLight.draw(box.x, box.y, box.z, box.w); } else if(state == ACTIVE) { - frameActive.draw(box.x, box.y, box.z, box.w); + Assets.uiFrameDark.draw(box.x, box.y, box.z, box.w); } else { - frame.draw(box.x, box.y, box.z, box.w); + Assets.uiFrame.draw(box.x, box.y, box.z, box.w); } Assets.flat.pushColor(Vector4f.black); @@ -80,6 +87,20 @@ public class Button extends GameObject implements IMouseCaptureArea, ITransient Assets.flat.popColor(); } + public Button setPosition(int x, int y) { + this.x = x; + this.y = y; + box = new Vector4i(x, y, width, height); + return this; + } + + public Button setSize(int w, int h) { + this.width = w; + this.height = h; + box = new Vector4i(x, y, w, h); + return this; + } + // public void draw(int x, int y, int w, int h) { // this.x = x; // this.y = y; @@ -201,7 +222,7 @@ public class Button extends GameObject implements IMouseCaptureArea, ITransient } @Override - public Vector4f getBox() { + public Vector4f getGuiBox() { return new Vector4f(x, y, width, height); } diff --git a/src/main/java/xyz/valnet/hadean/input/IButtonListener.java b/src/main/java/xyz/valnet/hadean/input/IButtonListener.java index b3469b9..c4b827c 100644 --- a/src/main/java/xyz/valnet/hadean/input/IButtonListener.java +++ b/src/main/java/xyz/valnet/hadean/input/IButtonListener.java @@ -1,5 +1,8 @@ package xyz.valnet.hadean.input; -public interface IButtonListener { +import java.io.Serializable; + +@FunctionalInterface +public interface IButtonListener extends Serializable { public void click(Button target); } \ No newline at end of file diff --git a/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java b/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java index 78e857c..cdab96c 100644 --- a/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java +++ b/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java @@ -31,4 +31,7 @@ public interface ISelectable { return Priority.NORMAL; } public String getName(); + public default String getGenericName() { + return getName(); + } } \ No newline at end of file diff --git a/src/main/java/xyz/valnet/hadean/scenes/GameScene.java b/src/main/java/xyz/valnet/hadean/scenes/GameScene.java index df1aaf1..e37d02e 100644 --- a/src/main/java/xyz/valnet/hadean/scenes/GameScene.java +++ b/src/main/java/xyz/valnet/hadean/scenes/GameScene.java @@ -11,6 +11,7 @@ import xyz.valnet.hadean.gameobjects.Terrain; import xyz.valnet.hadean.gameobjects.inputlayer.BuildLayer; import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer; import xyz.valnet.hadean.gameobjects.ui.HoverQuery; +import xyz.valnet.hadean.gameobjects.ui.Popup; import xyz.valnet.hadean.gameobjects.ui.tabs.BuildTab; import xyz.valnet.hadean.gameobjects.ui.tabs.JobBoardTab; import xyz.valnet.hadean.gameobjects.ui.tabs.LoadTab; @@ -42,7 +43,7 @@ public class GameScene extends SceneGraph { for(int i = 0; i < 5; i ++) { objects.add(new Pawn()); } - + objects.add(new SelectionLayer()); objects.add(new SelectionUI()); @@ -57,6 +58,8 @@ public class GameScene extends SceneGraph { objects.add(new MenuTab()); objects.add(new SaveTab()); objects.add(new LoadTab()); + + // objects.add(new Popup()); } } diff --git a/src/main/java/xyz/valnet/hadean/util/Assets.java b/src/main/java/xyz/valnet/hadean/util/Assets.java index 0a115de..d952e28 100644 --- a/src/main/java/xyz/valnet/hadean/util/Assets.java +++ b/src/main/java/xyz/valnet/hadean/util/Assets.java @@ -21,6 +21,7 @@ public class Assets { public static final Texture atlas; public static final Font font; + public static final Font bigFont; public static final Font miniFont; public static final Tile9 redFrame; public static final Tile9 frame; @@ -52,6 +53,7 @@ public class Assets { public static final Sprite lilPickaxe; public static final Sprite testTile; public static final Sprite quarry; + public static final Sprite fillTile; public static final SimpleShader flat; @@ -198,7 +200,8 @@ public class Assets { charset.put('\\', new Sprite(atlas, 208, 32, 8, 16)); charset.put('♥', new Sprite(atlas, 216, 32, 8, 16)); charset.put('|', new Sprite(atlas, 224, 32, 8, 16)); - font = new Font(charset, 8, 16); + font = new Font(charset, 8, 16, 1); + bigFont = new Font(charset, 8, 16, 2); Map miniCharset = new HashMap(); miniCharset.put('1', new Sprite(atlas, 0, 112, 4, 5)); @@ -211,7 +214,7 @@ public class Assets { miniCharset.put('8', new Sprite(atlas, 28, 112, 4, 5)); miniCharset.put('9', new Sprite(atlas, 32, 112, 4, 5)); miniCharset.put('0', new Sprite(atlas, 36, 112, 4, 5)); - miniFont = new Font(miniCharset, 4, 5); + miniFont = new Font(miniCharset, 4, 5, 1); frame = new Tile9( new Sprite(atlas, 24, 88, 8, 8), @@ -272,6 +275,8 @@ public class Assets { new Sprite(atlas, 44, 64, 1, 1), new Sprite(atlas, 44, 64, 1, 1) ); + + fillTile = new Sprite(atlas, 0, 88, 8, 8); uiFrame = new Tile9( new Sprite(atlas, 32, 80, 1, 1), diff --git a/src/main/java/xyz/valnet/hadean/util/Layers.java b/src/main/java/xyz/valnet/hadean/util/Layers.java index 9ce894c..04d3000 100644 --- a/src/main/java/xyz/valnet/hadean/util/Layers.java +++ b/src/main/java/xyz/valnet/hadean/util/Layers.java @@ -8,6 +8,7 @@ public class Layers { public static final float BACKGROUND = current ++; public static final float TILES = current ++; + public static final float GROUND_MARKERS = current ++; public static final float GROUND = current ++; public static final float AIR = current ++; public static final float PAWNS = Layers.AIR + 0.001f;