pawns now do work at work locations

shops
Ivory 2023-07-06 02:13:43 -04:00
parent 496ecd9796
commit f2f46acbb5
29 changed files with 193 additions and 198 deletions

10
pom.xml
View File

@ -14,8 +14,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<lwjgl.version>3.3.1</lwjgl.version>
<jar.finalName>${project.name}</jar.finalName>
</properties>
@ -143,6 +143,7 @@
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<outputDirectory>output/${platform.name}/raw</outputDirectory>
<archive>
<manifest>
<addClasspath>true</addClasspath>
@ -207,7 +208,10 @@
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<version>3.10.1</version>
<configuration>
<enablePreview>true</enablePreview>
</configuration>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>

View File

@ -1,4 +1,4 @@
package xyz.valnet.hadean.designation;
package xyz.valnet.hadean.designations;
import xyz.valnet.hadean.gameobjects.worldobjects.Tree;

View File

@ -1,11 +1,11 @@
package xyz.valnet.hadean.designation;
package xyz.valnet.hadean.designations;
import java.util.List;
import xyz.valnet.engine.math.Box;
import xyz.valnet.engine.math.TileBox;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.hadean.interfaces.BuildType;
import xyz.valnet.hadean.enums.BuildType;
import xyz.valnet.hadean.interfaces.IBuildable;
import xyz.valnet.hadean.interfaces.ISelectable;

View File

@ -1,4 +1,4 @@
package xyz.valnet.hadean.designation;
package xyz.valnet.hadean.designations;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;

View File

@ -1,4 +1,4 @@
package xyz.valnet.hadean.interfaces;
package xyz.valnet.hadean.enums;
public enum BuildType {
AREA,

View File

@ -0,0 +1,13 @@
package xyz.valnet.hadean.enums;
public enum WorkType {
Construct("Construct"),
Farm("Till"),
Chop("Chop");
public final String verb;
private WorkType(String verb) {
this.verb = verb;
}
}

View File

@ -9,8 +9,8 @@ import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.engine.scenegraph.IMouseCaptureArea;
import xyz.valnet.engine.scenegraph.ITransient;
import xyz.valnet.hadean.enums.BuildType;
import xyz.valnet.hadean.gameobjects.Camera;
import xyz.valnet.hadean.interfaces.BuildType;
import xyz.valnet.hadean.interfaces.IBuildLayerListener;
import xyz.valnet.hadean.util.Assets;
import xyz.valnet.hadean.util.Layers;

View File

@ -4,13 +4,16 @@ import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.interfaces.IItemReceiver;
import xyz.valnet.hadean.interfaces.IWorkable;
public interface Job {
// 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 {
@Override
public String getShortDescription() {
return workable.getWorkType().verb + " " + workable.getName();
return workable.getWorkType().verb + " " + workable.getWorkableName();
}
}

View File

@ -9,6 +9,7 @@ import java.util.Set;
import java.util.Map.Entry;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.hadean.gameobjects.ui.tabs.DebugTab;
import xyz.valnet.hadean.gameobjects.worldobjects.agents.pawn.Pawn;
import xyz.valnet.hadean.interfaces.IWorkable;
@ -25,8 +26,7 @@ public class JobBoard extends GameObject {
public Job post(IWorkable workable) {
var job = new Job.Work(workable);
availableJobs.add(job);
return job;
return postJob(job);
}
@ -81,6 +81,21 @@ public class JobBoard extends GameObject {
}
}
public class ENoJobsAvailable extends Error {}
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))];
DebugTab.log("" + worker.getName() + " took job: " + job.getShortDescription());
allocations.put(worker, job);
availableJobs.remove(job);
return job;
}
}

View File

@ -12,6 +12,7 @@ public class SimpleWorkable implements IWorkable {
private float MAX_WORK = 0;
private IProgressUpdateCallback callback;
private IGetPositionsFunction positions;
private final WorkType workType;
@FunctionalInterface
public interface IGetPositionsFunction extends Serializable {
@ -23,11 +24,12 @@ public class SimpleWorkable implements IWorkable {
public void progress(float progress);
}
public SimpleWorkable(String name, float maxWork, IGetPositionsFunction positionsFunction, IProgressUpdateCallback callback) {
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
@ -43,8 +45,13 @@ public class SimpleWorkable implements IWorkable {
}
@Override
public final String getJobName() {
public final String getWorkableName() {
return name;
}
@Override
public WorkType getWorkType() {
return workType;
}
}

View File

@ -4,7 +4,6 @@ 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;
@ -61,7 +60,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));
@ -89,7 +88,7 @@ public class Tile extends WorldObject implements IWorkable {
}
public void pingThings() {
// TODO this is a quick lil workaround for a concurerncy issue.
// this is a quick lil workaround for a concurerncy issue.
// just clone the items
for(var thing : new HashSet<IWorldObject>(stuff)) {
if(thing instanceof IPingable) {
@ -239,4 +238,9 @@ public class Tile extends WorldObject implements IWorkable {
public WorkType getWorkType() {
return WorkType.Farm;
}
@Override
public String getWorkableName() {
return "Soil";
}
}

View File

@ -77,12 +77,13 @@ public class SelectionUI extends ImmediateUI implements ISelectionChangeListener
private void updateActions() {
actions.clear();
updateActionsFlag = false;
for(ISelectable selectable : selected)
for(ISelectable selectable : selected) {
for(Action action : selectable.getActions()) {
DebugTab.log(action.name);
actions.add(action);
}
}
}
@Override
public void selectionChanged(List<ISelectable> newSelection) {

View File

@ -11,9 +11,10 @@ import xyz.valnet.engine.graphics.Drawing;
import xyz.valnet.engine.math.TileBox;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.engine.scenegraph.GameObject;
import xyz.valnet.hadean.designation.CutTreesDesignation;
import xyz.valnet.hadean.designation.HaulItemDesignation;
import xyz.valnet.hadean.gameobjects.ui.BottomBar;
import xyz.valnet.hadean.designations.CutTreesDesignation;
import xyz.valnet.hadean.designations.HaulItemDesignation;
import xyz.valnet.hadean.enums.BuildType;
import xyz.valnet.hadean.gameobjects.Camera;
import xyz.valnet.hadean.gameobjects.inputlayer.BuildLayer;
import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer;
@ -23,7 +24,6 @@ import xyz.valnet.hadean.gameobjects.worldobjects.constructions.Bed;
import xyz.valnet.hadean.gameobjects.worldobjects.constructions.MasonWorkshop;
import xyz.valnet.hadean.gameobjects.worldobjects.constructions.Quarry;
import xyz.valnet.hadean.gameobjects.worldobjects.constructions.Wall;
import xyz.valnet.hadean.interfaces.BuildType;
import xyz.valnet.hadean.interfaces.IBuildLayerListener;
import xyz.valnet.hadean.interfaces.IBuildable;
import xyz.valnet.hadean.interfaces.ISelectable;

View File

@ -2,6 +2,7 @@ package xyz.valnet.hadean.gameobjects.worldobjects;
import xyz.valnet.engine.math.Box;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.enums.WorkType;
import xyz.valnet.hadean.gameobjects.jobs.Job;
import xyz.valnet.hadean.gameobjects.jobs.JobBoard;
import xyz.valnet.hadean.gameobjects.terrain.Tile;
@ -125,4 +126,9 @@ public class Tree extends WorldObject implements ISelectable, IWorkable {
public WorkType getWorkType() {
return WorkType.Chop;
}
@Override
public String getWorkableName() {
return "Tree";
}
}

View File

@ -67,16 +67,6 @@ public abstract class WorldObject extends GameObject implements IWorldObject {
linkedTiles.add(tile);
tile.placeThing(this);
}
// ?? this shouldnt bew pivotal to anything?
// if(linkedTiles.size() == 0 && inScene()) {
// remove(this);
// }
// if(linkedTiles.size() != 0 && !inScene()) {
// add(this);
// }
}
protected void setPosition(Vector4i vector) {

View File

@ -16,6 +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.ISelectable;
import xyz.valnet.hadean.pathfinding.AStarPathfinder;
import xyz.valnet.hadean.pathfinding.IPathfinder;
@ -58,11 +59,11 @@ public abstract class Agent extends WorldObject implements ISelectable {
// if we're STILL doing jack, but we have a destination
// try to path to that place
if(dst != null) goTo(dst);
if(dsts != null) goTo(dsts);
if(isAnimating()) return;
// if all fails, tell the agent we're idle
idle();
idle(dTime);
}
// public void goTo(int x, int y) {
@ -77,14 +78,14 @@ public abstract class Agent extends WorldObject implements ISelectable {
path.pop();
if(path.isComplete()) {
DebugTab.log("Finished Pathing");
if(path.getDestination().getPosition().equals(dst)) {
dst = null;
if(path.getDestination().getPosition().equals(dsts)) {
dsts = null;
}
path = null;
return;
}
if(!getTile(path.peek().getPosition()).isWalkable()) {
goTo(dst);
goTo(dsts);
return;
}
updatePath();
@ -99,24 +100,25 @@ public abstract class Agent extends WorldObject implements ISelectable {
}
private Path path;
private Vector2i dst;
private IDestinationProvider dsts;
public void stopPathing() {
path = null;
dst = null;
dsts = null;
}
public void goTo(Vector2i location) {
public boolean goTo(IDestinationProvider location) {
if(isAnimating()) {
stopPathing();
dst = location;
return;
dsts = location;
return true;
}
Path newPath = pathfinder.getPath(getWorldPosition().xy(), location);
Path newPath = pathfinder.getBestPath(getWorldPosition().xy(), location);
path = newPath;
if(path == null) return;
dst = location;
if(path == null) return false;
dsts = location;
updatePath();
return true;
}
public void wander() {
@ -131,7 +133,7 @@ public abstract class Agent extends WorldObject implements ISelectable {
} while (terrain.isOutOfBounds(random.x, random.y));
goTo(random);
goTo(new IDestinationProvider.SingleDestination(random));
}
@Override
@ -173,7 +175,8 @@ public abstract class Agent extends WorldObject implements ISelectable {
}
Assets.flat.swapColor(Color.yellow);
if(dst != null) {
if(dsts != null) {
for(Vector2i dst : dsts.getDestinations())
camera.draw(
Layers.GROUND_MARKERS,
Assets.selectionFrame,
@ -207,7 +210,7 @@ public abstract class Agent extends WorldObject implements ISelectable {
return new Box(getCalculatedPosition(), 1, 1);
}
protected abstract void idle();
protected abstract void idle(float dTime);
public Vector2f getCalculatedPosition() {
if(!isAnimating())

View File

@ -1,29 +0,0 @@
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();
}

View File

@ -10,6 +10,8 @@ import xyz.valnet.engine.math.Vector2f;
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;
@ -22,30 +24,66 @@ public class Pawn extends Agent {
private String name = Names.getRandomName();
private Set<Desire> desires = new HashSet<>();
private Set<Item> inventory = new HashSet<>();
private Job currentJob = null;
private void doWorkJob(Job.Work workJob, float dTime) {
if(!this.getWorldPosition().xy().isOneOf(workJob.workable().getDestinations())) {
if(goTo(workJob.workable())) {
return;
} else {
cancelJob(currentJob);
}
}
workJob.workable().doWork(dTime);
}
private void doHaulJob(Job.Haul haulJob, float dTime) {
}
private void doJob(float dTime) {
if(currentJob instanceof Job.Haul haul) {
doHaulJob(haul, dTime);
} else if (currentJob instanceof Job.Work work) {
doWorkJob(work, dTime);
}
}
@Override
protected void ready() {
super.ready();
public void idle(float dTime) {
if(currentJob != null) {
doJob(dTime);
return;
}
// TODO implement some sort of negotiating pattern between pawn and jobboard
// such that a pawn can decide if its capable of a job (imagine a pawn stuck
// in a place where it cant reach the job location. or in future, where it
// may lack a skill)
if((currentJob = get(JobBoard.class).takeJob(this)) != null) {
doJob(dTime);
return;
}
}
public void cancelJob(Job job) {
currentJob = null;
}
protected void create() {
setPosition(
Terrain.WORLD_SIZE / 2,
Terrain.WORLD_SIZE / 2
// (int) (Math.random() * Terrain.WORLD_SIZE),
// (int) (Math.random() * Terrain.WORLD_SIZE)
(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);
// }
float shake = 0.0f;
camera.draw(
@ -74,86 +112,13 @@ public class Pawn extends Agent {
return "Pawn";
}
private Set<Item> inventory = new HashSet<>();
public void cancelJob(Job job) {
}
// private float restlessness = 0;
// private final
@Override
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
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;
}
}
if(HadeanGame.debugView) {
return new Action[] {
wanderAction,
FORCE_WANDER,
CANCEL_PATH
};
}
return new Action[] {
};
return new Action[] {};
}
@Override
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();
}
}
}

View File

@ -1,14 +0,0 @@
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 {}
}

View File

@ -6,11 +6,11 @@ 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.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.BuildType;
import xyz.valnet.hadean.interfaces.IItemPredicate;
import xyz.valnet.hadean.interfaces.IItemReceiver;
import xyz.valnet.hadean.interfaces.IWorkable;

View File

@ -5,9 +5,9 @@ import java.util.EnumSet;
import xyz.valnet.engine.graphics.Sprite;
import xyz.valnet.engine.graphics.Tile16.Direction;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.enums.BuildType;
import xyz.valnet.hadean.gameobjects.terrain.Tile;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder;
import xyz.valnet.hadean.interfaces.BuildType;
import xyz.valnet.hadean.interfaces.IItemPredicate;
import xyz.valnet.hadean.interfaces.IPingable;
import xyz.valnet.hadean.util.Action;

View File

@ -82,6 +82,10 @@ public abstract class Item extends WorldObject implements ISelectable {
jobboard.postJob(haulJob);
}
public void destroy() {
remove(this);
}
public boolean matches(IItemPredicate itemPredicate) {
return itemPredicate.matches(this);
}

View File

@ -1,7 +1,7 @@
package xyz.valnet.hadean.gameobjects.worldobjects.zones;
import xyz.valnet.hadean.enums.BuildType;
import xyz.valnet.hadean.gameobjects.worldobjects.Buildable;
import xyz.valnet.hadean.interfaces.BuildType;
public abstract class Zone extends Buildable {

View File

@ -2,6 +2,7 @@ package xyz.valnet.hadean.interfaces;
import xyz.valnet.engine.math.TileBox;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.enums.BuildType;
public interface IBuildable {

View File

@ -0,0 +1,16 @@
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

@ -9,4 +9,12 @@ 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

@ -3,23 +3,17 @@ package xyz.valnet.hadean.interfaces;
import java.io.Serializable;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.enums.WorkType;
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 interface IWorkable extends Serializable, IDestinationProvider {
public WorkType getWorkType();
public boolean doWork(float dTime);
public Vector2i[] getWorkablePositions();
public String getName();
public String getWorkableName();
@Override
public default Vector2i[] getDestinations() {
return getWorkablePositions();
}
}

View File

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

View File

@ -1,6 +1,7 @@
package xyz.valnet.hadean.pathfinding;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.interfaces.IDestinationProvider;
public interface IPathfinder {
public Path getPath(int x1, int y1, int x2, int y2);
@ -13,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, Vector2i[] dsts);
public Path getBestPath(Vector2i src, IDestinationProvider dsts);
}