diff --git a/src/main/java/xyz/valnet/engine/App.java b/src/main/java/xyz/valnet/engine/App.java index ba7fc57..30a9ada 100644 --- a/src/main/java/xyz/valnet/engine/App.java +++ b/src/main/java/xyz/valnet/engine/App.java @@ -9,6 +9,7 @@ import static org.lwjgl.system.MemoryUtil.*; import java.nio.IntBuffer; +import org.lwjgl.PointerBuffer; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.openal.AL; @@ -121,6 +122,8 @@ public class App { // Get the resolution of the primary monitor long primaryMonitor = glfwGetPrimaryMonitor(); + PointerBuffer monitors = glfwGetMonitors(); + primaryMonitor = monitors.get(1); GLFWVidMode vidmode = glfwGetVideoMode(primaryMonitor); IntBuffer monitorX = stack.mallocInt(1); // int* IntBuffer monitorY = stack.mallocInt(1); // int* diff --git a/src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java b/src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java index d1ad691..a2e38e6 100644 --- a/src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java +++ b/src/main/java/xyz/valnet/engine/graphics/ImmediateUI.java @@ -404,6 +404,21 @@ public abstract class ImmediateUI extends GameObject implements IMouseCaptureAre adjustBox(1, space); } + protected void space(int space, int space2) { + if(context.horizontal) { + adjustBox(space, 1); + vertical(() -> { + space(space2); + }); + } + else { + adjustBox(1, space); + horizontal(() -> { + space(space2); + }); + } + } + protected void end() { List buttonIdsToRemove = new ArrayList(); diff --git a/src/main/java/xyz/valnet/engine/math/Box.java b/src/main/java/xyz/valnet/engine/math/Box.java index 3dca1cb..38df72c 100644 --- a/src/main/java/xyz/valnet/engine/math/Box.java +++ b/src/main/java/xyz/valnet/engine/math/Box.java @@ -57,6 +57,10 @@ public class Box implements Serializable { this(pos.x, pos.y, dim.x, dim.y); } + public Box(Vector4i v) { + this(v.x, v.y, v.z, v.w); + } + public static Box fromPoints(Vector2i a, Vector2i b) { return new Box(a.x, a.y, b.x - a.x, b.y - a.y); } diff --git a/src/main/java/xyz/valnet/engine/math/Vector2f.java b/src/main/java/xyz/valnet/engine/math/Vector2f.java index 163e16f..0ca702d 100644 --- a/src/main/java/xyz/valnet/engine/math/Vector2f.java +++ b/src/main/java/xyz/valnet/engine/math/Vector2f.java @@ -4,7 +4,7 @@ import java.io.Serializable; public class Vector2f implements Serializable { - public float x, y; + public final float x, y; public static Vector2f zero = new Vector2f(0, 0); public static Vector2f north = new Vector2f(0, -1); @@ -57,4 +57,16 @@ public class Vector2f implements Serializable { float y = Math.min(Math.max(this.y, box.y), box.y2); return new Vector2f(x, y); } + + public Vector2f toPolar() { + float r = (float) Math.sqrt(x * x + y * y); + float theta = (float) Math.atan2(y, x); + return new Vector2f(r, theta); + } + + public Vector2f toCartesian() { + float x = this.x * (float) Math.cos(this.y); + float y = this.x * (float) Math.sin(this.y); + return new Vector2f(x, y); + } } diff --git a/src/main/java/xyz/valnet/engine/math/Vector2i.java b/src/main/java/xyz/valnet/engine/math/Vector2i.java index 44c94af..78fac4a 100644 --- a/src/main/java/xyz/valnet/engine/math/Vector2i.java +++ b/src/main/java/xyz/valnet/engine/math/Vector2i.java @@ -70,4 +70,8 @@ public class Vector2i implements Serializable { return new Vector2i(this.x - b.x, this.y - b.y); } + public Vector2i add(Vector2i b) { + return new Vector2i(this.x + b.x, this.y + b.y); + } + } diff --git a/src/main/java/xyz/valnet/hadean/HadeanGame.java b/src/main/java/xyz/valnet/hadean/HadeanGame.java index 1981596..91dd6fd 100644 --- a/src/main/java/xyz/valnet/hadean/HadeanGame.java +++ b/src/main/java/xyz/valnet/hadean/HadeanGame.java @@ -9,7 +9,7 @@ import xyz.valnet.hadean.util.Assets; public class HadeanGame extends Game { public static final HadeanGame HADEAN_GAME = new HadeanGame(); - public static boolean debugView = false; + public static boolean debugView = true; public static void main(String[] args) { new App(HADEAN_GAME).run(); @@ -25,4 +25,4 @@ public class HadeanGame extends Game { public void updateViewMatrix(Matrix4f matrix) { Assets.flat.setMatrices(matrix); } -} +} \ No newline at end of file diff --git a/src/main/java/xyz/valnet/hadean/designation/Designation.java b/src/main/java/xyz/valnet/hadean/designation/Designation.java index ce29865..9e78994 100644 --- a/src/main/java/xyz/valnet/hadean/designation/Designation.java +++ b/src/main/java/xyz/valnet/hadean/designation/Designation.java @@ -18,7 +18,7 @@ public abstract class Designation extends GameObject impl Class type = getType(); List things = getAll(type); for(ISelectable thing : things) { - Box thingBox = thing.getWorldBox(); + Box thingBox = thing.getSelectionWorldBox(); if(box.intersects(thingBox)) designate((T) thing); } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/Camera.java b/src/main/java/xyz/valnet/hadean/gameobjects/Camera.java index 62d35a5..73a4cf8 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/Camera.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/Camera.java @@ -24,7 +24,7 @@ import static xyz.valnet.engine.util.Math.lerp; public class Camera extends GameObject implements ITransient, IMouseCaptureArea { - private int tileWidth = 16; + private int tileWidth = 32; private Vector2f focus = new Vector2f(0, 0); @@ -79,8 +79,7 @@ public class Camera extends GameObject implements ITransient, IMouseCaptureArea } public void focus(float x, float y) { - this.focus.x = x; - this.focus.y = y; + this.focus = new Vector2f(x, y); } public final Box world2screen(Box box) { 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 82054d0..bee829a 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/SelectionLayer.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/inputlayer/SelectionLayer.java @@ -78,7 +78,7 @@ public class SelectionLayer extends GameObject implements IMouseCaptureArea, ITr float p = lerp(animationAmplitude, 0, t); for(ISelectable thing : selected) { - Box box = thing.getWorldBox().outset(p); + Box box = thing.getSelectionWorldBox().outset(p); camera.draw(Layers.SELECTION_IDENTIFIERS, Assets.selectedFrame, box); thing.selectedRender(); } @@ -100,7 +100,7 @@ public class SelectionLayer extends GameObject implements IMouseCaptureArea, ITr int prio = Integer.MIN_VALUE; for(ISelectable thing : selectables) { - Box thingBox = thing.getWorldBox(); + Box thingBox = thing.getSelectionWorldBox(); if(selectionBox.intersects(thingBox)) { int thingPrio = thing.getSelectPriority().toValue(); if(thingPrio > prio) { diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/jobs/Job.java b/src/main/java/xyz/valnet/hadean/gameobjects/jobs/Job.java index 128ef88..2200022 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/jobs/Job.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/jobs/Job.java @@ -1,254 +1,38 @@ package xyz.valnet.hadean.gameobjects.jobs; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import xyz.valnet.engine.math.Vector2i; -import xyz.valnet.engine.scenegraph.GameObject; import xyz.valnet.hadean.gameobjects.worldobjects.items.Item; -import xyz.valnet.hadean.gameobjects.worldobjects.zones.Stockpile; -import xyz.valnet.hadean.interfaces.IItemPredicate; import xyz.valnet.hadean.interfaces.IItemReceiver; import xyz.valnet.hadean.interfaces.IWorkable; -public class Job extends GameObject { - - private Job that = this; - private List closedListeners = new ArrayList(); - private List completedListeners = new ArrayList(); - - public abstract class JobStep implements Serializable { - public abstract Vector2i[] getLocations(); - public void next() { - that.nextStep(); - } - public abstract boolean isValid(); - } - - // pickup and dropoff should be all in one step, as each step should - // only need state tracked for the job, not the worker. - // workers can change between steps. - @Deprecated - public class PickupItem extends JobStep { - public Item item; - - public PickupItem(Item item) { - this.item = item; - } - +public interface Job { + record Work( + IWorkable workable + ) implements Job { @Override - public Vector2i[] getLocations() { - return new Vector2i[] { item.getWorldPosition().xy() }; + public String getShortDescription() { + return workable.getWorkType().verb + " " + workable.getName(); } + } + record Haul( + Item item, + IItemReceiver rcvr + ) implements Job { @Override - public boolean isValid() { - return true; + public String getShortDescription() { + return "Haul " + item.getName() + " to " + rcvr.getName(); } } - public class DropoffPredicateAtItemReceiver extends JobStep { + public String getShortDescription(); - public IItemReceiver receiver; - public IItemPredicate predicate; + // private JobBoard board; - public DropoffPredicateAtItemReceiver(IItemReceiver receiver, IItemPredicate predicate) { - this.receiver = receiver; - this.predicate = predicate; - } + // public void connect(JobBoard board) { + // this.board = boaard; + // } - @Override - public Vector2i[] getLocations() { - return receiver.getItemDropoffLocations(); - } - - @Override - public boolean isValid() { - return true; - } - } - - // pickup and dropoff should be all in one step, as each step should - // only need state tracked for the job, not the worker. - // workers can change between steps. - @Deprecated - public class PickupItemByPredicate extends JobStep { - public IItemPredicate predicate; - - public PickupItemByPredicate(IItemPredicate predicate) { - this.predicate = predicate; - } - - @Override - public Vector2i[] getLocations() { - Set positionSet = new HashSet(); - for(Item item : that.getAll(Item.class)) { - if(!item.matches(predicate)) continue; - positionSet.add(item.getWorldPosition().xy()); - } - Vector2i[] positions = new Vector2i[positionSet.size()]; - positionSet.toArray(positions); - return positions; - } - - @Override - public boolean isValid() { - return getLocations().length > 0; - } - } - - public class DropoffAtItemReceiver extends JobStep { - - public IItemReceiver receiver; - public Item item; - - public DropoffAtItemReceiver(IItemReceiver receiver, Item item) { - this.receiver = receiver; - this.item = item; - } - - @Override - public Vector2i[] getLocations() { - return receiver.getItemDropoffLocations(); - } - - @Override - public boolean isValid() { - return true; - } - } - - // TODO find the _best_ place to dropoff, instead of just the top left place. - public class DropoffAtStockpile extends JobStep { - public Item item; - public DropoffAtStockpile(Item item) { - this.item = item; - } - - public Vector2i[] getLocations() { - Stockpile pile = that.get(Stockpile.class); - // Vector4f box = pile.getWorldBox().toXYWH(); - return new Vector2i[] { - pile.getFreeTile() - }; - } - - @Override - public boolean isValid() { - return that.get(Stockpile.class) != null; - } - } - - public class Work extends JobStep { - public IWorkable subject; - public Work(IWorkable subject) { - this.subject = subject; - } - @Override - public Vector2i[] getLocations() { - return subject.getWorkablePositions(); - } - public boolean doWork(float dTime) { - return subject.doWork(dTime); - } - - @Override - public boolean isValid() { - return true; - } - } - - private List steps; - private String name; - private int step; - private boolean hasClosed = false; - private boolean hasCompleted = false; - - public void reset() { - step = 0; - } - - public Job(String name) { - this.steps = new ArrayList(); - this.name = name; - } - - public void addStep(JobStep step) { - steps.add(step); - } - - public Vector2i[] getLocations() { - if(steps.size() == 0) throw new Error("Cannot get location of job with no steps"); - JobStep step = getCurrentStep(); - return step.getLocations(); - } - - public void nextStep() { - step ++; - if(isCompleted()) { - completed(); - remove(this); - } - } - - public boolean isCompleted() { - return step >= steps.size(); - } - - public JobStep getCurrentStep() { - if(step >= steps.size()) return null; - return steps.get(step); - } - - public String getJobName() { - return name; - } - - @FunctionalInterface - public interface Callback extends Serializable { - public void apply(); - } - - private final void completed() { - if(hasCompleted) return; - hasCompleted = true; - for(Callback callback : completedListeners) { - callback.apply(); - } - close(); - } - - public void close() { - if(hasClosed) return; - hasClosed = true; - for(Callback callback : closedListeners) { - callback.apply(); - } - } - - public void registerClosedListener(Callback callback) { - closedListeners.add(callback); - } - - public void unregisterClosedListener(Callback callback) { - closedListeners.remove(callback); - } - - public void registerCompletedListener(Callback callback) { - completedListeners.add(callback); - } - - public void unregisterCompletedListener(Callback callback) { - completedListeners.remove(callback); - } - - public boolean isValid() { - for(JobStep step : steps) { - if(!step.isValid()) return false; - } - return true; - } + // public void close() { + // board.close(this); + // } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/jobs/JobBoard.java b/src/main/java/xyz/valnet/hadean/gameobjects/jobs/JobBoard.java index 1b3d9c6..168babc 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/jobs/JobBoard.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/jobs/JobBoard.java @@ -5,22 +5,12 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; -import java.util.stream.Stream; +import java.util.Map.Entry; -import xyz.valnet.engine.graphics.Color; -import xyz.valnet.engine.math.Vector2i; import xyz.valnet.engine.scenegraph.GameObject; -import xyz.valnet.hadean.HadeanGame; -import xyz.valnet.hadean.gameobjects.Camera; import xyz.valnet.hadean.gameobjects.worldobjects.agents.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,158 +18,26 @@ public class JobBoard extends GameObject { private List toRemove = new ArrayList(); private Map allocations = new HashMap(); - public Job postSimpleWorkJob(IWorkable subject) { - Job job = add(new Job(subject.getJobName())); - job.addStep(job.new Work(subject)); - postJob(job); + public Job postJob(Job job) { + availableJobs.add(job); return job; } - private Camera camera; - - @Override - public void connect() { - camera = get(Camera.class); - } - - @Override - public void renderAlpha() { - super.render(); - if(HadeanGame.debugView) { - float opacity = 0.6f; - Assets.flat.pushColor(Color.orange.withAlpha(opacity)); - for(Job job : availableJobs) { - for(Vector2i position : job.getLocations()) { - if(job.isValid()) { - Assets.flat.swapColor(Color.orange.withAlpha(opacity)); - } else { - Assets.flat.swapColor(Color.red.withAlpha(opacity)); - } - camera.draw(Layers.GROUND_MARKERS, Assets.fillTile, position.asFloat()); - } - } - Assets.flat.swapColor(Color.lime.withAlpha(opacity)); - for(Job job : allocations.values()) { - for(Vector2i position : job.getLocations()) { - camera.draw(Layers.GROUND_MARKERS, Assets.fillTile, position.asFloat()); - } - } - Assets.flat.popColor(); - } - } - - 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); + public Job post(IWorkable workable) { + var job = new Job.Work(workable); + availableJobs.add(job); return job; } - public void postJob(Job job) { - if(!job.inScene()) add(job); - job.registerClosedListener(() -> { - if(allocations.values().contains(job)) { - List toFire = new ArrayList(); + + // ================= lawl old shit - 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) { - return getJobsForWorker(worker).size() != 0; - } - - private List getJobsForWorker(Pawn worker) { - Vector2i workerLocation = worker.getWorldPosition().xy(); - - List workables = availableJobs - .stream() - .filter(job -> job.isValid()) - .map(job -> new Pair( - job, - Stream.of(job.getLocations()) - .map(v -> v.distanceTo((int) workerLocation.x, (int) workerLocation.y)) - .reduce(Float.MAX_VALUE, (a, b) -> a < b ? a : b) - )) - // sort the jobs by their distance from the worker - .sorted((Pair a, Pair b) -> { - if(a.second() > b.second()) return 1; - if(b.second() > a.second()) return -1; - return 0; - }) - // then convert the stream back to just the jobs - .map(workerDistanceTuple -> workerDistanceTuple.first()) - .toList(); - - return workables; - } - - public Job requestJob(Pawn worker) { - - - List workables = getJobsForWorker(worker); - - if(workables.size() > 0) { - Job firstJob = workables.get(0); - availableJobs.remove(firstJob); - allocations.put(worker, firstJob); - return firstJob; - } - return null; - } - - public void quitJob(Pawn worker, Job job) { - if(!allocations.containsKey(worker)) return; - Job foundJob = allocations.get(worker); - if(foundJob != job) return; - availableJobs.add(job); - allocations.remove(worker); - } - - @Override - public void update(float dTime) { - for(Job job : toRemove) { - if(allocations.values().contains(job)) { - // I AM NOT SURE THIS WORKS - allocations.values().remove(job); - } - if(availableJobs.contains(job)) { - availableJobs.remove(job); - } - } - toRemove.clear(); - } - - public boolean workerHasJob(Pawn worker) { - return allocations.containsKey(worker); - } - - // public Job getJob(Pawn worker) { - // if(allocations.containsKey(worker)) { - // return allocations.get(worker); - // } else return null; - // } - + @Deprecated public String getValidJobs() { Map jobs = new HashMap(); for(Job job : availableJobs) { - if(!job.isValid()) continue; - String name = job.getJobName(); + // if(!job.isValid()) continue; + String name = job.getShortDescription(); if(!jobs.containsKey(name)) jobs.put(name, 0); jobs.put(name, jobs.get(name) + 1); } @@ -192,49 +50,37 @@ public class JobBoard extends GameObject { return str.trim(); } + @Deprecated public String getInvalidJobs() { String str = ""; - for(Job job : availableJobs) { - if(job.isValid()) continue; - str += " " + job.getJobName() + "\n"; - } - return str; - } - - public String getTakenJobs() { - String str = ""; - for(Entry allocation : allocations.entrySet()) { - str += " " + allocation.getKey().getName() + ": " + allocation.getValue().getJobName() + "\n"; - } + // for(Job job : availableJobs) { + // if(job.isValid()) continue; + // str += " " + job.getJobName() + "\n"; + // } return str; } @Deprecated - public String details() { - - String takenJobsString = ""; - String availableJobsString = ""; - String impossibleJobsString = ""; - - int possibleJobs = 0; - int impossibleJobs = 0; - + public String getTakenJobs() { + String str = ""; for(Entry allocation : allocations.entrySet()) { - takenJobsString += " " + allocation.getKey().getName() + ": " + allocation.getValue().getJobName() + "\n"; + str += " " + allocation.getKey().getName() + ": " + allocation.getValue().getShortDescription() + "\n"; } - - for(Job job : availableJobs) { - if(job.isValid()) { - availableJobsString += " " + job.getJobName() + "\n"; - possibleJobs ++; - } else { - impossibleJobsString += " " + job.getJobName() + "\n"; - impossibleJobs ++; + return str; + } + + public void close(Job job) { + if(allocations.values().contains(job)) { + Set workers = new HashSet<>(); + for(var entry : allocations.entrySet()) { + if(entry.getValue() == job) workers.add(entry.getKey()); + } + for(var worker : workers) { + worker.cancelJob(job); } } - - 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/terrain/Terrain.java b/src/main/java/xyz/valnet/hadean/gameobjects/terrain/Terrain.java index e794b93..cae7936 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/terrain/Terrain.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/terrain/Terrain.java @@ -11,7 +11,7 @@ import xyz.valnet.hadean.pathfinding.IPathable; public class Terrain extends GameObject implements IPathable, IWorldBoundsAdapter { - public static final int WORLD_SIZE = 64; + public static final int WORLD_SIZE = 24; public static final int TILE_SIZE = 8; // public static int left, top; diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/terrain/Tile.java b/src/main/java/xyz/valnet/hadean/gameobjects/terrain/Tile.java index 20fefee..4025e77 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/terrain/Tile.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/terrain/Tile.java @@ -4,10 +4,11 @@ import java.util.HashSet; import java.util.Set; import xyz.valnet.engine.graphics.Color; +import xyz.valnet.engine.math.TileBox; import xyz.valnet.engine.math.Vector2i; import xyz.valnet.engine.math.Vector4i; import xyz.valnet.engine.scenegraph.GameObject; -import xyz.valnet.hadean.gameobjects.jobs.JobBoard; +import xyz.valnet.hadean.HadeanGame; import xyz.valnet.hadean.gameobjects.worldobjects.Tree; import xyz.valnet.hadean.gameobjects.worldobjects.WorldObject; import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder; @@ -60,7 +61,7 @@ public class Tile extends WorldObject implements IWorkable { rocks = true; } if(Math.random() > 0.97) { - add(new Tree(pos.x, pos.y)); + // add(new Tree(pos.x, pos.y)); } else if(Math.random() > 0.98) { rocks = false; add(new Boulder(pos.x, pos.y)); @@ -121,7 +122,8 @@ public class Tile extends WorldObject implements IWorkable { pingNeighbors(); if(thing instanceof FarmPlot) { - get(JobBoard.class).postSimpleWorkJob(this); + throw new Error("Cannot Till Soil Yet"); + // get(JobBoard.class).postSimpleWorkJob(this); } } @@ -141,6 +143,23 @@ public class Tile extends WorldObject implements IWorkable { @Override public void update(float dTime) {} + @Override + public void renderAlpha() { + if(!HadeanGame.debugView) return; + // if(!isWalkable()) { + // Assets.flat.pushColor(new Color(1, 0.3f, 0.3f, 0.5f)); + // camera.draw( + // Layers.MARKERS, + // Assets.fillColor, + // new TileBox(getWorldPosition().xy(), 1, 1)); + // camera.draw( + // Layers.MARKERS, + // Assets.selectionFrame, + // new TileBox(getWorldPosition().xy(), 1, 1)); + // Assets.flat.popColor(); + // } + } + @Override public void render() { Vector4i pos = getWorldPosition(); @@ -182,11 +201,6 @@ public class Tile extends WorldObject implements IWorkable { }; } - @Override - public String getJobName() { - return "Till Soil"; - } - @Override public boolean doWork(float dTime) { tillLevel += 0.005f * dTime; @@ -220,4 +234,9 @@ public class Tile extends WorldObject implements IWorkable { } return false; } + + @Override + public WorkType getWorkType() { + return WorkType.Farm; + } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/ui/HoverQuery.java b/src/main/java/xyz/valnet/hadean/gameobjects/ui/HoverQuery.java index 3396a32..3c1278b 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/ui/HoverQuery.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/ui/HoverQuery.java @@ -42,7 +42,7 @@ public class HoverQuery extends GameObject implements ITransient { Vector2f position = camera.screen2world(App.mouseX, App.mouseY); thingStrings.clear(); for(WorldObject obj : getAll(WorldObject.class)) { - Box box = obj.getWorldBox(); + Box box = obj.getSelectionWorldBox(); if(box.contains(position)) { thingStrings.add(obj.getName()); if (!HadeanGame.debugView) continue; diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/ui/SelectionUI.java b/src/main/java/xyz/valnet/hadean/gameobjects/ui/SelectionUI.java index a4e45d7..94a883a 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/ui/SelectionUI.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/ui/SelectionUI.java @@ -13,6 +13,7 @@ import xyz.valnet.engine.graphics.ImmediateUI; import xyz.valnet.engine.scenegraph.ITransient; import xyz.valnet.hadean.Constants; import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer; +import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab; import xyz.valnet.hadean.interfaces.ISelectable; import xyz.valnet.hadean.interfaces.ISelectionChangeListener; import xyz.valnet.hadean.util.Action; @@ -63,6 +64,7 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener if(!opened && openness < 0.0001f && selected.size() > 0) { resetCache(); } + if(updateActionsFlag) updateActions(); } private void resetCache() { @@ -72,6 +74,16 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener actions.clear(); } + private void updateActions() { + actions.clear(); + updateActionsFlag = false; + for(ISelectable selectable : selected) + for(Action action : selectable.getActions()) { + DebugTab.log(action.name); + actions.add(action); + } + } + @Override public void selectionChanged(List newSelection) { @@ -93,9 +105,6 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener properName = selectable.getName(); genericName = selectable.getGenericName(); - for(Action action : selectable.getActions()) { - actions.add(action); - } if(!selectedByType.containsKey(clazz)) { selectedByType.put(clazz, new ArrayList()); @@ -104,6 +113,8 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener selectedByType.get(clazz).add(selectable); } + updateActions(); + open(); } @@ -116,6 +127,8 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener return (int) Math.round(lerp(a, b, openness)); } + private boolean updateActionsFlag = false; + @Override protected void gui(int screenWidth, int screenHeight) { // if(selected.isEmpty()) return; @@ -161,6 +174,7 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener horizontal(() -> { for(Action action : actions) { if(button(action.name)) { + updateActionsFlag = true; for(ISelectable selectable : selected) { selectable.runAction(action); } @@ -169,8 +183,8 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener } }); } else { - space(8); - text(" Select an Item Type"); + space(8, 16); + text("Select an Item Type"); } }); diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/DebugTab.java b/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/DebugTab.java index 909090f..5c0d50d 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/DebugTab.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/DebugTab.java @@ -59,7 +59,6 @@ public class DebugTab extends Tab implements IKeyboardListener { HadeanGame.debugView = !HadeanGame.debugView; } }); - } private static List logs = new LinkedList(); 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 90e2c9b..9804e7f 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Tree.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Tree.java @@ -55,9 +55,9 @@ public class Tree extends WorldObject implements ISelectable, IWorkable { public void runAction(Action action) { if(action == ACTION_CHOP) { if(chopJob == null) { - chopJob = get(JobBoard.class).postSimpleWorkJob(this); + chopJob = get(JobBoard.class).post(this); } else { - chopJob.close(); + get(JobBoard.class).close(chopJob); chopJob = null; } } @@ -116,13 +116,13 @@ public class Tree extends WorldObject implements ISelectable, IWorkable { add(new Log(pos.x, pos.y)); } - @Override - public String getJobName() { - return "Chop Tree"; - } - @Override public String getName() { return "Tree"; } + + @Override + public WorkType getWorkType() { + return WorkType.Chop; + } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java index 7371846..ca8a0b9 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java @@ -120,6 +120,10 @@ public abstract class WorldObject extends GameObject implements IWorldObject { return terrain.getTile(x, y); } + public Tile getTile(Vector2i location) { + return terrain.getTile(location.x, location.y); + } + public Set getTiles() { return linkedTiles; } @@ -130,7 +134,7 @@ public abstract class WorldObject extends GameObject implements IWorldObject { public abstract String getName(); - public Box getWorldBox() { + public Box getSelectionWorldBox() { return new Box(x, y, w, h); } 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 196e047..a19e1b9 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 @@ -8,17 +8,17 @@ import static org.lwjgl.opengl.GL20.glVertexAttrib2f; import xyz.valnet.engine.graphics.Color; import xyz.valnet.engine.graphics.Drawing; +import xyz.valnet.engine.math.Box; +import xyz.valnet.engine.math.TileBox; import xyz.valnet.engine.math.Vector2f; import xyz.valnet.engine.math.Vector2i; import xyz.valnet.engine.shaders.SimpleShader; import xyz.valnet.hadean.HadeanGame; -import xyz.valnet.hadean.gameobjects.terrain.Terrain; -import xyz.valnet.hadean.gameobjects.terrain.Tile; +import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab; import xyz.valnet.hadean.gameobjects.worldobjects.WorldObject; import xyz.valnet.hadean.interfaces.ISelectable; import xyz.valnet.hadean.pathfinding.AStarPathfinder; import xyz.valnet.hadean.pathfinding.IPathfinder; -import xyz.valnet.hadean.pathfinding.Node; import xyz.valnet.hadean.pathfinding.Path; import xyz.valnet.hadean.util.Action; import xyz.valnet.hadean.util.Assets; @@ -27,147 +27,134 @@ import static xyz.valnet.engine.util.Math.lerp; public abstract class Agent extends WorldObject implements ISelectable { public abstract String getName(); - private float frameCounter = 0; private int speed = 100 + (int)(Math.random() * 50); + private float frameCounter = speed; private IPathfinder pathfinder; - private Path path = null; - private boolean stopPathingFlag = false; - - public boolean isPathing() { - return path != null && !path.isComplete(); - } - - public Vector2f getCalculatedPosition() { - if(path == null || path.isComplete()) return getWorldPosition().xy().asFloat(); - Vector2i pos = getWorldPosition().xy(); - Vector2f nextPos = path.peek().getPosition().asFloat(); - return new Vector2f( - lerp(pos.x, nextPos.x, frameCounter / (float)speed), - lerp(pos.y, nextPos.y, frameCounter / (float)speed) - ); + private boolean isAnimating() { + return frameCounter < speed; } @Override public void start() { super.start(); - frameCounter = 0; + frameCounter = speed; pathfinder = new AStarPathfinder(terrain); + previousPosition = getWorldPosition().xy(); } @Override public void update(float dTime) { - think(); - act(dTime); - postAct(); - } - - protected abstract void postAct(); - - private void move(float dTime) { frameCounter += dTime; - if(frameCounter >= speed) { - Vector2i nextPos = path.pop().getPosition(); - setPosition(nextPos.x, nextPos.y); - if(nextPath != null) { - path = nextPath; - nextPath = null; - } - if(path.isComplete()) path = null; - frameCounter -= speed; - if(stopPathingFlag) { - path = null; - nextPath = null; - stopPathingFlag = false; + + // if we're already doing something, let it happen + if(isAnimating()) return; + + // if we're not doing anything and we have a path, + // try to take a step + if(path != null) takeStep(); + if(isAnimating()) return; + + // if we're STILL doing jack, but we have a destination + // try to path to that place + if(dst != null) goTo(dst); + if(isAnimating()) return; + + // if all fails, tell the agent we're idle + idle(); + } + + // public void goTo(int x, int y) { + // Vector2i pos = getWorldPosition().xy(); + // Path newPath = pathfinder.getPath(pos.x, pos.y, x, y); + // path = newPath; + // } + + private Vector2i previousPosition; + + private void takeStep() { + path.pop(); + if(path.isComplete()) { + DebugTab.log("Finished Pathing"); + if(path.getDestination().getPosition().equals(dst)) { + dst = null; } + path = null; + return; } + if(!getTile(path.peek().getPosition()).isWalkable()) { + goTo(dst); + return; + } + updatePath(); } - protected void stopPathing() { - stopPathingFlag = true; - nextPath = null; + private void updatePath() { + frameCounter = 0; + Vector2i nextPos = path.peek().getPosition(); + if(path.isComplete()) path = null; + previousPosition = getWorldPosition().xy(); + setPosition(nextPos); } - private void correctPath() { - if(path != null && path.isComplete()) path = null; - if(path == null) return; - if(path.peek().getPosition().equals(this.getWorldPosition().xy())) { - path.pop(); - } - if(path != null && path.isComplete()) path = null; - if(path == null) return; - Tile nextTile = terrain.getTile(path.peek().getPosition()); - if(!nextTile.isWalkable()) { - Vector2i pos = getWorldPosition().xy(); - path = pathfinder.getPath( - pos.x, - pos.y, - path.dst.x, - path.dst.y - ); - } - } + private Path path; + private Vector2i dst; - protected void think() { - correctPath(); - } - - protected boolean act(float dTime) { - if(path != null) { - move(dTime); - return true; - } - - return false; - } - - private Path nextPath = null; - - public void goTo(int x, int y) { - Vector2i pos = getWorldPosition().xy(); - Path newPath = pathfinder.getPath(pos.x, pos.y, x, y); - if(path == null) { - path = newPath; - frameCounter -= 0; - } else { - nextPath = newPath; - } - } - - public void goToClosest(Vector2i[] destinations) { - Path newPath = pathfinder.getBestPath(getWorldPosition().xy(), destinations); - if(path == null) { - path = newPath; - frameCounter = 0; - } else { - nextPath = newPath; - } + public void stopPathing() { + path = null; + dst = null; } public void goTo(Vector2i location) { - goTo(location.x, location.y); + if(isAnimating()) { + stopPathing(); + dst = location; + return; + } + Path newPath = pathfinder.getPath(getWorldPosition().xy(), location); + path = newPath; + if(path == null) return; + dst = location; + updatePath(); } public void wander() { - Vector2i pos = getWorldPosition().xy(); - int randomX = (int)Math.floor(Math.random() * Terrain.WORLD_SIZE); - int randomY = (int)Math.floor(Math.random() * Terrain.WORLD_SIZE); - path = pathfinder.getPath(pos.x, pos.y, randomX, randomY); + Vector2i pos; + Vector2i random; + do { + pos = getWorldPosition().xy(); + random = new Vector2f( + 2 + (float) Math.random() * 10, + (float) Math.random() * (float) Math.PI * 2 + ).toCartesian().asInt().add(pos); + + } while (terrain.isOutOfBounds(random.x, random.y)); + + goTo(random); + } + + @Override + public void render() { } @Override public void renderAlpha() { if(!HadeanGame.debugView) return; + camera.drawProgressBar(frameCounter / speed, new Box(getWorldPosition())); + Drawing.setLayer(Layers.GROUND_MARKERS); - Assets.flat.pushColor(Color.white.withAlpha(0.6f)); + Assets.flat.pushColor(new Color(0.1f, 0.7f, 1.0f, 0.6f)); + if(path != null) { + Assets.flat.swapColor(Color.white.withAlpha(0.6f)); int count = 0; - for(Node node : path) { + for(var node : path) { glBegin(GL_LINES); Vector2i u, v; Vector2i pos = getWorldPosition().xy(); + if(node.from == null) u = camera.world2screen(pos.x, pos.y); else u = camera.world2screen(node.from.x + 0.5f, node.from.y + 0.5f); @@ -177,15 +164,23 @@ public abstract class Agent extends WorldObject implements ISelectable { u = camera.world2screen(getCalculatedPosition().add(new Vector2f(0.5f, 0.5f))); } glVertexAttrib2f(SimpleShader.TEX_COORD, 0, 88 / 256f); - glVertex3f(u.x, u.y, 3f); + glVertex3f(u.x, u.y, Layers.GROUND_MARKERS); glVertexAttrib2f(SimpleShader.TEX_COORD, 0, 88 / 255f); - glVertex3f(v.x, v.y, 3f); + glVertex3f(v.x, v.y, Layers.GROUND_MARKERS); glEnd(); count ++; } - - camera.draw(Layers.GROUND_MARKERS, Assets.selectionFrame, path.getDestination().getPosition().getTileBox()); } + + Assets.flat.swapColor(Color.yellow); + if(dst != null) { + camera.draw( + Layers.GROUND_MARKERS, + Assets.selectionFrame, + new TileBox(dst, 1, 1) + ); + } + Assets.flat.popColor(); } @@ -199,9 +194,34 @@ public abstract class Agent extends WorldObject implements ISelectable { return ISelectable.Priority.HIGH; } - public Vector2i getDestination() { - if(nextPath != null) return nextPath.getDestination().getPosition(); - if(path == null) return null; - return path.getDestination().getPosition(); + @Override + public void runAction(Action action) {} + + @Override + public boolean isWalkable() { + return false; } -} + + @Override + public Box getSelectionWorldBox() { + return new Box(getCalculatedPosition(), 1, 1); + } + + protected abstract void idle(); + + public Vector2f getCalculatedPosition() { + if(!isAnimating()) + return getWorldPosition().xy().asFloat(); + return new Vector2f( + lerp(previousPosition.x, getWorldPosition().x, frameCounter / speed), + lerp(previousPosition.y, getWorldPosition().y, frameCounter / speed) + ); + // if(path == null || path.isComplete()) return getWorldPosition().xy().asFloat(); + // Vector2i pos = getWorldPosition().xy(); + // Vector2f nextPos = path.peek().getPosition().asFloat(); + // return new Vector2f( + // lerp(pos.x, nextPos.x, frameCounter / (float)speed), + // lerp(pos.y, nextPos.y, frameCounter / (float)speed) + // ); + } +} \ No newline at end of file diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Activity.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Activity.java deleted file mode 100644 index 0d80994..0000000 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Activity.java +++ /dev/null @@ -1,22 +0,0 @@ -package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; - -import java.io.Serializable; - -import xyz.valnet.engine.math.Vector2i; - -public abstract class Activity implements Serializable { - - @FunctionalInterface - public interface ActivityCancellationCallback extends Serializable { - public void apply(Activity activity); - } - - public abstract boolean isUrgent(); - public abstract float getBenefit(); - public abstract boolean isValid(); - public abstract void act(float dTime); - public abstract void begin(ActivityCancellationCallback callback); - public abstract void end(); - public abstract String toString(); - public abstract Vector2i[] getTargetLocations(); -} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Desire.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Desire.java new file mode 100644 index 0000000..c4cbd3f --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Desire.java @@ -0,0 +1,29 @@ +package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; + +import xyz.valnet.engine.math.Vector2i; +import xyz.valnet.hadean.gameobjects.worldobjects.items.Item; +import xyz.valnet.hadean.interfaces.IItemPredicate; + +public interface Desire { + record BeAtLocation( + Vector2i dest, + Reason reason + ) implements Desire {} + + record BeAtDynamicLocation( + Vector2i dest, + Reason reason + ) implements Desire {} + + record HoldItemByPredicate( + IItemPredicate item, + Reason reason + ) implements Desire {} + + record HoldExplicitItem( + Item item, + Reason reason + ) implements Desire {} + + public Reason reason(); +} \ No newline at end of file diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/JobActivity.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/JobActivity.java deleted file mode 100644 index d05899d..0000000 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/JobActivity.java +++ /dev/null @@ -1,138 +0,0 @@ -package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; - -import xyz.valnet.engine.math.Vector2i; -import xyz.valnet.hadean.gameobjects.jobs.Job; -import xyz.valnet.hadean.gameobjects.jobs.Job.JobStep; -import xyz.valnet.hadean.gameobjects.jobs.JobBoard; - -public class JobActivity extends Activity { - - private JobBoard jobboard; - private Pawn worker; - private Job job; - - public JobActivity(Pawn worker, JobBoard jobboard) { - this.worker = worker; - this.jobboard = jobboard; - } - - @Override - public boolean isUrgent() { - return false; - } - - @Override - public boolean isValid() { - return jobboard.jobsAvailableForWorker(worker); - } - - @Override - public float getBenefit() { - return 0.5f; - } - - @Override - public void act(float dTime) { - if (doJob(dTime)) return; - } - - - private boolean isPathingToJobLocation() { - if(!worker.isPathing()) return false; - return worker.getDestination().isOneOf(job.getCurrentStep().getLocations()); - } - - private boolean isAtJobStepLocation() { - return worker.getWorldPosition().xy().isOneOf(job.getCurrentStep().getLocations()); - } - - private void goToJobStepLocation() { - worker.goToClosest(job - .getCurrentStep() - .getLocations() - ); - } - - ActivityCancellationCallback callback; - - @Override - public void begin(ActivityCancellationCallback callback) { - - this.callback = callback; - job = jobboard.requestJob(worker); - if(job == null) callback.apply(this); - job.registerClosedListener(() -> { - callback.apply(this); - }); - - // if we dont have a job - if(!jobboard.workerHasJob(worker)) { - } - - // if we have a job and need to go to it and we're not pathing to it - if(jobboard.workerHasJob(worker) && !isAtJobStepLocation() && !isPathingToJobLocation()) { - goToJobStepLocation(); // start pathing there. - return; // and dont think about anything else. - } - } - - @Override - public void end() { - jobboard.quitJob(worker, job); - job = null; - } - // TODO pawns should keep tabs of what job step an item is picked up from - // so dropoff steps can reference the pickup step. - private boolean doJob(float dTime) { - if(!jobboard.workerHasJob(worker)) return false; - JobStep step = job.getCurrentStep(); - // if we're not at the location of the job... - if(!isAtJobStepLocation()) return false; - - if(step instanceof Job.Work) { - Job.Work workStep = (Job.Work)step; - if(workStep.doWork(dTime)) step.next(); - return true; - } else if(step instanceof Job.PickupItem) { - Job.PickupItem pickupStep = (Job.PickupItem) step; - worker.pickupItem(pickupStep.item); - step.next(); - return true; - } else if(step instanceof Job.DropoffAtStockpile) { - if(!worker.getTile().isTileFree()) return false; - Job.DropoffAtStockpile dropoffStep = (Job.DropoffAtStockpile) step; - worker.dropoffItem(dropoffStep.item); - step.next(); - return true; - } else if(step instanceof Job.DropoffAtItemReceiver) { - Job.DropoffAtItemReceiver dropoffStep = (Job.DropoffAtItemReceiver) step; - worker.dropoffItem(dropoffStep.item, dropoffStep.receiver); - step.next(); - return true; - } else if(step instanceof Job.DropoffPredicateAtItemReceiver) { - Job.DropoffPredicateAtItemReceiver dropoffStep = (Job.DropoffPredicateAtItemReceiver) step; - worker.dropoffPredicate(dropoffStep.predicate, dropoffStep.receiver); - step.next(); - return true; - } else if(step instanceof Job.PickupItemByPredicate) { - Job.PickupItemByPredicate pickupStep = (Job.PickupItemByPredicate) step; - worker.pickupItemByPredicate(pickupStep.predicate); - step.next(); - return true; - } - - return false; - } - - @Override - public String toString() { - if(job == null) return "No Work"; - return job.getJobName(); - } - - @Override - public Vector2i[] getTargetLocations() { - return job.getCurrentStep().getLocations(); - } - -} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Needs.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Needs.java deleted file mode 100644 index e47500c..0000000 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Needs.java +++ /dev/null @@ -1,43 +0,0 @@ -package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; - -import java.io.Serializable; - -import xyz.valnet.hadean.HadeanGame; -import xyz.valnet.hadean.util.detail.Detail; -import xyz.valnet.hadean.util.detail.PercentDetail; - -public class Needs implements Serializable { - - private float energy = 0.7f + (float)Math.random() * 0.3f; - private float recreation = 0.5f + (float)Math.random() * 0.5f; - - private float restRatio = 3; - private float decay = 0.000004f; - - public void update(float dTime) { - energy = Math.max(energy - decay * dTime, 0); - recreation = Math.max(recreation - decay * dTime, 0); - } - - public void sleep(float dTime) { - energy = Math.min(energy + decay * dTime * restRatio, 1); - } - - public float getSleepNeed() { - if(HadeanGame.debugView) { - return 0; - } - return 1 - energy; - } - - // public - - public Detail[] getDetails() { - return new Detail[] { - new PercentDetail("Energy", energy, 1), - new PercentDetail("Fun", recreation, 1) - }; - } - - -} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Pawn.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Pawn.java index 6768c76..6f7bd68 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Pawn.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Pawn.java @@ -2,150 +2,66 @@ package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; import static xyz.valnet.hadean.util.detail.Detail.*; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import xyz.valnet.engine.graphics.Color; -import xyz.valnet.engine.math.Box; import xyz.valnet.engine.math.Vector2f; -import xyz.valnet.engine.math.Vector2i; import xyz.valnet.engine.util.Names; import xyz.valnet.hadean.HadeanGame; -import xyz.valnet.hadean.gameobjects.Clock; -import xyz.valnet.hadean.gameobjects.jobs.JobBoard; +import xyz.valnet.hadean.gameobjects.jobs.Job; import xyz.valnet.hadean.gameobjects.terrain.Terrain; -import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab; import xyz.valnet.hadean.gameobjects.worldobjects.agents.Agent; import xyz.valnet.hadean.gameobjects.worldobjects.items.Item; -import xyz.valnet.hadean.interfaces.IItemPredicate; -import xyz.valnet.hadean.interfaces.IItemReceiver; import xyz.valnet.hadean.util.Action; import xyz.valnet.hadean.util.Assets; import xyz.valnet.hadean.util.Layers; -import xyz.valnet.hadean.util.Pair; -import xyz.valnet.hadean.util.detail.BooleanDetail; import xyz.valnet.hadean.util.detail.Detail; -import xyz.valnet.hadean.util.detail.ObjectDetail; -import xyz.valnet.hadean.util.detail.PercentDetail; public class Pawn extends Agent { private String name = Names.getRandomName(); - private Needs needs = new Needs(); - // private int meaningless = 0; - - // private float workEthic = (float) Math.random(); - // private float selfWorth = (float) Math.random(); - - private transient List activities = new ArrayList(); - private Activity currentActivity = null; - - public void pickupItemByPredicate(IItemPredicate itemPredicate) { - Item item = getTile().pickupByItemPredicate(itemPredicate); - if(item == null) { - DebugTab.log("This is an issue"); - } - inventory.add(item); - } - - public void pickupItem(Item i) { - Item item = getTile().removeThing(i); - if(item == null) return; - remove(item); - inventory.add(item); - } - - public void dropoffItem(Item item) { - if(!inventory.contains(item)) { - return; - } - inventory.remove(add(item)); - item.setPosition(getWorldPosition().xy()); - getTile().placeThing(item); - } - - public void dropoffItem(Item item, IItemReceiver receiver) { - if(!inventory.contains(item)) { - return; - } - inventory.remove(item); - // TODO this might need to exist at some point - // add(item); - receiver.receive(item); - } - - private Item getInventoryItemByPredicate(IItemPredicate predicate) { - for(Item item : inventory) { - if(!item.matches(predicate)) continue; - return item; - } - return null; - } - - public void dropoffPredicate(IItemPredicate predicate, IItemReceiver receiver) { - Item item = getInventoryItemByPredicate(predicate); - if(!inventory.contains(item)) { - return; - } - dropoffItem(item, receiver); - } + private Set desires = new HashSet<>(); @Override protected void ready() { super.ready(); - activities = new ArrayList(); - } - - @Override - public void start() { - super.start(); - - activities.add(new JobActivity(this, get(JobBoard.class))); - activities.add(new SleepActivity(needs, get(Clock.class))); - activities.add(new WanderActivity(get(Terrain.class))); } protected void create() { setPosition( - (int) (Math.random() * Terrain.WORLD_SIZE), - (int) (Math.random() * Terrain.WORLD_SIZE) + Terrain.WORLD_SIZE / 2, + Terrain.WORLD_SIZE / 2 + // (int) (Math.random() * Terrain.WORLD_SIZE), + // (int) (Math.random() * Terrain.WORLD_SIZE) ); } @Override public void render() { super.render(); - if(currentActivity instanceof SleepActivity) { - Assets.flat.pushColor(Color.grey(0.5f)); - } else { - Assets.flat.pushColor(Color.white); - } - camera.draw(Layers.PAWNS, Assets.pawn, getCalculatedPosition()); + // if(currentActivity instanceof SleepActivity) { + // Assets.flat.pushColor(Color.grey(0.5f)); + // } else { + Assets.flat.pushColor(Color.white); + // } + + float shake = 0.0f; + camera.draw( + Layers.PAWNS, + Assets.pawn, + getCalculatedPosition().add(new Vector2f( + (float) Math.random() * shake - shake / 2, + (float) Math.random() * shake - shake / 2 + )) + ); Assets.flat.popColor(); } - @Override - public void runAction(Action action) {} - @Override public Detail[] getDetails() { - // return needs.getDetails(); - Detail[] details = mergeDetails(needs.getDetails(), new Detail[] { - new ObjectDetail("Activity", currentActivity), - new BooleanDetail("Pathing", isPathing()), - new ObjectDetail("Inventory", inventory.size()) - }); - - if(HadeanGame.debugView) { - for(Activity activity : activities) { - details = mergeDetails(details, new Detail[] { - new PercentDetail(activity.toString(), activity.getBenefit()) - }); - } - } - - return details; + return new Detail[] {}; } @Override @@ -158,88 +74,86 @@ public class Pawn extends Agent { return "Pawn"; } - @Override - public Box getWorldBox() { - Vector2f pos = getCalculatedPosition(); - return new Box(pos, 1, 1); + private Set inventory = new HashSet<>(); + + public void cancelJob(Job job) { + } + // private float restlessness = 0; + // private final + + + @Override - protected void think() { - super.think(); - decideActivity(); - pathToActivity(); - } - - private void pathToActivity() { - if(currentActivity == null) return; - Vector2i[] locations = currentActivity.getTargetLocations(); - if(locations == null || locations.length == 0) return; - if(isPathing() && getDestination().isOneOf(locations)) return; - goToClosest(locations); - } - - private void decideActivity() { - - List urgentActivities = activities.stream() - .filter(a -> a.isValid() && a.isUrgent()) - .toList(); - - if(urgentActivities.size() > 0) { - switchActivity(urgentActivities.get(0)); - return; - } - - if(currentActivity != null) return; - - List valueSortedActivities = activities.stream() - .filter(a -> a.isValid()) - .map(a -> new Pair(a, a.getBenefit())) - .filter(a -> a.second() >= 0) - .sorted((a, b) -> a.second() > b.second() ? -1 : 1) - .map(p -> p.first()) - .toList(); - - if(valueSortedActivities.size() == 0) return; - - switchActivity(valueSortedActivities.get(0)); - } - - private void switchActivity(Activity activity) { - if(activity == currentActivity) return; - if(currentActivity != null) currentActivity.end(); - currentActivity = activity; - stopPathing(); - currentActivity.begin(a -> endActivity(a)); - } - - private void endActivity(Activity activity) { - activity.end(); - stopPathing(); - if(currentActivity == activity) { - currentActivity = null; + public void idle() { + switch(currentWanderState) { + case On: + wander(); + break; + case Off: break; + case Random: + if(Math.random() < 0.005) wander(); + break; } } + private enum WanderState { + Off, + Random, + On + } + + private WanderState currentWanderState = WanderState.Off; + + public static final Action CYCLE_WANDER_STATE_ON = new Action("Wander\nON"); + public static final Action CYCLE_WANDER_STATE_RANDOM = new Action("Wander\nRANDOM"); + public static final Action CYCLE_WANDER_STATE_OFF = new Action("Wander\nOFF"); + + public static final Action FORCE_WANDER = new Action("Wander"); + public static final Action CANCEL_PATH = new Action("Cancel\n Path"); + @Override - protected boolean act(float dTime) { - if(super.act(dTime)) return true; - if(currentActivity != null) { - currentActivity.act(dTime); - return true; + public Action[] getActions() { + Action wanderAction = null; + switch(currentWanderState) { + case Off: { + wanderAction = CYCLE_WANDER_STATE_OFF; + break; + } + case Random: { + wanderAction = CYCLE_WANDER_STATE_RANDOM; + break; + } + case On: { + wanderAction = CYCLE_WANDER_STATE_ON; + break; + } } - return false; - } + if(HadeanGame.debugView) { + return new Action[] { + wanderAction, + FORCE_WANDER, + CANCEL_PATH + }; + } + return new Action[] { - protected void postAct() { - needs.update(1); + }; } - private List inventory = new ArrayList(); - @Override - public boolean isWalkable() { - // TODO thiss could be an interesting mechanic, but it may be bad - return true; + public void runAction(Action action) { + if(action == CYCLE_WANDER_STATE_ON) { + currentWanderState = WanderState.Off; + } else if (action == CYCLE_WANDER_STATE_RANDOM) { + currentWanderState = WanderState.On; + } else if (action == CYCLE_WANDER_STATE_OFF) { + currentWanderState = WanderState.Random; + } else if (action == FORCE_WANDER) { + wander(); + } else if (action == CANCEL_PATH) { + stopPathing(); + } } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Reason.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Reason.java new file mode 100644 index 0000000..78eb96b --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/Reason.java @@ -0,0 +1,14 @@ +package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; + +public interface Reason { + record Job( + xyz.valnet.hadean.gameobjects.jobs.Job job + ) implements Reason {} + + record Need( + ) implements Reason {} + + record Desire( + Desire desire + ) implements Reason {} +} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/SleepActivity.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/SleepActivity.java deleted file mode 100644 index 71d607b..0000000 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/SleepActivity.java +++ /dev/null @@ -1,71 +0,0 @@ -package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; - -import xyz.valnet.engine.math.Vector2i; -import xyz.valnet.engine.util.Math.WeightedAverage; -import xyz.valnet.hadean.gameobjects.Clock; - -public class SleepActivity extends Activity { - - private Needs needs; - private Clock clock; - - private float circadianStrength = (float)Math.random() * 5f; - - - public SleepActivity(Needs needs, Clock clock) { - this.needs = needs; - this.clock = clock; - } - - @Override - public boolean isUrgent() { - return needs.getSleepNeed() >= 0.97f; - } - - @Override - public float getBenefit() { - // subtract because sleeping for only 5 minutes when - // you're not that tired to hit 100% is undesireable. - // as it will induce oversleep - WeightedAverage average = new WeightedAverage(); - average.add(needs.getSleepNeed(), 1); - - average.add(1 - 2 * clock.getSunlight(), circadianStrength); - return average.calculate(); - } - - // isValid vs canBeStarted? idk, maybe thats not important. - @Override - public boolean isValid() { - return needs.getSleepNeed() > 0.2f; - } - - @Override - public void act(float dTime) { - needs.sleep(dTime); - if(needs.getSleepNeed() == 0) { - callback.apply(this); - } - } - - ActivityCancellationCallback callback; - - @Override - public void begin(ActivityCancellationCallback callback) { - this.callback = callback; - } - - @Override - public void end() { } - - @Override - public String toString() { - return "Sleeping"; - } - - @Override - public Vector2i[] getTargetLocations() { - return null; - } - -} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/WanderActivity.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/WanderActivity.java deleted file mode 100644 index c340b27..0000000 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/agents/pawn/WanderActivity.java +++ /dev/null @@ -1,69 +0,0 @@ -package xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn; - -import xyz.valnet.engine.math.Vector2i; -import xyz.valnet.hadean.gameobjects.terrain.Terrain; -import xyz.valnet.hadean.gameobjects.terrain.Tile; - -// TODO actually implement this activity. -public class WanderActivity extends Activity { - - // TODO implement fun? - // private Needs needs; - - private Terrain terrain; - - public WanderActivity(Terrain terrain) { - this.terrain = terrain; - } - - @Override - public boolean isUrgent() { - return false; - } - - @Override - public float getBenefit() { - return 0.0f; - } - - @Override - public boolean isValid() { - return true; - } - - @Override - public void act(float dTime) { - callback.apply(this); - } - - ActivityCancellationCallback callback; - - @Override - public void begin(ActivityCancellationCallback callback) { - this.callback = callback; - - Tile tile = terrain.getRandomWalkableTile(); - if(tile == null) { - callback.apply(this); - return; - } - target = tile.getCoords(); - } - - @Override - public void end() { } - - @Override - public String toString() { - return "Wandering"; - } - - private Vector2i target = null; - - @Override - public Vector2i[] getTargetLocations() { - if(target == null) return null; - return new Vector2i[] { target }; - } - -} 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 index ec3ce14..65fd093 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Construction.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Construction.java @@ -49,7 +49,7 @@ public abstract class Construction extends Buildable { @Override public Vector2i[] getItemDropoffLocations() { - return getWorldBox().getBorders(); + return getSelectionWorldBox().getBorders(); } } @@ -70,7 +70,7 @@ public abstract class Construction extends Buildable { @Override public Vector2i[] getWorkablePositions() { - return getWorldBox().getBorders(); + return getSelectionWorldBox().getBorders(); } @Override @@ -127,7 +127,7 @@ public abstract class Construction extends Buildable { Assets.flat.popColor(); if(getBuildProgress() > 0) { - camera.drawProgressBar(getBuildProgress(), getWorldBox()); + camera.drawProgressBar(getBuildProgress(), getSelectionWorldBox()); } } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/MasonWorkshop.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/MasonWorkshop.java index c1737b0..72fc384 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/MasonWorkshop.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/MasonWorkshop.java @@ -92,7 +92,7 @@ public class MasonWorkshop extends Construction implements IWorkshop { @Override public Vector2i[] getWorkablePositions() { - return getWorldBox().getBorders(); + return getSelectionWorldBox().getBorders(); } @Override @@ -102,7 +102,7 @@ public class MasonWorkshop extends Construction implements IWorkshop { @Override public TileBox getSpawnableArea() { - return getWorldBox().asTileBox(); + return getSelectionWorldBox().asTileBox(); } private Job job = null; 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 9f1f91b..884d0c8 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 @@ -21,11 +21,11 @@ public class Quarry extends Construction { private float digProgress = 0; private void tryCreateDigJob() { - if(!isBuilt()) return; + if (!isBuilt()) return; if (digJob != null) return; if (terrain.getTile(getWorldPosition().xy().south().east()).has(Boulder.class)) return; - digJob = get(JobBoard.class).postSimpleWorkJob(new SimpleWorkable("Mine at Quarry", 5000, () -> { + digJob = get(JobBoard.class).post(new SimpleWorkable("Mine at Quarry", 5000, () -> { return new Vector2i[] { getWorldPosition().xy().south().east() }; @@ -80,7 +80,7 @@ public class Quarry extends Construction { if(!isBuilt()) return; if(digJob != null && !digJob.isCompleted() && digProgress > 0) { - camera.drawProgressBar(digProgress, getWorldBox()); + camera.drawProgressBar(digProgress, getSelectionWorldBox()); } } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Wall.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Wall.java index 754cd46..e268ac6 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Wall.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Wall.java @@ -45,7 +45,7 @@ public class Wall extends Construction implements IPingable { @Override public void ping() { - Vector2i pos = getWorldBox().a.asInt(); + Vector2i pos = getSelectionWorldBox().a.asInt(); wallSides = EnumSet.noneOf(Direction.class); Tile north = terrain.getTile(pos.x, pos.y - 1); diff --git a/src/main/java/xyz/valnet/hadean/interfaces/IItemReceiver.java b/src/main/java/xyz/valnet/hadean/interfaces/IItemReceiver.java index 21d6601..09898f2 100644 --- a/src/main/java/xyz/valnet/hadean/interfaces/IItemReceiver.java +++ b/src/main/java/xyz/valnet/hadean/interfaces/IItemReceiver.java @@ -8,4 +8,5 @@ import xyz.valnet.hadean.gameobjects.worldobjects.items.Item; public interface IItemReceiver extends Serializable { public boolean receive(Item item); public Vector2i[] getItemDropoffLocations(); + public String getName(); } diff --git a/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java b/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java index ab3957c..8b515ba 100644 --- a/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java +++ b/src/main/java/xyz/valnet/hadean/interfaces/ISelectable.java @@ -22,7 +22,7 @@ public interface ISelectable { } } - public Box getWorldBox(); + public Box getSelectionWorldBox(); public Action[] getActions(); public void runAction(Action action); public Detail[] getDetails(); diff --git a/src/main/java/xyz/valnet/hadean/interfaces/IWorkable.java b/src/main/java/xyz/valnet/hadean/interfaces/IWorkable.java index 0df9104..d52b086 100644 --- a/src/main/java/xyz/valnet/hadean/interfaces/IWorkable.java +++ b/src/main/java/xyz/valnet/hadean/interfaces/IWorkable.java @@ -5,9 +5,21 @@ import java.io.Serializable; import xyz.valnet.engine.math.Vector2i; public interface IWorkable extends Serializable { + + public enum WorkType { + Construct("Construct"), + Farm("Till"), + Chop("Chop"); + + public final String verb; + + private WorkType(String verb) { + this.verb = verb; + } + } + + public WorkType getWorkType(); public boolean doWork(float dTime); public Vector2i[] getWorkablePositions(); - // we should honestly switch to only have names of jobs not steps... - @Deprecated - public String getJobName(); + public String getName(); } diff --git a/src/main/java/xyz/valnet/hadean/pathfinding/IPathfinder.java b/src/main/java/xyz/valnet/hadean/pathfinding/IPathfinder.java index 9e16107..16061c0 100644 --- a/src/main/java/xyz/valnet/hadean/pathfinding/IPathfinder.java +++ b/src/main/java/xyz/valnet/hadean/pathfinding/IPathfinder.java @@ -4,5 +4,14 @@ import xyz.valnet.engine.math.Vector2i; public interface IPathfinder { public Path getPath(int x1, int y1, int x2, int y2); + public default Path getPath(Vector2i u, int x2, int y2) { + return getPath(u.x, u.y, x2, y2); + } + public default Path getPath(int x1, int y1, Vector2i v) { + return getPath(x1, y1, v.x, v.y); + } + public default Path getPath(Vector2i u, Vector2i v) { + return getPath(u.x, u.y, v.x, v.y); + } public Path getBestPath(Vector2i src, Vector2i[] dsts); } diff --git a/src/main/java/xyz/valnet/hadean/pathfinding/Path.java b/src/main/java/xyz/valnet/hadean/pathfinding/Path.java index bdc9659..a3ebfde 100644 --- a/src/main/java/xyz/valnet/hadean/pathfinding/Path.java +++ b/src/main/java/xyz/valnet/hadean/pathfinding/Path.java @@ -28,7 +28,7 @@ public class Path implements Iterable, Serializable { } public Node getDestination() { - return nodes.firstElement(); + return dst; } public boolean isComplete() { diff --git a/src/main/java/xyz/valnet/hadean/scenes/GameScene.java b/src/main/java/xyz/valnet/hadean/scenes/GameScene.java index 8dc2090..86b48d4 100644 --- a/src/main/java/xyz/valnet/hadean/scenes/GameScene.java +++ b/src/main/java/xyz/valnet/hadean/scenes/GameScene.java @@ -41,7 +41,7 @@ public class GameScene extends SceneGraph { objects.add(new JobBoard()); objects.add(new Clock()); - for(int i = 0; i < 5; i ++) { + for(int i = 0; i < 1; i ++) { objects.add(new Pawn()); } diff --git a/src/main/java/xyz/valnet/hadean/util/Action.java b/src/main/java/xyz/valnet/hadean/util/Action.java index f1e4d2e..a2b9271 100644 --- a/src/main/java/xyz/valnet/hadean/util/Action.java +++ b/src/main/java/xyz/valnet/hadean/util/Action.java @@ -39,4 +39,8 @@ public class Action { Action[] merged = mergeActions(rest); return mergeActions(first, merged); } + + public String toString() { + return name; + } }