better jobs, quarry, immediateui

bottom-bar
Ivory 2023-01-15 04:11:17 -05:00
parent 58e4c02dc1
commit 58618b97bc
25 changed files with 709 additions and 279 deletions

View File

@ -6,50 +6,57 @@ import xyz.valnet.engine.math.Vector4i;
public class Font { public class Font {
private Map<Character, Sprite> charset; private Map<Character, Sprite> charset;
private final int w, h; private final int w, h;
public Font(Map<Character, Sprite> charset, int w, int h) { private final int scale;
this.charset = charset;
this.w = w;
this.h = h;
}
public void drawString(String str, int x, int y) { public Font(Map<Character, Sprite> charset, int w, int h, int scale) {
int cursorX = x; this.charset = charset;
int cursorY = y; 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()) { Sprite s;
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;
}
}
public Vector4i measure(String text) { for(char c : str.toCharArray()) {
String[] lines = text.split("\n"); if(c == '\n') {
int longest = 0; cursorY += h;
int c = 0; cursorX = x;
for(String line : lines) { continue;
c = line.length();
if(c > longest) longest = c;
} }
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;
}
} }

View File

@ -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<StackingContext> contextStack;
private transient Map<String, Button> buttons = new HashMap<String, Button>();
private Set<String> usedButtonId = new HashSet<String>();
private transient Map<Button, Integer> clicks = new HashMap<Button, Integer>();
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<StackingContext>();
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<String> buttonIdsToRemove = new ArrayList<String>();
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;
}
}
}

View File

@ -58,4 +58,15 @@ public class Vector4f implements Serializable {
return new Vector4i((int)x, (int)y, (int)z, (int)w); 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);
}
} }

View File

@ -24,4 +24,36 @@ public class Vector4i implements Serializable {
return new Vector2i(x, y); 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;
}
} }

View File

@ -8,6 +8,6 @@ public interface IMouseCaptureArea {
public void mouseDown(int button); public void mouseDown(int button);
public void mouseUp(int button); public void mouseUp(int button);
public Vector4f getBox(); public Vector4f getGuiBox();
public float getLayer(); public float getLayer();
} }

View File

@ -83,7 +83,7 @@ public abstract class SceneGraph implements IScene {
} }
}); });
for(IMouseCaptureArea listener : mouseListeners) { 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(currentlyEntered) {
if(listener != hoveredMouseListener) { if(listener != hoveredMouseListener) {
if(hoveredMouseListener != null) { if(hoveredMouseListener != null) {

View File

@ -155,6 +155,7 @@ public class Job extends GameObject {
private List<JobStep> steps; private List<JobStep> steps;
private String name; private String name;
private int step; private int step;
private boolean hasClosed = false;
public void reset() { public void reset() {
step = 0; step = 0;
@ -171,17 +172,14 @@ public class Job extends GameObject {
public Vector2i[] getLocations() { public Vector2i[] getLocations() {
if(steps.size() == 0) throw new Error("Cannot get location of job with no steps"); 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(); return step.getLocations();
} }
public void nextStep() { public void nextStep() {
step ++; step ++;
if(isCompleted()) { if(isCompleted()) {
get(JobBoard.class).completeJob(this); close();
for(Callback callback : closedListeners) {
callback.apply();
}
remove(this); remove(this);
} }
} }
@ -205,6 +203,8 @@ public class Job extends GameObject {
} }
public void close() { public void close() {
if(hasClosed) return;
hasClosed = true;
for(Callback callback : closedListeners) { for(Callback callback : closedListeners) {
callback.apply(); callback.apply();
} }

View File

@ -10,9 +10,15 @@ import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import xyz.valnet.engine.math.Vector2i; import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.math.Vector4f;
import xyz.valnet.engine.scenegraph.GameObject; import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.hadean.HadeanGame;
import xyz.valnet.hadean.gameobjects.worldobjects.pawn.Pawn; 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.interfaces.IWorkable;
import xyz.valnet.hadean.util.Assets;
import xyz.valnet.hadean.util.Layers;
import xyz.valnet.hadean.util.Pair; import xyz.valnet.hadean.util.Pair;
public class JobBoard extends GameObject { public class JobBoard extends GameObject {
@ -28,30 +34,68 @@ public class JobBoard extends GameObject {
return job; return job;
} }
public void postJob(Job job) { private Camera camera;
availableJobs.add(job);
@Override
public void connect() {
camera = get(Camera.class);
} }
public void rescindJob(Job job) { @Override
if(allocations.values().contains(job)) { public void renderAlpha() {
List<Pawn> toFire = new ArrayList<Pawn>(); super.render();
if(HadeanGame.debugView) {
for(Pawn worker : allocations.keySet()) { float opacity = 0.6f;
if(allocations.get(worker) == job) { Assets.flat.pushColor(new Vector4f(1, 0.8f, 0, opacity));
toFire.add(worker); 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());
} }
} }
Assets.flat.swapColor(new Vector4f(0.2f, 1.0f, 0, opacity));
for(Pawn worker : toFire) { for(Job job : allocations.values()) {
allocations.remove(worker); for(Vector2i position : job.getLocations()) {
camera.draw(Layers.GROUND_MARKERS, Assets.fillTile, position.asFloat());
}
} }
Assets.flat.popColor();
} }
}
if(availableJobs.contains(job)) { public Job postSimpleItemRequirementJob(String name, IItemPredicate predicate, IItemReceiver recv) {
availableJobs.remove(job); 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<Pawn> toFire = new ArrayList<Pawn>();
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) { public boolean jobsAvailableForWorker(Pawn worker) {
@ -97,10 +141,6 @@ public class JobBoard extends GameObject {
return null; return null;
} }
public void completeJob(Job job) {
this.rescindJob(job);
}
public void quitJob(Pawn worker, Job job) { public void quitJob(Pawn worker, Job job) {
if(!allocations.containsKey(worker)) return; if(!allocations.containsKey(worker)) return;
Job foundJob = allocations.get(worker); Job foundJob = allocations.get(worker);
@ -137,17 +177,28 @@ public class JobBoard extends GameObject {
String takenJobsString = ""; String takenJobsString = "";
String availableJobsString = ""; String availableJobsString = "";
String impossibleJobsString = "";
int possibleJobs = 0;
int impossibleJobs = 0;
for(Entry<Pawn, Job> allocation : allocations.entrySet()) { for(Entry<Pawn, Job> allocation : allocations.entrySet()) {
takenJobsString += " " + allocation.getKey().getName() + ": " + allocation.getValue().getJobName() + "\n"; takenJobsString += " " + allocation.getKey().getName() + ": " + allocation.getValue().getJobName() + "\n";
} }
for(Job job : availableJobs) { 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 + return "Available Jobs: " + possibleJobs + "\n" + availableJobsString +
"Taken Jobs: " + allocations.size() + "\n" + takenJobsString; "Taken Jobs: " + allocations.size() + "\n" + takenJobsString +
"Impossible Jobs: " + impossibleJobs + "\n" + impossibleJobsString;
} }
} }

View File

@ -2,40 +2,29 @@ package xyz.valnet.hadean.gameobjects;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import xyz.valnet.engine.graphics.ImmediateUI;
import xyz.valnet.engine.math.Vector4f; 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.engine.scenegraph.ITransient;
import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer; 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.ISelectable;
import xyz.valnet.hadean.interfaces.ISelectionChangeListener; 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.Layers;
import xyz.valnet.hadean.util.detail.Detail; 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 class SelectedByType extends HashMap<Class<? extends ISelectable>, List<ISelectable>> {}
private String genericName = "";
private int count = 0; private int selectedCount = 0;
private String properName;
private String genericName;
private List<ISelectable> selected = new ArrayList<ISelectable>(); private List<ISelectable> selected = new ArrayList<ISelectable>();
private HashMap<String, Integer> selectedTypes = new HashMap<String, Integer>(); private transient SelectedByType selectedByType = new SelectedByType();
private HashMap<String, Button> narrowButtons = new HashMap<String, Button>();
private HashMap<Button, List<ISelectable>> narrowBuckets = new HashMap<Button, List<ISelectable>>();
private static final Button[] ACTIONS_BUTTONS_NULL = new Button[] {};
private Button[] actionButtons = ACTIONS_BUTTONS_NULL;
private SelectionLayer selectionManager; private SelectionLayer selectionManager;
private final int width = 300, height = 200; private final int width = 300, height = 200;
private final int padding = 10; private final int padding = 10;
private final int actionButtonSize = 100; private final int actionButtonSize = 100;
@ -46,168 +35,44 @@ public class SelectionUI extends GameObject implements ISelectionChangeListener,
// exception, where the buttons are attempting to // exception, where the buttons are attempting to
// change while updating. // change while updating.
// TODO this could be fixed by delaying button clicks to the next frame. // TODO this could be fixed by delaying button clicks to the next frame.
private List<ISelectable> newSelection = null;; private List<ISelectable> newSelection = null;
public void start() { public void start() {
selectionManager = get(SelectionLayer.class); selectionManager = get(SelectionLayer.class);
selectionManager.subscribe(this); 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 @Override
public void update(float dTime) { public void update(float dTime) {
if(newSelection != null) { if(newSelection != null) {
selectionManager.updateSelection(newSelection); selectionManager.updateSelection(newSelection);
newSelection = null; newSelection = null;
} }
if(selectedTypes.size() == 1) {
} else {
}
}
private HashMap<Button, Action> buttonActionMap = new HashMap<Button, Action>();
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 @Override
public void selectionChanged(List<ISelectable> selected) { public void selectionChanged(List<ISelectable> newSelection) {
this.selected = selected;
selectedTypes.clear(); selected = newSelection;
clearNarrowButtons(); selectedByType.clear();
narrowBuckets.clear(); selectedCount = newSelection.size();
buttonActionMap.clear();
setActionButtons(ACTIONS_BUTTONS_NULL); for(ISelectable selectable : newSelection) {
for(ISelectable selectable : selected) { Class<? extends ISelectable> clazz = selectable.getClass();
String name = selectable.getClass().getName();
String[] splitName = name.split("\\.");
String properName = selectable.getName();
String shortName = splitName[splitName.length - 1];
if(selectedTypes.containsKey(name)) { properName = selectable.getName();
selectedTypes.replace(name, selectedTypes.get(name) + 1); genericName = selectable.getGenericName();
Button btn = narrowButtons.get(name);
List<ISelectable> items = narrowBuckets.get(btn); if(!selectedByType.containsKey(clazz)) {
items.add(selectable); selectedByType.put(clazz, new ArrayList<ISelectable>());
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<ISelectable> list = new ArrayList<ISelectable>();
list.add(selectable);
narrowBuckets.put(btn, list);
count = 1;
this.name = properName;
this.genericName = shortName;
} }
}
if(selectedTypes.size() == 1) {
createActionButtons();
}
if(selectedTypes.size() <= 1) {
clearNarrowButtons();
}
}
private void createActionButtons() { selectedByType.get(clazz).add(selectable);
buttonActionMap.clear();
setActionButtons(ACTIONS_BUTTONS_NULL);
Set<Action> actionSet = new HashSet<Action>();
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);
} }
} }
@Override @Override
public void click(Button target) { public Vector4f getGuiBox() {
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() {
if(selected.isEmpty()) return Vector4f.zero; if(selected.isEmpty()) return Vector4f.zero;
return new Vector4f(10, 576 - BottomBar.bottomBarHeight - height - padding, width, height); 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() { public float getLayer() {
return Layers.GENERAL_UI; 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<ISelectable> 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;
}
}
}
}
} }

View File

@ -129,7 +129,7 @@ public class BuildLayer extends GameObject implements IMouseCaptureArea, ITransi
} }
@Override @Override
public Vector4f getBox() { public Vector4f getGuiBox() {
return active ? new Vector4f(0, 0, 1024, 576) : Vector4f.zero; return active ? new Vector4f(0, 0, 1024, 576) : Vector4f.zero;
} }

View File

@ -181,7 +181,7 @@ public class SelectionLayer extends GameObject implements IMouseCaptureArea, ITr
} }
@Override @Override
public Vector4f getBox() { public Vector4f getGuiBox() {
return new Vector4f(0, 0, 1000, 1000); return new Vector4f(0, 0, 1000, 1000);
} }

View File

@ -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...");
}
}

View File

@ -289,7 +289,7 @@ public class BuildTab extends Tab implements ISelectionChangeListener, IMouseCap
public void mouseUp(int button) {} public void mouseUp(int button) {}
@Override @Override
public Vector4f getBox() { public Vector4f getGuiBox() {
return new Vector4f(padding, 576 - BottomBar.bottomBarHeight - padding - height, width, height); return new Vector4f(padding, 576 - BottomBar.bottomBarHeight - padding - height, width, height);
} }

View File

@ -61,7 +61,7 @@ public class Tree extends WorldObject implements ITileThing, ISelectable, IWorka
if(chopJob == null) { if(chopJob == null) {
chopJob = get(JobBoard.class).postSimpleWorkJob("Chop Tree", this); chopJob = get(JobBoard.class).postSimpleWorkJob("Chop Tree", this);
} else { } else {
get(JobBoard.class).rescindJob(chopJob); chopJob.close();
chopJob = null; chopJob = null;
} }
} }

View File

@ -159,8 +159,8 @@ public abstract class Agent extends WorldObject implements ISelectable {
@Override @Override
public void renderAlpha() { public void renderAlpha() {
if(!HadeanGame.debugView) return; if(!HadeanGame.debugView) return;
Drawing.setLayer(Layers.GENERAL_UI); Drawing.setLayer(Layers.GROUND_MARKERS);
Assets.flat.pushColor(Vector4f.opacity(0.4f)); Assets.flat.pushColor(Vector4f.opacity(0.6f));
if(path != null) { if(path != null) {
int count = 0; int count = 0;
for(Node node : path) { for(Node node : path) {
@ -183,7 +183,6 @@ public abstract class Agent extends WorldObject implements ISelectable {
glEnd(); glEnd();
count ++; count ++;
} }
Assets.flat.swapColor(Vector4f.opacity(0.6f));
Assets.selectionFrame.draw( Assets.selectionFrame.draw(
camera.world2Screen( camera.world2Screen(

View File

@ -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<Item> containedItems = new ArrayList<Item>();
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();
}
}

View File

@ -4,45 +4,19 @@ import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.math.Vector4f; import xyz.valnet.engine.math.Vector4f;
import xyz.valnet.hadean.gameobjects.Job; import xyz.valnet.hadean.gameobjects.Job;
import xyz.valnet.hadean.gameobjects.JobBoard; 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.Boulder;
import xyz.valnet.hadean.interfaces.BuildableMetadata; import xyz.valnet.hadean.interfaces.BuildableMetadata;
import xyz.valnet.hadean.interfaces.IItemPredicate;
import xyz.valnet.hadean.interfaces.IWorkable; import xyz.valnet.hadean.interfaces.IWorkable;
import xyz.valnet.hadean.util.Assets; import xyz.valnet.hadean.util.Assets;
import xyz.valnet.hadean.util.Layers; import xyz.valnet.hadean.util.Layers;
@BuildableMetadata(category = "Buildings", name = "Quarry", type = BuildableMetadata.Type.SINGLE) @BuildableMetadata(category = "Buildings", name = "Quarry", type = BuildableMetadata.Type.SINGLE)
public class Quarry extends Buildable { public class Quarry extends Construction {
private float work = 0; private float work = 0;
private static float MAX_WORK = 10000;
private Job digJob = null; 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 @Override
public void render() { public void render() {
if(isBuilt()) { if(isBuilt()) {
@ -53,14 +27,13 @@ public class Quarry extends Buildable {
} }
} else { } else {
float p = work / MAX_WORK;
float b = 4; float b = 4;
Assets.flat.pushColor(new Vector4f(b, b, b, 0.5f)); Assets.flat.pushColor(new Vector4f(b, b, b, 0.5f));
camera.draw(Layers.GROUND, Assets.quarry, getWorldPosition()); camera.draw(Layers.GROUND, Assets.quarry, getWorldPosition());
Assets.flat.popColor(); 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 (digJob != null) return;
if (terrain.getTile(getWorldPosition().xy().south().east()).has(Boulder.class)) return; if (terrain.getTile(getWorldPosition().xy().south().east()).has(Boulder.class)) return;
System.out.println("Dig job?");
digJob = get(JobBoard.class) digJob = get(JobBoard.class)
.postSimpleWorkJob("Mine at Quarry", new IWorkable() { .postSimpleWorkJob("Mine at Quarry", new IWorkable() {
@ -118,10 +94,6 @@ public class Quarry extends Buildable {
tryCreateDigJob(); tryCreateDigJob();
} }
private boolean isBuilt() {
return work >= MAX_WORK;
}
@Override @Override
public boolean isWalkable() { public boolean isWalkable() {
return true; return true;
@ -140,4 +112,14 @@ public class Quarry extends Buildable {
public String getName() { public String getName() {
return "Quarry"; return "Quarry";
} }
@Override
protected IItemPredicate getBuildingMaterial() {
return null;
}
@Override
protected int getBuildingMaterialCount() {
return 0;
}
} }

View File

@ -69,7 +69,7 @@ public abstract class Item extends WorldObject implements ISelectable, ITileThin
private void cancelHaul() { private void cancelHaul() {
if(haulJob == null) return; if(haulJob == null) return;
jobboard.rescindJob(haulJob); haulJob.close();
haulJob = null; haulJob = null;
} }

View File

@ -147,6 +147,11 @@ public class Pawn extends Agent {
return name; return name;
} }
@Override
public String getGenericName() {
return "Pawn";
}
@Override @Override
public Vector4f getWorldBox() { public Vector4f getWorldBox() {
Vector2f pos = getCalculatedPosition(); Vector2f pos = getCalculatedPosition();

View File

@ -53,22 +53,29 @@ public class Button extends GameObject implements IMouseCaptureArea, ITransient
return text; return text;
} }
public void setText(String text) { public Button setText(String text) {
if(text == this.text) return this;
this.text = text; this.text = text;
Vector4i measuredText = Assets.font.measure(text); Vector4i measuredText = Assets.font.measure(text);
textWidth = measuredText.x; textWidth = measuredText.x;
textHeight = measuredText.y; textHeight = measuredText.y;
return this;
}
public Button setLayer(float layer) {
this.layer = layer;
return this;
} }
@Override @Override
public void render() { public void render() {
Drawing.setLayer(layer); Drawing.setLayer(layer);
if(state == HOVER) { 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) { } 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 { } 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); Assets.flat.pushColor(Vector4f.black);
@ -80,6 +87,20 @@ public class Button extends GameObject implements IMouseCaptureArea, ITransient
Assets.flat.popColor(); 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) { // public void draw(int x, int y, int w, int h) {
// this.x = x; // this.x = x;
// this.y = y; // this.y = y;
@ -201,7 +222,7 @@ public class Button extends GameObject implements IMouseCaptureArea, ITransient
} }
@Override @Override
public Vector4f getBox() { public Vector4f getGuiBox() {
return new Vector4f(x, y, width, height); return new Vector4f(x, y, width, height);
} }

View File

@ -1,5 +1,8 @@
package xyz.valnet.hadean.input; package xyz.valnet.hadean.input;
public interface IButtonListener { import java.io.Serializable;
@FunctionalInterface
public interface IButtonListener extends Serializable {
public void click(Button target); public void click(Button target);
} }

View File

@ -31,4 +31,7 @@ public interface ISelectable {
return Priority.NORMAL; return Priority.NORMAL;
} }
public String getName(); public String getName();
public default String getGenericName() {
return getName();
}
} }

View File

@ -11,6 +11,7 @@ import xyz.valnet.hadean.gameobjects.Terrain;
import xyz.valnet.hadean.gameobjects.inputlayer.BuildLayer; import xyz.valnet.hadean.gameobjects.inputlayer.BuildLayer;
import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer; import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer;
import xyz.valnet.hadean.gameobjects.ui.HoverQuery; 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.BuildTab;
import xyz.valnet.hadean.gameobjects.ui.tabs.JobBoardTab; import xyz.valnet.hadean.gameobjects.ui.tabs.JobBoardTab;
import xyz.valnet.hadean.gameobjects.ui.tabs.LoadTab; import xyz.valnet.hadean.gameobjects.ui.tabs.LoadTab;
@ -42,7 +43,7 @@ public class GameScene extends SceneGraph {
for(int i = 0; i < 5; i ++) { for(int i = 0; i < 5; i ++) {
objects.add(new Pawn()); objects.add(new Pawn());
} }
objects.add(new SelectionLayer()); objects.add(new SelectionLayer());
objects.add(new SelectionUI()); objects.add(new SelectionUI());
@ -57,6 +58,8 @@ public class GameScene extends SceneGraph {
objects.add(new MenuTab()); objects.add(new MenuTab());
objects.add(new SaveTab()); objects.add(new SaveTab());
objects.add(new LoadTab()); objects.add(new LoadTab());
// objects.add(new Popup());
} }
} }

View File

@ -21,6 +21,7 @@ public class Assets {
public static final Texture atlas; public static final Texture atlas;
public static final Font font; public static final Font font;
public static final Font bigFont;
public static final Font miniFont; public static final Font miniFont;
public static final Tile9 redFrame; public static final Tile9 redFrame;
public static final Tile9 frame; public static final Tile9 frame;
@ -52,6 +53,7 @@ public class Assets {
public static final Sprite lilPickaxe; public static final Sprite lilPickaxe;
public static final Sprite testTile; public static final Sprite testTile;
public static final Sprite quarry; public static final Sprite quarry;
public static final Sprite fillTile;
public static final SimpleShader flat; 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, 208, 32, 8, 16));
charset.put('♥', new Sprite(atlas, 216, 32, 8, 16)); charset.put('♥', new Sprite(atlas, 216, 32, 8, 16));
charset.put('|', new Sprite(atlas, 224, 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<Character, Sprite> miniCharset = new HashMap<Character, Sprite>(); Map<Character, Sprite> miniCharset = new HashMap<Character, Sprite>();
miniCharset.put('1', new Sprite(atlas, 0, 112, 4, 5)); 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('8', new Sprite(atlas, 28, 112, 4, 5));
miniCharset.put('9', new Sprite(atlas, 32, 112, 4, 5)); miniCharset.put('9', new Sprite(atlas, 32, 112, 4, 5));
miniCharset.put('0', new Sprite(atlas, 36, 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( frame = new Tile9(
new Sprite(atlas, 24, 88, 8, 8), 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),
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( uiFrame = new Tile9(
new Sprite(atlas, 32, 80, 1, 1), new Sprite(atlas, 32, 80, 1, 1),

View File

@ -8,6 +8,7 @@ public class Layers {
public static final float BACKGROUND = current ++; public static final float BACKGROUND = current ++;
public static final float TILES = current ++; public static final float TILES = current ++;
public static final float GROUND_MARKERS = current ++;
public static final float GROUND = current ++; public static final float GROUND = current ++;
public static final float AIR = current ++; public static final float AIR = current ++;
public static final float PAWNS = Layers.AIR + 0.001f; public static final float PAWNS = Layers.AIR + 0.001f;