basic work jobs work

shops
Ivory 2023-10-22 20:21:07 -04:00
commit fd64b53d6c
39 changed files with 755 additions and 313 deletions

View File

@ -123,7 +123,7 @@ public class App {
// Get the resolution of the primary monitor
long primaryMonitor = glfwGetPrimaryMonitor();
PointerBuffer monitors = glfwGetMonitors();
primaryMonitor = monitors.get(1);
primaryMonitor = monitors.get(0);
GLFWVidMode vidmode = glfwGetVideoMode(primaryMonitor);
IntBuffer monitorX = stack.mallocInt(1); // int*
IntBuffer monitorY = stack.mallocInt(1); // int*

View File

@ -74,4 +74,9 @@ public class Vector2i implements Serializable {
return new Vector2i(this.x + b.x, this.y + b.y);
}
public Vector2i[] asArray() {
return new Vector2i[] {
this
};
}
}

View File

@ -1,13 +1,16 @@
package xyz.valnet.engine.scenegraph;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.SceneGraph.GameObjectCallback;
import xyz.valnet.hadean.interfaces.ISceneReferent;
import xyz.valnet.hadean.util.Pair;
public class GameObject implements IRenderable, ITickable, Serializable {
public class GameObject implements IRenderable, ITickable, Serializable, ISceneReferent {
private transient SceneGraph scene;
private boolean created = false;
@ -41,26 +44,6 @@ public class GameObject implements IRenderable, ITickable, Serializable {
return obj;
}
@Override
public void render() {}
@Override
public void renderAlpha() {}
@Override
public void update(float dTime) {}
// call order goes from top to bottom \/\/\/
// ready is called before scene linkage, and serves to initialize
// values that may be needed before incoming requests.
protected void ready() {}
// connect is solely for ensuring links to other objects. get() and getAll()
protected void connect() {}
// create is guaranteed to only run once for an object, even after save/load
protected void create() {}
// start is called any time the object is added to a scene
protected void start() {}
public final void addedToScene() {
connect();
if(!created) {
@ -94,19 +77,50 @@ public class GameObject implements IRenderable, ITickable, Serializable {
return scene.getFPS();
}
protected void onAddGameObject(GameObjectCallback listener) {
protected final void onAddGameObject(GameObjectCallback listener) {
scene.registerAddListener(listener);
}
protected void onRemoveGameObject(GameObjectCallback listener) {
protected final void onRemoveGameObject(GameObjectCallback listener) {
scene.registerRemoveListener(listener);
}
protected Vector2i getBufferDimensions() {
protected final Vector2i getBufferDimensions() {
return scene.getBufferDimensions();
}
protected void beforeRemoved() {}
private final Set<GameObjectCallback> removedListeners = new HashSet<>();
protected void afterRemoved() {}
@Override
public final void onRemoved(GameObjectCallback listener) {
removedListeners.add(listener);
}
protected final void finalized() {
for(var listener : removedListeners) {
listener.apply(this);
}
}
protected void cleanup() {}
@Override
public void render() {}
@Override
public void renderAlpha() {}
@Override
public void update(float dTime) {}
// call order goes from top to bottom \/\/\/
// ready is called before scene linkage, and serves to initialize
// values that may be needed before incoming requests.
protected void ready() {}
// connect is solely for ensuring links to other objects. get() and getAll()
protected void connect() {}
// create is guaranteed to only run once for an object, even after save/load
protected void create() {}
// start is called any time the object is added to a scene
protected void start() {}
}

View File

@ -23,9 +23,9 @@ import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab;
import xyz.valnet.hadean.util.Pair;
public abstract class SceneGraph implements IScene {
protected final List<GameObject> objects = new ArrayList<GameObject>();
private final List<GameObject> newObjects = new ArrayList<GameObject>();
private final List<GameObject> removeObjects = new ArrayList<GameObject>();
private final Set<GameObject> objects = new HashSet<GameObject>();
private final Set<GameObject> newObjects = new HashSet<GameObject>();
private final Set<GameObject> removeObjects = new HashSet<GameObject>();
private IMouseCaptureArea hoveredMouseListener = null;
@ -159,7 +159,11 @@ public abstract class SceneGraph implements IScene {
@Override
public void enable(Game game) {
this.game = game;
this.construct();
Set<GameObject> scene = new HashSet<GameObject>();
this.construct(scene);
this.objects.clear();
this.objects.addAll(scene);
for(GameObject obj : objects) {
addObjectToCache(obj);
@ -190,7 +194,7 @@ public abstract class SceneGraph implements IScene {
}
}
protected abstract void construct();
protected abstract void construct(Set<GameObject> scene);
@Override
public void disable() {
@ -217,11 +221,11 @@ public abstract class SceneGraph implements IScene {
public void remove(GameObject obj) {
removeObjects.add(obj);
obj.beforeRemoved();
obj.cleanup();
for(GameObjectCallback listener : onRemoveListeners) {
listener.apply(obj);
}
obj.afterRemoved();
obj.finalized();
}
public boolean inScene(GameObject gameObject) {

View File

@ -0,0 +1,21 @@
package xyz.valnet.engine.util;
import java.lang.ref.WeakReference;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.hadean.interfaces.ISceneReferent;
public class SceneReference<T extends ISceneReferent> extends WeakReference<T> {
public SceneReference(T ref) {
super(ref);
ref.onRemoved((GameObject obj) -> {
this.clear();
});
}
public String toString() {
var o = this.get();
if(o == null) return "";
return "[" + o.toString() + "]";
}
}

View File

@ -1,13 +1,25 @@
package xyz.valnet.hadean.enums;
// TODO skills such as
// upper body strength
// lower body strength
// finess
// plant knowledge
// construction knowledge
// for now, this isnt meant to be any of that. simply categories for turning work on and off for pawns.
public enum WorkType {
Construct("Construct"),
Farm("Till"),
Chop("Chop");
Basic("basic", "Work on"),
Construct("building", "Construct"),
Farm("farming", "Till"),
Chop("felling", "Chop");
public final String verb;
public final String idString;
private WorkType(String verb) {
private WorkType(String idString, String verb) {
this.idString = idString;
this.verb = verb;
}
}

View File

@ -0,0 +1,8 @@
package xyz.valnet.hadean.functions;
import java.io.Serializable;
@FunctionalInterface
public interface FCompleted extends Serializable {
public void apply();
}

View File

@ -0,0 +1,10 @@
package xyz.valnet.hadean.functions;
import java.io.Serializable;
import xyz.valnet.engine.math.Vector2i;
@FunctionalInterface
public interface FGetPositions extends Serializable {
public Vector2i[] get();
}

View File

@ -0,0 +1,8 @@
package xyz.valnet.hadean.functions;
import java.io.Serializable;
@FunctionalInterface
public interface FProgressEvent extends Serializable {
public void progress(float progress);
}

View File

@ -24,7 +24,7 @@ import static xyz.valnet.engine.util.Math.lerp;
public class Camera extends GameObject implements ITransient, IMouseCaptureArea {
private int tileWidth = 32;
private int tileWidth = 16;
private Vector2f focus = new Vector2f(0, 0);

View File

@ -1,41 +1,79 @@
package xyz.valnet.hadean.gameobjects.jobs;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.util.SceneReference;
import xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn.Pawn;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.interfaces.IItemReceiver;
import xyz.valnet.hadean.interfaces.DItemRequest;
import xyz.valnet.hadean.interfaces.IDestination;
import xyz.valnet.hadean.interfaces.IWorkable;
// TODO create a subtype of job for jobs with
// locations so pawn can generically path to it
public sealed interface Job {
record Work(
IWorkable workable
) implements Job {
SceneReference<IWorkable> workable
) implements Job, IDestination {
@Override
public String getShortDescription() {
return workable.getWorkType().verb + " " + workable.getWorkableName();
return workable.get().getWorkType().verb + " " + workable.get().getWorkableName();
}
@Override
public boolean isValidForWorker(Pawn worker) {
var workable = this.workable().get();
if(workable == null) return false;
if(workable.getWorkablePositions() == null) return false;
return true;
}
public Vector2i[] getWorkablePositions() {
var workable = this.workable().get();
if(workable == null) return new Vector2i[] {};
return workable.getDestinations();
}
public void doWork(float dTime) {
var workable = this.workable().get();
if(workable == null) return;
workable.doWork(dTime);
}
@Override
public Vector2i[] getDestinations() {
var workable = this.workable().get();
if(workable == null) return null;
return workable.getDestinations();
}
}
record Haul(
Item item,
IItemReceiver rcvr
record ItemRequest(
SceneReference<DItemRequest> recvr
) implements Job {
@Override
public String getShortDescription() {
return "Haul " + item.getName() + " to " + rcvr.getName();
return "Fulfilling " + recvr.get().getName() + " item Request";
}
@Override
public boolean isValidForWorker(Pawn worker) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'isValidForWorker'");
}
}
record HaulToStockpile(
SceneReference<Item> item
) implements Job {
@Override
public String getShortDescription() {
return "Haul " + item.get().getName() + " to stockpile";
}
@Override
public boolean isValidForWorker(Pawn worker) {
return item.get() != null;
}
}
public String getShortDescription();
// private JobBoard board;
// public void connect(JobBoard board) {
// this.board = boaard;
// }
// public void close() {
// board.close(this);
// }
public boolean isValidForWorker(Pawn worker);
}

View File

@ -1,34 +1,71 @@
package xyz.valnet.hadean.gameobjects.jobs;
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.Map.Entry;
import java.util.Set;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.engine.util.SceneReference;
import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab;
import xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn.Pawn;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.interfaces.DItemRequest;
import xyz.valnet.hadean.interfaces.IWorkable;
public class JobBoard extends GameObject {
private Set<Job> availableJobs = new HashSet<Job>();
private List<Job> toRemove = new ArrayList<Job>();
private Map<Pawn, Job> allocations = new HashMap<Pawn, Job>();
// private Set<Job> toRemove = new HashSet<Job>();
// private Map<Pawn, Job> allocations = new HashMap<Pawn, Job>();
public Job postJob(Job job) {
public Job post(Job job) {
availableJobs.add(job);
return job;
}
public Job post(IWorkable workable) {
var job = new Job.Work(workable);
return postJob(job);
SceneReference<IWorkable> ref = new SceneReference<IWorkable>(workable);
var job = new Job.Work(ref);
return post(job);
}
public Job post(Item item) {
var job = new Job.HaulToStockpile(item.asRef());
return post(job);
}
public Job post(DItemRequest request) {
var job = new Job.ItemRequest(request.asRef());
return post(job);
}
public void rescind(Job job) {
if(!availableJobs.contains(job)) return;
availableJobs.remove(job);
}
// === migrate it may be idk
public Job takeJob(Pawn worker) {
if(availableJobs.isEmpty()) return null;
Job[] jobs = new Job[availableJobs.size()];
availableJobs.toArray(jobs);
Job job = jobs[(int) (Math.floor(Math.random() * jobs.length))];
DebugTab.log("" + worker.getName() + " took job: " + job.getShortDescription());
// allocations.put(worker, job);
availableJobs.remove(job);
return job;
}
// ================= lawl old shit
@ -62,40 +99,40 @@ public class JobBoard extends GameObject {
@Deprecated
public String getTakenJobs() {
String str = "";
for(Entry<Pawn, Job> allocation : allocations.entrySet()) {
str += " " + allocation.getKey().getName() + ": " + allocation.getValue().getShortDescription() + "\n";
}
String str = "Unknown for now, as jobboard does not track taken jobs.";
// for(Entry<Pawn, Job> allocation : allocations.entrySet()) {
// str += " " + allocation.getKey().getName() + ": " + allocation.getValue().getShortDescription() + "\n";
// }
return str;
}
public void close(Job job) {
if(allocations.values().contains(job)) {
Set<Pawn> workers = new HashSet<>();
for(var entry : allocations.entrySet()) {
if(entry.getValue() == job) workers.add(entry.getKey());
}
for(var worker : workers) {
worker.cancelJob(job);
}
}
}
// public void close(Job job) {
// if(allocations.values().contains(job)) {
// Set<Pawn> workers = new HashSet<>();
// for(var entry : allocations.entrySet()) {
// if(entry.getValue() == job) workers.add(entry.getKey());
// }
// for(var worker : workers) {
// worker.cancelJob(job);
// }
// }
// }
public class ENoJobsAvailable extends Error {}
// public class ENoJobsAvailable extends Error {}
public Job takeJob(Pawn worker) {
if(availableJobs.size() == 0) return null;
// public Job takeJob(Pawn worker) {
// if(availableJobs.size() == 0) return null;
Job[] jobs = new Job[availableJobs.size()];
availableJobs.toArray(jobs);
Job job = jobs[(int) (Math.floor(Math.random() * jobs.length))];
// Job[] jobs = new Job[availableJobs.size()];
// availableJobs.toArray(jobs);
// Job job = jobs[(int) (Math.floor(Math.random() * jobs.length))];
DebugTab.log("" + worker.getName() + " took job: " + job.getShortDescription());
// DebugTab.log("" + worker.getName() + " took job: " + job.getShortDescription());
allocations.put(worker, job);
availableJobs.remove(job);
// allocations.put(worker, job);
// availableJobs.remove(job);
return job;
}
// return job;
// }
}

View File

@ -1,57 +0,0 @@
package xyz.valnet.hadean.gameobjects.jobs;
import java.io.Serializable;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.interfaces.IWorkable;
public class SimpleWorkable implements IWorkable {
private final String name;
private float work = 0;
private float MAX_WORK = 0;
private IProgressUpdateCallback callback;
private IGetPositionsFunction positions;
private final WorkType workType;
@FunctionalInterface
public interface IGetPositionsFunction extends Serializable {
public Vector2i[] get();
}
@FunctionalInterface
public interface IProgressUpdateCallback extends Serializable {
public void progress(float progress);
}
public SimpleWorkable(String name, float maxWork, WorkType type, IGetPositionsFunction positionsFunction, IProgressUpdateCallback callback) {
this.name = name;
this.MAX_WORK = maxWork;
this.positions = positionsFunction;
this.callback = callback;
this.workType = type;
}
@Override
public boolean doWork(float dTime) {
work += dTime;
callback.progress(work / MAX_WORK);
return work >= MAX_WORK;
}
@Override
public Vector2i[] getWorkablePositions() {
return positions.get();
}
@Override
public final String getWorkableName() {
return name;
}
@Override
public WorkType getWorkType() {
return workType;
}
}

View File

@ -7,7 +7,7 @@ import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.interfaces.IItemReceiver;
import xyz.valnet.hadean.interfaces.DItemRequest;
import xyz.valnet.hadean.interfaces.IWorkshop;
public class WorkOrder extends GameObject {
@ -72,29 +72,29 @@ public class WorkOrder extends GameObject {
}
public Job createNextJob(IWorkshop shop) {
Job job = new Job("Cut brickies");
job.addStep(job.new PickupItemByPredicate(Boulder.BOULDER_PREDICATE));
job.addStep(job.new DropoffPredicateAtItemReceiver(new IItemReceiver() {
@Override
public boolean receive(Item item) {
return null;
// Job job = new Job("Cut brickies");
// job.addStep(job.new PickupItemByPredicate(Boulder.BOULDER_PREDICATE));
// job.addStep(job.new DropoffPredicateAtItemReceiver(new IItemReceiver() {
// @Override
// public boolean receive(Item item) {
return false;
}
// return false;
// }
@Override
public Vector2i[] getItemDropoffLocations() {
return shop.getWorkablePositions();
}
}, Boulder.BOULDER_PREDICATE));
job.addStep(job.new Work(shop));
job.registerCompletedListener(() -> {
decreaseCount();
});
job.registerClosedListener(() -> {
relatedJobs.remove(job);
});
get(JobBoard.class).postJob(job);
return job;
// @Override
// public Vector2i[] getItemDropoffLocations() {
// return shop.getWorkablePositions();
// }
// }, Boulder.BOULDER_PREDICATE));
// job.addStep(job.new Work(shop));
// job.registerCompletedListener(() -> {
// decreaseCount();
// });
// job.registerClosedListener(() -> {
// relatedJobs.remove(job);
// });
// get(JobBoard.class).post(job);
// return job;
}
}

View File

@ -0,0 +1,76 @@
package xyz.valnet.hadean.gameobjects.jobs;
import java.util.HashSet;
import java.util.Set;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.SceneGraph.GameObjectCallback;
import xyz.valnet.hadean.enums.WorkType;
import xyz.valnet.hadean.functions.FGetPositions;
import xyz.valnet.hadean.functions.FProgressEvent;
import xyz.valnet.hadean.functions.FCompleted;
import xyz.valnet.hadean.interfaces.ISceneReferent;
import xyz.valnet.hadean.interfaces.IWorkable;
public class Workable implements IWorkable {
private final String name;
private float work = 0;
private float MAX_WORK = 0;
private FProgressEvent progressCallback;
private FGetPositions positions;
private final WorkType workType;
private ISceneReferent linkedSceneObject;
private Set<FCompleted> completedCallback = new HashSet<>();
public Workable(String name, float maxWork, WorkType type, FGetPositions positionsFunction, FProgressEvent progressCallback, ISceneReferent linkedSceneObject) {
this.name = name;
this.MAX_WORK = maxWork;
this.positions = positionsFunction;
this.progressCallback = progressCallback;
this.workType = type;
this.linkedSceneObject = linkedSceneObject;
}
public void onCompleted(FCompleted cb) {
completedCallback.add(cb);
}
private void finished() {
for(var cb : completedCallback)
cb.apply();
}
@Override
public boolean doWork(float dTime) {
if (work >= MAX_WORK) return true;
work += dTime;
progressCallback.progress(work / MAX_WORK);
if (work >= MAX_WORK) {
finished();
return true;
}
return false;
}
@Override
public Vector2i[] getWorkablePositions() {
return positions.get();
}
@Override
public final String getWorkableName() {
return name;
}
@Override
public WorkType getWorkType() {
return workType;
}
@Override
public void onRemoved(GameObjectCallback callback) {
linkedSceneObject.onRemoved(callback);
}
}

View File

@ -0,0 +1,74 @@
package xyz.valnet.hadean.gameobjects.jobs;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.enums.WorkType;
import xyz.valnet.hadean.functions.FGetPositions;
import xyz.valnet.hadean.functions.FProgressEvent;
import xyz.valnet.hadean.functions.FCompleted;
import xyz.valnet.hadean.interfaces.ISceneReferent;
public class WorkableBuilder {
private String name = "work area";
private float maxWork = 1000;
private WorkType type = WorkType.Basic;
private FGetPositions getPositionsFunction;
private FProgressEvent progressUpdateCallback;
private FCompleted completed;
private ISceneReferent linked;
public WorkableBuilder(ISceneReferent linked) {
this.linked = linked;
}
public WorkableBuilder type(WorkType t) {
type = t;
return this;
}
public WorkableBuilder work(float f) {
maxWork = f;
return this;
}
public WorkableBuilder progress(FProgressEvent f) {
this.progressUpdateCallback = f;
return this;
}
public WorkableBuilder name(String n) {
name = n;
return this;
}
public WorkableBuilder positions(Vector2i[] poss) {
getPositionsFunction = () -> {
return poss;
};
return this;
}
public WorkableBuilder position(Vector2i pos) {
var arr = pos.asArray();
getPositionsFunction = () -> {
return arr;
};
return this;
}
public WorkableBuilder positions(FGetPositions f) {
getPositionsFunction = f;
return this;
}
public WorkableBuilder completed(FCompleted f) {
this.completed = f;
return this;
}
public Workable build() {
var o = new Workable(name, maxWork, type, getPositionsFunction, progressUpdateCallback, linked);
o.onCompleted(completed);
return o;
}
}

View File

@ -11,7 +11,7 @@ import xyz.valnet.hadean.pathfinding.IPathable;
public class Terrain extends GameObject implements IPathable, IWorldBoundsAdapter {
public static final int WORLD_SIZE = 24;
public static final int WORLD_SIZE = 48;
public static final int TILE_SIZE = 8;
// public static int left, top;

View File

@ -8,6 +8,7 @@ import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.math.Vector4i;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.hadean.HadeanGame;
import xyz.valnet.hadean.enums.WorkType;
import xyz.valnet.hadean.gameobjects.worldobjects.Tree;
import xyz.valnet.hadean.gameobjects.worldobjects.WorldObject;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder;

View File

@ -8,6 +8,7 @@ import java.util.Map;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.engine.scenegraph.ITransient;
import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab;
import xyz.valnet.hadean.input.Button;
import xyz.valnet.hadean.input.IButtonListener;
import xyz.valnet.hadean.input.SimpleButton;
@ -16,18 +17,11 @@ import xyz.valnet.hadean.util.Layers;
public class BottomBar extends GameObject implements IButtonListener, ITransient {
public static final int bottomBarHeight = 32;
private int screenWidth = 1024;
private Map<Button, IBottomBarItem> btnItemTable = new HashMap<Button, IBottomBarItem>();
private List<IBottomBarItem> items = new ArrayList<IBottomBarItem>();
@Override
public void start() {
items.clear();
clearButtons();
}
public void registerButton(IBottomBarItem newItem) {
Vector2i screen = getBufferDimensions();
clearButtons();
@ -41,6 +35,7 @@ public class BottomBar extends GameObject implements IButtonListener, ITransient
int w = r - l;
Button btn = new SimpleButton(item.getTabName(), l, screen.y - bottomBarHeight, w, bottomBarHeight, Layers.BOTTOM_BAR);
// Button btn = new SimpleButton(item.getTabName(), 100 * i, 100, 100, 100, Layers.BOTTOM_BAR);
if(item.isButtonClickSilent()) btn = btn.setClickSound(false);
btn.registerClickListener(this);
add(btn);

View File

@ -79,7 +79,6 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener
updateActionsFlag = false;
for(ISelectable selectable : selected) {
for(Action action : selectable.getActions()) {
DebugTab.log(action.name);
actions.add(action);
}
}

View File

@ -58,7 +58,7 @@ public class Tree extends WorldObject implements ISelectable, IWorkable {
if(chopJob == null) {
chopJob = get(JobBoard.class).post(this);
} else {
get(JobBoard.class).close(chopJob);
get(JobBoard.class).rescind(chopJob);
chopJob = null;
}
}
@ -111,8 +111,8 @@ public class Tree extends WorldObject implements ISelectable, IWorkable {
}
@Override
protected void beforeRemoved() {
super.beforeRemoved();
protected void cleanup() {
super.cleanup();
Vector2i pos = getWorldPosition().xy();
add(new Log(pos.x, pos.y));
}

View File

@ -129,7 +129,7 @@ public abstract class WorldObject extends GameObject implements IWorldObject {
}
@Override
protected void beforeRemoved() {
protected void cleanup() {
for(Tile tile : this.getTiles()) {
tile.removeThing(this);
}

View File

@ -16,7 +16,7 @@ import xyz.valnet.engine.shaders.SimpleShader;
import xyz.valnet.hadean.HadeanGame;
import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab;
import xyz.valnet.hadean.gameobjects.worldobjects.WorldObject;
import xyz.valnet.hadean.interfaces.IDestinationProvider;
import xyz.valnet.hadean.interfaces.IDestination;
import xyz.valnet.hadean.interfaces.ISelectable;
import xyz.valnet.hadean.pathfinding.AStarPathfinder;
import xyz.valnet.hadean.pathfinding.IPathfinder;
@ -100,14 +100,14 @@ public abstract class Agent extends WorldObject implements ISelectable {
}
private Path path;
private IDestinationProvider dsts;
private IDestination dsts;
public void stopPathing() {
path = null;
dsts = null;
}
public boolean goTo(IDestinationProvider location) {
public boolean goTo(IDestination location) {
if(isAnimating()) {
stopPathing();
dsts = location;
@ -133,7 +133,7 @@ public abstract class Agent extends WorldObject implements ISelectable {
} while (terrain.isOutOfBounds(random.x, random.y));
goTo(new IDestinationProvider.SingleDestination(random));
goTo(new IDestination.Single(random));
}
@Override
@ -212,6 +212,10 @@ public abstract class Agent extends WorldObject implements ISelectable {
protected abstract void idle(float dTime);
protected boolean isPathing() {
return dsts != null;
}
public Vector2f getCalculatedPosition() {
if(!isAnimating())
return getWorldPosition().xy().asFloat();

View File

@ -11,14 +11,16 @@ import xyz.valnet.engine.util.Names;
import xyz.valnet.hadean.HadeanGame;
import xyz.valnet.hadean.gameobjects.jobs.Job;
import xyz.valnet.hadean.gameobjects.jobs.JobBoard;
import xyz.valnet.hadean.gameobjects.jobs.JobBoard.ENoJobsAvailable;
import xyz.valnet.hadean.gameobjects.terrain.Terrain;
import xyz.valnet.hadean.gameobjects.worldobjects.agents.Agent;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.util.Action;
import xyz.valnet.hadean.util.Assets;
import xyz.valnet.hadean.util.Layers;
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 {
@ -29,23 +31,28 @@ public class Pawn extends Agent {
private Job currentJob = null;
private void doWorkJob(Job.Work workJob, float dTime) {
if(!this.getWorldPosition().xy().isOneOf(workJob.workable().getDestinations())) {
if(goTo(workJob.workable())) {
if(!this.getWorldPosition().xy().isOneOf(workJob.getWorkablePositions())) {
if(goTo(workJob)) {
return;
} else {
cancelJob(currentJob);
}
}
workJob.workable().doWork(dTime);
workJob.doWork(dTime);
}
private void doHaulJob(Job.Haul haulJob, float dTime) {
private void doHaulToStockpileJob(Job.HaulToStockpile haulJob, float dTime) {
if(inventory.contains(haulJob.item().get()))
}
private void doJob(float dTime) {
if(currentJob instanceof Job.Haul haul) {
doHaulJob(haul, dTime);
if(!currentJob.isValidForWorker(this)) {
get(JobBoard.class).post(currentJob);
currentJob = null;
return;
}
if(currentJob instanceof Job.HaulToStockpile haul) {
doHaulToStockpileJob(haul, dTime);
} else if (currentJob instanceof Job.Work work) {
doWorkJob(work, dTime);
}
@ -99,7 +106,15 @@ public class Pawn extends Agent {
@Override
public Detail[] getDetails() {
return new Detail[] {};
// return needs.getDetails();
Detail[] details = new Detail[] {
// new ObjectDetail<Activity>("Activity", currentActivity),
new BooleanDetail("Pathing", isPathing()),
new ObjectDetail<String>("Job", currentJob == null ? "null" : currentJob.toString()),
new ObjectDetail<Integer>("Inventory", inventory.size())
};
return details;
}
@Override

View File

@ -6,13 +6,14 @@ import java.util.List;
import xyz.valnet.engine.graphics.Color;
import xyz.valnet.engine.graphics.Sprite;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.SceneGraph.GameObjectCallback;
import xyz.valnet.hadean.enums.BuildType;
import xyz.valnet.hadean.gameobjects.jobs.Job;
import xyz.valnet.hadean.gameobjects.jobs.JobBoard;
import xyz.valnet.hadean.gameobjects.worldobjects.Buildable;
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.DItemRequest;
import xyz.valnet.hadean.interfaces.IWorkable;
import xyz.valnet.hadean.util.Action;
import xyz.valnet.hadean.util.Assets;
@ -31,32 +32,64 @@ public abstract class Construction extends Buildable {
return containedItems.size() >= getBuildingMaterialCount();
}
private void onItem(Item item) {
containedItems.add(item);
postNextJob();
}
private void postNextJob() {
if(!isBuildingMaterialSatisfied()) {
Job job = get(JobBoard.class).postSimpleItemRequirementJob(
"Haul items to building",
getBuildingMaterial(),
new IItemReceiver() {
@Override
public final boolean receive(Item item) {
if(item == null) return false;
if(!item.matches(getBuildingMaterial())) return false;
remove(item);
containedItems.add(item);
return true;
}
var itemRequest = new DItemRequest(this, "Haul items to building")
.accepts(getBuildingMaterial())
.positions(getSelectionWorldBox().getBorders())
.handleItem((item) -> onItem(item));
@Override
public Vector2i[] getItemDropoffLocations() {
return getSelectionWorldBox().getBorders();
}
var job = get(JobBoard.class).post(itemRequest);
// TODO -> job.onCompleted(postNextJob();)
}
);
job.registerClosedListener(() -> {
postNextJob();
});
// Job job = get(JobBoard.class).postSimpleItemRequirementJob(
// "Haul items to building",
// getBuildingMaterial(),
// new DItemRequest() {
// @Override
// public final boolean receive(Item item) {
// if(item == null) return false;
// if(!item.matches(getBuildingMaterial())) return false;
// remove(item);
// containedItems.add(item);
// return true;
// }
// @Override
// public Vector2i[] getItemDropoffLocations() {
// return getSelectionWorldBox().getBorders();
// }
// @Override
// public void onRemoved(GameObjectCallback callback) {
// // TODO Auto-generated method stub
// throw new UnsupportedOperationException("Unimplemented method 'onRemoved'");
// }
// @Override
// public String getName() {
// // TODO Auto-generated method stub
// throw new UnsupportedOperationException("Unimplemented method 'getName'");
// }
// @Override
// public IItemPredicate acceptedItems() {
// // TODO Auto-generated method stub
// throw new UnsupportedOperationException("Unimplemented method 'acceptedItems'");
// }
// }
// );
// job.registerClosedListener(() -> {
// postNextJob();
// });
return;
}
if(!isBuilt()) {

View File

@ -4,7 +4,7 @@ import xyz.valnet.engine.graphics.Sprite;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.gameobjects.jobs.Job;
import xyz.valnet.hadean.gameobjects.jobs.JobBoard;
import xyz.valnet.hadean.gameobjects.jobs.SimpleWorkable;
import xyz.valnet.hadean.gameobjects.jobs.WorkableBuilder;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder;
import xyz.valnet.hadean.interfaces.IItemPredicate;
import xyz.valnet.hadean.util.Assets;
@ -25,22 +25,39 @@ public class Quarry extends Construction {
if (digJob != null) return;
if (terrain.getTile(getWorldPosition().xy().south().east()).has(Boulder.class)) return;
digJob = get(JobBoard.class).post(new SimpleWorkable("Mine at Quarry", 5000, () -> {
return new Vector2i[] {
getWorldPosition().xy().south().east()
};
}, (progress) -> {
digProgress = progress;
}));
var workable = new WorkableBuilder(this)
.name("Quarry")
.position(getWorldPosition().xy().south().east())
.work(5000)
.progress((p) -> digProgress = p)
.completed(() -> {
digProgress = 0;
Vector2i dropPos = getWorldPosition().xy().south().east();
Boulder boulder = new Boulder(dropPos.x, dropPos.y);
add(boulder);
boulder.runAction(Boulder.HAUL);
digJob = null;
})
.build();
digJob.registerClosedListener(() -> {
digProgress = 0;
Vector2i dropPos = getWorldPosition().xy().south().east();
Boulder boulder = new Boulder(dropPos.x, dropPos.y);
add(boulder);
boulder.runAction(Boulder.HAUL);
digJob = null;
});
digJob = get(JobBoard.class).post(workable);
// digJob = get(JobBoard.class).post(new SimpleWorkable("Mine at Quarry", 5000, () -> {
// return new Vector2i[] {
// getWorldPosition().xy().south().east()
// };
// }, (progress) -> {
// digProgress = progress;
// }));
// digJob.registerClosedListener(() -> {
// digProgress = 0;
// Vector2i dropPos = getWorldPosition().xy().south().east();
// Boulder boulder = new Boulder(dropPos.x, dropPos.y);
// add(boulder);
// boulder.runAction(Boulder.HAUL);
// digJob = null;
// });
}
@Override
@ -79,7 +96,7 @@ public class Quarry extends Construction {
super.render();
if(!isBuilt()) return;
if(digJob != null && !digJob.isCompleted() && digProgress > 0) {
if(digJob != null && digProgress > 0) {
camera.drawProgressBar(digProgress, getSelectionWorldBox());
}
}

View File

@ -8,7 +8,17 @@ import xyz.valnet.hadean.util.detail.Detail;
public class Boulder extends Item {
public static IItemPredicate BOULDER_PREDICATE = (item) -> (item instanceof Boulder);
public static IItemPredicate BOULDER_PREDICATE = new IItemPredicate() {
@Override
public String getName() {
return "Boulder";
}
@Override
public boolean matches(Item item) {
return item instanceof Boulder;
}
};
public Boulder(int x, int y) {
setPosition(x, y);

View File

@ -2,20 +2,26 @@ package xyz.valnet.hadean.gameobjects.worldobjects.items;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.math.Vector4i;
import xyz.valnet.engine.util.SceneReference;
import xyz.valnet.hadean.gameobjects.jobs.Job;
import xyz.valnet.hadean.gameobjects.jobs.JobBoard;
import xyz.valnet.hadean.gameobjects.worldobjects.WorldObject;
import xyz.valnet.hadean.interfaces.IItemPredicate;
import xyz.valnet.hadean.interfaces.ISceneReferent;
import xyz.valnet.hadean.interfaces.ISelectable;
import xyz.valnet.hadean.util.Action;
import xyz.valnet.hadean.util.Assets;
import xyz.valnet.hadean.util.Layers;
public abstract class Item extends WorldObject implements ISelectable {
public abstract class Item extends WorldObject implements ISelectable, ISceneReferent {
protected JobBoard jobboard;
private Job haulJob = null;
public final SceneReference<Item> asRef() {
return new SceneReference<Item>(this);
}
@Override
protected void connect() {
super.connect();
@ -67,19 +73,14 @@ public abstract class Item extends WorldObject implements ISelectable {
private void cancelHaul() {
if(haulJob == null) return;
haulJob.close();
jobboard.rescind(haulJob);
haulJob = null;
}
private void markForHaul() {
if(haulJob != null) return;
haulJob = add(new Job("Haul " + this.getName()));
haulJob.addStep(haulJob.new PickupItem(this));
haulJob.addStep(haulJob.new DropoffAtStockpile(this));
haulJob.registerClosedListener(() -> {
haulJob = null;
});
jobboard.postJob(haulJob);
var j = new Job.HaulToStockpile(asRef());
jobboard.post(j);
}
public void destroy() {
@ -105,4 +106,8 @@ public abstract class Item extends WorldObject implements ISelectable {
public void setPosition(int x, int y, int w, int h) {
super.setPosition(x, y, w, h);
}
public String toString() {
return getGenericName();
}
}

View File

@ -13,7 +13,19 @@ import xyz.valnet.hadean.util.detail.Detail;
// when placed in a non stockpile, not just on create.
public class Log extends Item {
public static IItemPredicate LOG_PREDICATE = (item) -> (item instanceof Log);
public static IItemPredicate LOG_PREDICATE = new IItemPredicate() {
@Override
public boolean matches(Item item) {
return item instanceof Log;
}
@Override
public String getName() {
return "Any Log";
}
};
public Log(int x, int y) {
setPosition(x, y);

View File

@ -0,0 +1,92 @@
package xyz.valnet.hadean.interfaces;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.SceneGraph.GameObjectCallback;
import xyz.valnet.engine.util.SceneReference;
import xyz.valnet.hadean.functions.FGetPositions;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
public class DItemRequest implements ISceneReferent, Serializable {
@FunctionalInterface
public interface FItemHandler {
public void give(Item item);
}
private ISceneReferent linked;
private String name = "Default Request Name";
private IItemPredicate acceptedItems = IItemPredicate.NONE;
private FItemHandler itemHandler;
private FGetPositions positionsFn = () -> new Vector2i[] {};
public DItemRequest(ISceneReferent linked, String name) {
this.linked = linked;
}
// === fluent API
public DItemRequest accepts(IItemPredicate accepts) {
acceptedItems = accepts;
return this;
}
public DItemRequest handleItem(FItemHandler handler) {
itemHandler = handler;
return this;
}
public DItemRequest positions(Vector2i[] poss) {
positionsFn = () -> {
return poss;
};
return this;
}
public DItemRequest position(Vector2i pos) {
var arr = pos.asArray();
positionsFn = () -> {
return arr;
};
return this;
}
public DItemRequest positions(FGetPositions f) {
positionsFn = f;
return this;
}
// === public API
public Vector2i[] getLocations() {
return positionsFn.get();
}
public final boolean accepts(Item i) {
return acceptedItems.matches(i);
}
public void give(Item item) {
if(!accepts(item)) throw new Error("Unaccepted Item given to receiver!");
itemHandler.give(item);
item.destroy();
}
@Override
public void onRemoved(GameObjectCallback callback) {
linked.onRemoved(callback);
}
public String getName() {
return name;
}
public SceneReference<DItemRequest> asRef() {
return new SceneReference<DItemRequest>(this);
}
}

View File

@ -0,0 +1,21 @@
package xyz.valnet.hadean.interfaces;
import xyz.valnet.engine.math.Vector2i;
public interface IDestination {
public Vector2i[] getDestinations();
record Single(Vector2i dst) implements IDestination {
@Override
public Vector2i[] getDestinations() {
return new Vector2i[] { dst };
}
}
record Many(Vector2i[] dsts) implements IDestination {
@Override
public Vector2i[] getDestinations() {
return dsts;
}
}
}

View File

@ -1,16 +0,0 @@
package xyz.valnet.hadean.interfaces;
import xyz.valnet.engine.math.Vector2i;
public interface IDestinationProvider {
public Vector2i[] getDestinations();
record SingleDestination(Vector2i dst) implements IDestinationProvider {
@Override
public Vector2i[] getDestinations() {
return new Vector2i[] { dst };
}
}
}

View File

@ -4,7 +4,20 @@ import java.io.Serializable;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
@FunctionalInterface
public interface IItemPredicate extends Serializable {
public boolean matches(Item item);
public String getName();
public static IItemPredicate NONE = new IItemPredicate() {
@Override
public boolean matches(Item item) {
return false;
}
@Override
public String getName() {
return "Nothing";
}
};
}

View File

@ -1,20 +0,0 @@
package xyz.valnet.hadean.interfaces;
import java.io.Serializable;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
public interface IItemReceiver extends Serializable {
public boolean receive(Item item);
public Vector2i[] getItemDropoffLocations();
public String getName();
public default boolean give(Item item) {
if(receive(item)) {
item.destroy();
return true;
}
return false;
}
}

View File

@ -0,0 +1,7 @@
package xyz.valnet.hadean.interfaces;
import xyz.valnet.engine.scenegraph.SceneGraph.GameObjectCallback;
public interface ISceneReferent {
public void onRemoved(GameObjectCallback callback);
}

View File

@ -3,9 +3,10 @@ package xyz.valnet.hadean.interfaces;
import java.io.Serializable;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.util.SceneReference;
import xyz.valnet.hadean.enums.WorkType;
public interface IWorkable extends Serializable, IDestinationProvider {
public interface IWorkable extends Serializable, IDestination, ISceneReferent {
public WorkType getWorkType();
public boolean doWork(float dTime);
public Vector2i[] getWorkablePositions();
@ -16,4 +17,7 @@ public interface IWorkable extends Serializable, IDestinationProvider {
return getWorkablePositions();
}
public default SceneReference<IWorkable> asRef() {
return new SceneReference<>(this);
}
}

View File

@ -7,7 +7,7 @@ import java.util.List;
import java.util.Stack;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.interfaces.IDestinationProvider;
import xyz.valnet.hadean.interfaces.IDestination;
public class AStarPathfinder implements IPathfinder, Serializable {
@ -163,7 +163,7 @@ public class AStarPathfinder implements IPathfinder, Serializable {
}
@Override
public Path getBestPath(Vector2i src, IDestinationProvider provider) {
public Path getBestPath(Vector2i src, IDestination provider) {
var dsts = provider.getDestinations();

View File

@ -1,7 +1,7 @@
package xyz.valnet.hadean.pathfinding;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.interfaces.IDestinationProvider;
import xyz.valnet.hadean.interfaces.IDestination;
public interface IPathfinder {
public Path getPath(int x1, int y1, int x2, int y2);
@ -14,5 +14,5 @@ public interface IPathfinder {
public default Path getPath(Vector2i u, Vector2i v) {
return getPath(u.x, u.y, v.x, v.y);
}
public Path getBestPath(Vector2i src, IDestinationProvider dsts);
public Path getBestPath(Vector2i src, IDestination dsts);
}

View File

@ -1,5 +1,8 @@
package xyz.valnet.hadean.scenes;
import java.util.Set;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.engine.scenegraph.SceneGraph;
import xyz.valnet.engine.util.Names;
import xyz.valnet.hadean.gameobjects.ui.BottomBar;
@ -32,37 +35,34 @@ import xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn.Pawn;
public class GameScene extends SceneGraph {
@Override
protected void construct() {
protected void construct(Set<GameObject> scene) {
Names.loadNames();
objects.add(new Terrain());
objects.add(new Camera());
objects.add(new JobBoard());
objects.add(new Clock());
scene.add(new Terrain());
scene.add(new Camera());
scene.add(new JobBoard());
scene.add(new Clock());
for(int i = 0; i < 1; i ++) {
objects.add(new Pawn());
scene.add(new Pawn());
}
objects.add(new WorkOrderManager());
objects.add(new ExclusivityManager());
scene.add(new WorkOrderManager());
scene.add(new ExclusivityManager());
objects.add(new SelectionLayer());
objects.add(new BuildLayer());
scene.add(new SelectionLayer());
scene.add(new BuildLayer());
objects.add(new WorkshopOrdersUI());
objects.add(new SelectionUI());
scene.add(new WorkshopOrdersUI());
scene.add(new SelectionUI());
objects.add(new HoverQuery());
objects.add(new BottomBar());
objects.add(new BuildTab());
objects.add(new JobBoardTab());
objects.add(new DebugTab());
objects.add(new MenuTab());
// objects.add(new Popup());
scene.add(new HoverQuery());
scene.add(new BottomBar());
scene.add(new BuildTab());
scene.add(new JobBoardTab());
scene.add(new DebugTab());
scene.add(new MenuTab());
}
}