Compare commits
2 Commits
dd775f6d9c
...
496ecd9796
| Author | SHA1 | Date |
|---|---|---|
|
|
496ecd9796 | |
|
|
4bf7a092aa |
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package xyz.valnet.engine;
|
|||
|
||||
import static xyz.valnet.engine.util.Math.lerp;
|
||||
|
||||
import xyz.valnet.engine.graphics.Drawing;
|
||||
import xyz.valnet.engine.math.Matrix4f;
|
||||
import xyz.valnet.engine.math.Vector2i;
|
||||
import xyz.valnet.engine.scenegraph.IScene;
|
||||
|
|
@ -29,6 +30,7 @@ public abstract class Game {
|
|||
}
|
||||
|
||||
public void render() {
|
||||
Drawing.setLayer(0);
|
||||
scene.render();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String> buttonIdsToRemove = new ArrayList<String>();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public class SimpleShader extends Shader {
|
|||
|
||||
public SimpleShader(String vertPath, String fragPath) {
|
||||
super(vertPath, fragPath);
|
||||
pushColor(Color.white);
|
||||
}
|
||||
|
||||
public void pushColor(Color color) {
|
||||
|
|
|
|||
|
|
@ -2,35 +2,24 @@ package xyz.valnet.hadean;
|
|||
|
||||
import xyz.valnet.engine.App;
|
||||
import xyz.valnet.engine.Game;
|
||||
import xyz.valnet.engine.graphics.Color;
|
||||
import xyz.valnet.engine.graphics.Drawing;
|
||||
import xyz.valnet.engine.math.Matrix4f;
|
||||
import xyz.valnet.hadean.scenes.GameScene;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
|
||||
public class HadeanGame extends Game {
|
||||
public static final HadeanGame Hadean = new HadeanGame();
|
||||
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).run();
|
||||
new App(HADEAN_GAME).run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
Assets.flat.pushColor(Color.white);
|
||||
changeScene(new GameScene());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Drawing.setLayer(0);
|
||||
super.render();
|
||||
|
||||
if(!debugView) return;
|
||||
}
|
||||
|
||||
// receive the updated matrix every frame for the actual window.
|
||||
@Override
|
||||
public void updateViewMatrix(Matrix4f matrix) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public abstract class Designation<T extends ISelectable> extends GameObject impl
|
|||
Class<T> type = getType();
|
||||
List<T> things = getAll(type);
|
||||
for(ISelectable thing : things) {
|
||||
Box thingBox = thing.getWorldBox();
|
||||
Box thingBox = thing.getSelectionWorldBox();
|
||||
if(box.intersects(thingBox))
|
||||
designate((T) thing);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<Callback> closedListeners = new ArrayList<Callback>();
|
||||
private List<Callback> completedListeners = new ArrayList<Callback>();
|
||||
|
||||
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<Vector2i> positionSet = new HashSet<Vector2i>();
|
||||
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<JobStep> 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<JobStep>();
|
||||
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);
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Job> toRemove = new ArrayList<Job>();
|
||||
private Map<Pawn, Job> allocations = new HashMap<Pawn, Job>();
|
||||
|
||||
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<Pawn> toFire = new ArrayList<Pawn>();
|
||||
|
||||
for(Pawn worker : allocations.keySet()) {
|
||||
if(allocations.get(worker) == job) {
|
||||
toFire.add(worker);
|
||||
}
|
||||
}
|
||||
|
||||
for(Pawn worker : toFire) {
|
||||
allocations.remove(worker);
|
||||
}
|
||||
}
|
||||
|
||||
if(availableJobs.contains(job)) {
|
||||
availableJobs.remove(job);
|
||||
}
|
||||
});
|
||||
availableJobs.add(job);
|
||||
}
|
||||
|
||||
public boolean jobsAvailableForWorker(Pawn worker) {
|
||||
return getJobsForWorker(worker).size() != 0;
|
||||
}
|
||||
|
||||
private List<Job> getJobsForWorker(Pawn worker) {
|
||||
Vector2i workerLocation = worker.getWorldPosition().xy();
|
||||
|
||||
List<Job> workables = availableJobs
|
||||
.stream()
|
||||
.filter(job -> job.isValid())
|
||||
.map(job -> new Pair<Job, Float>(
|
||||
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<Job, Float> a, Pair<Job, Float> 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<Job> 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;
|
||||
// }
|
||||
// ================= lawl old shit
|
||||
|
||||
@Deprecated
|
||||
public String getValidJobs() {
|
||||
Map<String, Integer> jobs = new HashMap<String, Integer>();
|
||||
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<Pawn, Job> 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<Pawn, Job> allocation : allocations.entrySet()) {
|
||||
takenJobsString += " " + allocation.getKey().getName() + ": " + allocation.getValue().getJobName() + "\n";
|
||||
str += " " + allocation.getKey().getName() + ": " + allocation.getValue().getShortDescription() + "\n";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
for(Job job : availableJobs) {
|
||||
if(job.isValid()) {
|
||||
availableJobsString += " " + job.getJobName() + "\n";
|
||||
possibleJobs ++;
|
||||
} else {
|
||||
impossibleJobsString += " " + job.getJobName() + "\n";
|
||||
impossibleJobs ++;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return "Available Jobs: " + possibleJobs + "\n" + availableJobsString +
|
||||
"Taken Jobs: " + allocations.size() + "\n" + takenJobsString +
|
||||
"Impossible Jobs: " + impossibleJobs + "\n" + impossibleJobsString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<ISelectable> 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<ISelectable>());
|
||||
|
|
@ -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");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ public class DebugTab extends Tab implements IKeyboardListener {
|
|||
HadeanGame.debugView = !HadeanGame.debugView;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static List<String> logs = new LinkedList<String>();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Tile> 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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<Activity> activities = new ArrayList<Activity>();
|
||||
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<Desire> desires = new HashSet<>();
|
||||
|
||||
@Override
|
||||
protected void ready() {
|
||||
super.ready();
|
||||
activities = new ArrayList<Activity>();
|
||||
}
|
||||
|
||||
@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>("Activity", currentActivity),
|
||||
new BooleanDetail("Pathing", isPathing()),
|
||||
new ObjectDetail<Integer>("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<Item> 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<Activity> urgentActivities = activities.stream()
|
||||
.filter(a -> a.isValid() && a.isUrgent())
|
||||
.toList();
|
||||
|
||||
if(urgentActivities.size() > 0) {
|
||||
switchActivity(urgentActivities.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentActivity != null) return;
|
||||
|
||||
List<Activity> valueSortedActivities = activities.stream()
|
||||
.filter(a -> a.isValid())
|
||||
.map(a -> new Pair<Activity, Float>(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<Item> inventory = new ArrayList<Item>();
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public interface ISelectable {
|
|||
}
|
||||
}
|
||||
|
||||
public Box getWorldBox();
|
||||
public Box getSelectionWorldBox();
|
||||
public Action[] getActions();
|
||||
public void runAction(Action action);
|
||||
public Detail[] getDetails();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class Path implements Iterable<Node>, Serializable {
|
|||
}
|
||||
|
||||
public Node getDestination() {
|
||||
return nodes.firstElement();
|
||||
return dst;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,4 +39,8 @@ public class Action {
|
|||
Action[] merged = mergeActions(rest);
|
||||
return mergeActions(first, merged);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ public class Assets {
|
|||
flat = new SimpleShader("res/shaders/base.vert", "res/shaders/flat.frag");
|
||||
depth = new SimpleShader("res/shaders/base.vert", "res/shaders/depth.frag");
|
||||
|
||||
flat.enable();
|
||||
|
||||
atlas = new Texture("res/textures.png");
|
||||
|
||||
whiteBox = new Sprite(atlas, 0, 88, 8, 8);
|
||||
|
|
|
|||
Loading…
Reference in New Issue