weighted average function
parent
c270373196
commit
37abb12e98
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"configurations": [
|
||||
|
||||
]
|
||||
}
|
||||
|
|
@ -13,4 +13,15 @@ public class Math {
|
|||
float scale = (t - minT) / (maxT - minT);
|
||||
return outMin + (scale * (outMax - outMin));
|
||||
}
|
||||
|
||||
public static class WeightedAverage {
|
||||
private float value = 0, weight = 0;
|
||||
public void add(float value, float weight) {
|
||||
this.value += value * weight;
|
||||
this.weight += weight;
|
||||
}
|
||||
public float calculate() {
|
||||
return value / weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
package xyz.valnet.hadean.gameobjects;
|
||||
|
||||
import xyz.valnet.engine.Game;
|
||||
import xyz.valnet.engine.graphics.Drawing;
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.engine.scenegraph.GameObject;
|
||||
import xyz.valnet.hadean.HadeanGame;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.util.Layers;
|
||||
import xyz.valnet.hadean.util.detail.PercentDetail;
|
||||
|
||||
public class Clock extends GameObject {
|
||||
|
||||
private float time = 7;
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dTime) {
|
||||
time += 0.0004f;
|
||||
while (time >= 24) time -= 24;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
int hour = (int) Math.floor(time);
|
||||
int minutes = (int) Math.floor((time % 1) * 60);
|
||||
String hs = hour < 10 ? " " + hour : "" + hour;
|
||||
String ms = minutes < 10 ? "0" + minutes : "" + minutes;
|
||||
return "" + hs + ":" + ms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Drawing.setLayer(Layers.GENERAL_UI);
|
||||
String str = toString() + " (Light: " + Math.round(getSunlight() * 100) + "%)";
|
||||
int left = 440;
|
||||
Assets.flat.pushColor(Vector4f.black);
|
||||
Assets.font.drawString(str, left - 1, 9);
|
||||
Assets.font.drawString(str, left, 9);
|
||||
Assets.font.drawString(str, left + 1, 9);
|
||||
Assets.font.drawString(str, left - 1, 10);
|
||||
Assets.font.drawString(str, left + 1, 10);
|
||||
Assets.font.drawString(str, left - 1, 11);
|
||||
Assets.font.drawString(str, left, 11);
|
||||
Assets.font.drawString(str, left + 1, 11);
|
||||
Assets.flat.swapColor(Vector4f.one);
|
||||
Assets.font.drawString(str, left, 10);
|
||||
Assets.flat.popColor();
|
||||
}
|
||||
|
||||
public float getSunlight() {
|
||||
float k = 2;
|
||||
float w = 1;
|
||||
float u = (k * (float) Math.sin((Math.PI*(time - 7))/(12))) + w;
|
||||
double kp = Math.atan(k);
|
||||
float m0 = (float)((Math.atan(k + w)) / kp);
|
||||
float m1 = (float)((Math.atan(k - w)) / kp);
|
||||
float t = (float)((Math.atan(u)) / kp);
|
||||
return (m1 + t) / (m1 + m0);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ public class Job extends GameObject {
|
|||
public void next() {
|
||||
that.nextStep();
|
||||
}
|
||||
public abstract boolean isValid();
|
||||
}
|
||||
|
||||
public class PickupItem extends JobStep {
|
||||
|
|
@ -37,6 +38,11 @@ public class Job extends GameObject {
|
|||
public Vector2i[] getLocations() {
|
||||
return new Vector2i[] { item.getWorldPosition().asInt() };
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class DropoffAtStockpile extends JobStep {
|
||||
|
|
@ -52,6 +58,11 @@ public class Job extends GameObject {
|
|||
pile.getFreeTile()
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return that.get(Stockpile.class) != null;
|
||||
}
|
||||
}
|
||||
|
||||
public class Work extends JobStep {
|
||||
|
|
@ -66,6 +77,11 @@ public class Job extends GameObject {
|
|||
public boolean doWork() {
|
||||
return subject.doWork();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private List<JobStep> steps;
|
||||
|
|
@ -120,7 +136,24 @@ public class Job extends GameObject {
|
|||
public void apply();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for(Callback callback : closedListeners) {
|
||||
callback.apply();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerClosedListener(Callback callback) {
|
||||
closedListeners.add(callback);
|
||||
}
|
||||
|
||||
public void unregisterClosedListener(Callback callback) {
|
||||
closedListeners.remove(callback);
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
for(JobStep step : steps) {
|
||||
if(!step.isValid()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
package xyz.valnet.hadean.gameobjects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2f;
|
||||
import xyz.valnet.engine.scenegraph.GameObject;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.pawn.Pawn;
|
||||
import xyz.valnet.hadean.interfaces.IWorkable;
|
||||
import xyz.valnet.hadean.interfaces.IWorker;
|
||||
import xyz.valnet.hadean.util.Pair;
|
||||
|
||||
public class JobBoard extends GameObject {
|
||||
|
||||
private Set<Job> availableJobs = new HashSet<Job>();
|
||||
private List<Job> toRemove = new ArrayList<Job>();
|
||||
private Map<IWorker, Job> allocations = new HashMap<IWorker, Job>();
|
||||
private Map<Pawn, Job> allocations = new HashMap<Pawn, Job>();
|
||||
|
||||
public Job postSimpleWorkJob(String name, IWorkable subject) {
|
||||
Job job = add(new Job(name));
|
||||
|
|
@ -37,15 +35,15 @@ public class JobBoard extends GameObject {
|
|||
|
||||
public void rescindJob(Job job) {
|
||||
if(allocations.values().contains(job)) {
|
||||
List<IWorker> toFire = new ArrayList<IWorker>();
|
||||
List<Pawn> toFire = new ArrayList<Pawn>();
|
||||
|
||||
for(IWorker worker : allocations.keySet()) {
|
||||
for(Pawn worker : allocations.keySet()) {
|
||||
if(allocations.get(worker) == job) {
|
||||
toFire.add(worker);
|
||||
}
|
||||
}
|
||||
|
||||
for(IWorker worker : toFire) {
|
||||
for(Pawn worker : toFire) {
|
||||
allocations.remove(worker);
|
||||
}
|
||||
}
|
||||
|
|
@ -53,14 +51,20 @@ public class JobBoard extends GameObject {
|
|||
if(availableJobs.contains(job)) {
|
||||
availableJobs.remove(job);
|
||||
}
|
||||
|
||||
job.close();
|
||||
}
|
||||
|
||||
public void requestJob(IWorker worker) {
|
||||
// TODO worker has capabilities?
|
||||
public boolean jobsAvailableForWorker(Pawn worker) {
|
||||
return availableJobs.size() != 0;
|
||||
}
|
||||
|
||||
public Job requestJob(Pawn worker) {
|
||||
Vector2f workerLocation = worker.getWorldPosition();
|
||||
|
||||
List<Job> workables = availableJobs
|
||||
.stream()
|
||||
.filter(job -> job.isValid())
|
||||
.map(job -> new Pair<Job, Float>(
|
||||
job,
|
||||
Stream.of(job.getLocations())
|
||||
|
|
@ -85,17 +89,21 @@ public class JobBoard extends GameObject {
|
|||
Job firstJob = workables.get(0);
|
||||
availableJobs.remove(firstJob);
|
||||
allocations.put(worker, firstJob);
|
||||
return;
|
||||
return firstJob;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void completeJob(Job job) {
|
||||
this.rescindJob(job);
|
||||
}
|
||||
|
||||
public void completeJob(IWorker worker) {
|
||||
if(!workerHasJob(worker)) return;
|
||||
rescindJob(getJob(worker));
|
||||
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
|
||||
|
|
@ -112,22 +120,22 @@ public class JobBoard extends GameObject {
|
|||
toRemove.clear();
|
||||
}
|
||||
|
||||
public boolean workerHasJob(IWorker worker) {
|
||||
public boolean workerHasJob(Pawn worker) {
|
||||
return allocations.containsKey(worker);
|
||||
}
|
||||
|
||||
public Job getJob(IWorker worker) {
|
||||
if(allocations.containsKey(worker)) {
|
||||
return allocations.get(worker);
|
||||
} else return null;
|
||||
}
|
||||
// public Job getJob(Pawn worker) {
|
||||
// if(allocations.containsKey(worker)) {
|
||||
// return allocations.get(worker);
|
||||
// } else return null;
|
||||
// }
|
||||
|
||||
public String details() {
|
||||
|
||||
String takenJobsString = "";
|
||||
String availableJobsString = "";
|
||||
|
||||
for(Entry<IWorker, Job> allocation : allocations.entrySet()) {
|
||||
for(Entry<Pawn, Job> allocation : allocations.entrySet()) {
|
||||
takenJobsString += " " + allocation.getKey().getName() + ": " + allocation.getValue().getJobName() + "\n";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import xyz.valnet.hadean.interfaces.ISelectionChangeListener;
|
|||
import xyz.valnet.hadean.util.Action;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.util.Layers;
|
||||
import xyz.valnet.hadean.util.detail.Detail;
|
||||
|
||||
public class SelectionUI extends GameObject implements ISelectionChangeListener, IButtonListener, IMouseCaptureArea {
|
||||
|
||||
|
|
@ -53,19 +54,12 @@ public class SelectionUI extends GameObject implements ISelectionChangeListener,
|
|||
|
||||
Assets.uiFrame.draw(10, 576 - BottomBar.bottomBarHeight - height - padding, width, height);
|
||||
|
||||
// int i = 0;
|
||||
// for(String name : selectedTypes.keySet()) {
|
||||
// int n = selectedTypes.get(name);
|
||||
// Assets.font.drawString("" + n + "x " + name, 26, 376 + 16 * i);
|
||||
// i ++;
|
||||
// }
|
||||
|
||||
|
||||
if(selectedTypes.size() == 1) {
|
||||
Assets.font.drawString("" + count + "x " + name, 26, 576 - BottomBar.bottomBarHeight - height);
|
||||
|
||||
if(count == 1) {
|
||||
String details = selected.get(0).details();
|
||||
|
||||
String details = Detail.renderDetails(selected.get(0).getDetails());
|
||||
Assets.font.drawString(details, 26, 576 - BottomBar.bottomBarHeight - height + 32);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import xyz.valnet.hadean.interfaces.ITileThing;
|
|||
import xyz.valnet.hadean.util.Action;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.util.Layers;
|
||||
import xyz.valnet.hadean.util.detail.Detail;
|
||||
|
||||
@BuildableMetadata(category = "Zones", name = "Farm Plot")
|
||||
public class FarmPlot extends WorldObject implements ISelectable, ITileThing, IBuildable {
|
||||
|
|
@ -45,8 +46,8 @@ public class FarmPlot extends WorldObject implements ISelectable, ITileThing, IB
|
|||
}
|
||||
|
||||
@Override
|
||||
public String details() {
|
||||
return "";
|
||||
public Detail[] getDetails() {
|
||||
return new Detail[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package xyz.valnet.hadean.gameobjects.worldobjects;
|
|||
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.util.Layers;
|
||||
import xyz.valnet.hadean.util.detail.Detail;
|
||||
|
||||
public class Log extends Item {
|
||||
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
|
@ -36,8 +36,8 @@ public class Log extends Item {
|
|||
public void onRemove() {}
|
||||
|
||||
@Override
|
||||
public String details() {
|
||||
return "A fat log";
|
||||
public Detail[] getDetails() {
|
||||
return new Detail[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,149 +0,0 @@
|
|||
package xyz.valnet.hadean.gameobjects.worldobjects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2f;
|
||||
import xyz.valnet.engine.math.Vector2i;
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.hadean.gameobjects.Job;
|
||||
import xyz.valnet.hadean.gameobjects.JobBoard;
|
||||
import xyz.valnet.hadean.gameobjects.Terrain;
|
||||
import xyz.valnet.hadean.gameobjects.Job.JobStep;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.agents.Agent;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
|
||||
import xyz.valnet.hadean.interfaces.ITileThing;
|
||||
import xyz.valnet.hadean.interfaces.IWorker;
|
||||
import xyz.valnet.hadean.util.Action;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.util.Layers;
|
||||
|
||||
public class Pawn extends Agent implements IWorker {
|
||||
|
||||
private static int pawnCount = 0;
|
||||
private String name = "Pawn " + (++ pawnCount);
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
jobboard = get(JobBoard.class);
|
||||
x = (int) (Math.random() * Terrain.WORLD_SIZE);
|
||||
y = (int) (Math.random() * Terrain.WORLD_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
super.render();
|
||||
camera.draw(Layers.PAWNS, Assets.pawn, getCalculatedPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runAction(Action action) {}
|
||||
|
||||
@Override
|
||||
public String details() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f getWorldPosition() {
|
||||
return new Vector2f(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4f getWorldBox() {
|
||||
Vector2f pos = getCalculatedPosition();
|
||||
return new Vector4f(pos.x, pos.y, pos.x+1, pos.y+1);
|
||||
}
|
||||
|
||||
private JobBoard jobboard;
|
||||
|
||||
@Override
|
||||
protected void think() {
|
||||
super.think();
|
||||
|
||||
// if we dont have a job
|
||||
if(!jobboard.workerHasJob(this)) {
|
||||
jobboard.requestJob(this); // try to get one
|
||||
}
|
||||
|
||||
// if we have a job and need to go to it and we're not pathing to it
|
||||
if(jobboard.workerHasJob(this) && !isAtJobStepLocation() && !isPathingToJobLocation()) {
|
||||
goToJobStepLocation(); // start pathing there.
|
||||
return; // and dont think about anything else.
|
||||
}
|
||||
|
||||
// if we still dont have a job and we're not moving around
|
||||
if(!jobboard.workerHasJob(this) && !isPathing()) {
|
||||
if(Math.random() < 0.001f) wander(); // have a chance of wandering!
|
||||
return; // and dont think about anything else.
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPathingToJobLocation() {
|
||||
if(!isPathing()) return false;
|
||||
return getDestination().isOneOf(jobboard.getJob(this).getCurrentStep().getLocations());
|
||||
}
|
||||
|
||||
private boolean isAtJobStepLocation() {
|
||||
return this.getWorldPosition().asInt().isOneOf(jobboard.getJob(this).getCurrentStep().getLocations());
|
||||
}
|
||||
|
||||
private void goToJobStepLocation() {
|
||||
goToClosest(jobboard
|
||||
.getJob(this)
|
||||
.getCurrentStep()
|
||||
.getLocations()
|
||||
);
|
||||
}
|
||||
|
||||
// TODO at some point rewrite this to use an actor component array
|
||||
// where we loop through until something _does_ sometihng.
|
||||
@Override
|
||||
protected boolean act() {
|
||||
if(super.act()) return true;
|
||||
if(doJob()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Item> inventory = new ArrayList<Item>();
|
||||
|
||||
private boolean doJob() {
|
||||
if(!jobboard.workerHasJob(this)) return false;
|
||||
JobStep step = jobboard.getJob(this).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()) step.next();
|
||||
return true;
|
||||
} else if(step instanceof Job.PickupItem) {
|
||||
Job.PickupItem pickupStep = (Job.PickupItem) step;
|
||||
Item item = getTile().removeThing(pickupStep.item);
|
||||
remove(item);
|
||||
inventory.add(item);
|
||||
step.next();
|
||||
return true;
|
||||
} else if(step instanceof Job.DropoffAtStockpile) {
|
||||
if(!getTile().isTileFree()) return false;
|
||||
Job.DropoffAtStockpile dropoffStep = (Job.DropoffAtStockpile) step;
|
||||
Item item = dropoffStep.item;
|
||||
if(!inventory.contains(item)) {
|
||||
return false;
|
||||
}
|
||||
inventory.remove(item);
|
||||
add(item);
|
||||
getTile().placeThing(item);
|
||||
step.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,13 @@ package xyz.valnet.hadean.gameobjects.worldobjects;
|
|||
import xyz.valnet.engine.math.Vector2f;
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.hadean.gameobjects.JobBoard;
|
||||
import xyz.valnet.hadean.gameobjects.Tile;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
|
||||
import xyz.valnet.hadean.interfaces.ISelectable;
|
||||
import xyz.valnet.hadean.interfaces.ITileThing;
|
||||
import xyz.valnet.hadean.util.Action;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.util.Layers;
|
||||
import xyz.valnet.hadean.util.detail.Detail;
|
||||
|
||||
public class Rice extends Item implements ITileThing, ISelectable {
|
||||
|
||||
|
|
@ -79,8 +79,8 @@ public class Rice extends Item implements ITileThing, ISelectable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String details() {
|
||||
return "Bag of Rice";
|
||||
public Detail[] getDetails() {
|
||||
return new Detail[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import xyz.valnet.hadean.interfaces.ITileThing;
|
|||
import xyz.valnet.hadean.util.Action;
|
||||
import xyz.valnet.hadean.util.Assets;
|
||||
import xyz.valnet.hadean.util.Layers;
|
||||
import xyz.valnet.hadean.util.detail.Detail;
|
||||
|
||||
@BuildableMetadata(category = "Zones", name = "Stockpile")
|
||||
public class Stockpile extends WorldObject implements ISelectable, ITileThing, IBuildable {
|
||||
|
|
@ -82,9 +83,8 @@ public class Stockpile extends WorldObject implements ISelectable, ITileThing, I
|
|||
}
|
||||
|
||||
@Override
|
||||
public String details() {
|
||||
|
||||
return "";
|
||||
public Detail[] getDetails() {
|
||||
return new Detail[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ import xyz.valnet.hadean.interfaces.IWorkable;
|
|||
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.PercentDetail;
|
||||
|
||||
public class Tree extends WorldObject implements ITileThing, ISelectable, IWorkable {
|
||||
|
||||
|
|
@ -89,10 +92,11 @@ public class Tree extends WorldObject implements ITileThing, ISelectable, IWorka
|
|||
}
|
||||
|
||||
@Override
|
||||
public String details() {
|
||||
return "" + name + "\n" +
|
||||
"Chop Flag | " + (chopJob != null) + "\n" +
|
||||
"Progress | " + (String.format("%.2f", getProgress() * 100)) + "%";
|
||||
public Detail[] getDetails() {
|
||||
return new Detail[] {
|
||||
new BooleanDetail("Chop Flag", chopJob != null),
|
||||
new PercentDetail("Progress", getProgress())
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -33,11 +33,13 @@ public abstract class Agent extends WorldObject implements ISelectable {
|
|||
private IPathfinder pathfinder;
|
||||
private Path path = null;
|
||||
|
||||
protected boolean isPathing() {
|
||||
private boolean stopPathingFlag = false;
|
||||
|
||||
public boolean isPathing() {
|
||||
return path != null && !path.isComplete();
|
||||
}
|
||||
|
||||
protected Vector2f getCalculatedPosition() {
|
||||
public Vector2f getCalculatedPosition() {
|
||||
if(path == null || path.isComplete()) return getWorldPosition();
|
||||
Vector2f nextPos = path.peek().getPosition().asFloat();
|
||||
return new Vector2f(
|
||||
|
|
@ -57,8 +59,11 @@ public abstract class Agent extends WorldObject implements ISelectable {
|
|||
public void update(float dTime) {
|
||||
think();
|
||||
act();
|
||||
postAct();
|
||||
}
|
||||
|
||||
protected abstract void postAct();
|
||||
|
||||
private void move() {
|
||||
frameCounter++;
|
||||
if(frameCounter >= speed) {
|
||||
|
|
@ -71,9 +76,19 @@ public abstract class Agent extends WorldObject implements ISelectable {
|
|||
}
|
||||
if(path.isComplete()) path = null;
|
||||
frameCounter = 0;
|
||||
if(stopPathingFlag) {
|
||||
path = null;
|
||||
nextPath = null;
|
||||
stopPathingFlag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void stopPathing() {
|
||||
stopPathingFlag = true;
|
||||
nextPath = null;
|
||||
}
|
||||
|
||||
private void correctPath() {
|
||||
if(path != null && path.isComplete()) path = null;
|
||||
if(path == null) return;
|
||||
|
|
@ -108,7 +123,7 @@ public abstract class Agent extends WorldObject implements ISelectable {
|
|||
|
||||
private Path nextPath = null;
|
||||
|
||||
protected void goTo(int x, int y) {
|
||||
public void goTo(int x, int y) {
|
||||
Path newPath = pathfinder.getPath((int)this.x, (int)this.y, x, y);
|
||||
if(path == null) {
|
||||
path = newPath;
|
||||
|
|
@ -118,7 +133,7 @@ public abstract class Agent extends WorldObject implements ISelectable {
|
|||
}
|
||||
}
|
||||
|
||||
protected void goToClosest(Vector2i[] destinations) {
|
||||
public void goToClosest(Vector2i[] destinations) {
|
||||
Path newPath = pathfinder.getBestPath(this.getWorldPosition().asInt(), destinations);
|
||||
if(path == null) {
|
||||
path = newPath;
|
||||
|
|
@ -128,11 +143,11 @@ public abstract class Agent extends WorldObject implements ISelectable {
|
|||
}
|
||||
}
|
||||
|
||||
protected void goTo(Vector2i location) {
|
||||
public void goTo(Vector2i location) {
|
||||
goTo(location.x, location.y);
|
||||
}
|
||||
|
||||
protected void wander() {
|
||||
public void wander() {
|
||||
int randomX = (int)Math.floor(Math.random() * Terrain.WORLD_SIZE);
|
||||
int randomY = (int)Math.floor(Math.random() * Terrain.WORLD_SIZE);
|
||||
path = pathfinder.getPath((int)x, (int)y, randomX, randomY);
|
||||
|
|
@ -184,7 +199,8 @@ public abstract class Agent extends WorldObject implements ISelectable {
|
|||
return new Action[0];
|
||||
}
|
||||
|
||||
protected Vector2i getDestination() {
|
||||
public Vector2i getDestination() {
|
||||
if(nextPath != null) return nextPath.getDestination().getPosition();
|
||||
if(path == null) return null;
|
||||
return path.getDestination().getPosition();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package xyz.valnet.hadean.gameobjects.worldobjects.pawn;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2i;
|
||||
|
||||
public abstract class Activity {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ActivityCancellationCallback {
|
||||
public void apply(Activity activity);
|
||||
}
|
||||
|
||||
public abstract boolean isUrgent();
|
||||
public abstract float getBenefit();
|
||||
public abstract boolean isValid();
|
||||
public abstract void act();
|
||||
public abstract void begin(ActivityCancellationCallback callback);
|
||||
public abstract void end();
|
||||
public abstract String toString();
|
||||
public abstract Vector2i[] getTargetLocations();
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
package xyz.valnet.hadean.gameobjects.worldobjects.pawn;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2i;
|
||||
import xyz.valnet.hadean.gameobjects.Job;
|
||||
import xyz.valnet.hadean.gameobjects.Job.JobStep;
|
||||
import xyz.valnet.hadean.gameobjects.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() {
|
||||
if (doJob()) return;
|
||||
}
|
||||
|
||||
|
||||
private boolean isPathingToJobLocation() {
|
||||
if(!worker.isPathing()) return false;
|
||||
return worker.getDestination().isOneOf(job.getCurrentStep().getLocations());
|
||||
}
|
||||
|
||||
private boolean isAtJobStepLocation() {
|
||||
return worker.getWorldPosition().asInt().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(() -> {
|
||||
System.out.println("job cancelled");
|
||||
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;
|
||||
}
|
||||
|
||||
private boolean doJob() {
|
||||
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()) 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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if(job == null) return "Activity (Null Job)";
|
||||
return job.getJobName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2i[] getTargetLocations() {
|
||||
return job.getCurrentStep().getLocations();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package xyz.valnet.hadean.gameobjects.worldobjects.pawn;
|
||||
|
||||
import xyz.valnet.hadean.util.detail.Detail;
|
||||
import xyz.valnet.hadean.util.detail.PercentDetail;
|
||||
|
||||
public class Needs {
|
||||
|
||||
private float energy = 0.5f + (float)Math.random() * 0.5f;
|
||||
private float recreation = 0.5f + (float)Math.random() * 0.5f;
|
||||
|
||||
private float restRatio = 6;
|
||||
private float decay = 0.00001f;
|
||||
|
||||
public void update(float dTime) {
|
||||
energy = Math.max(energy - decay, 0);
|
||||
recreation = Math.max(recreation - decay, 0);
|
||||
}
|
||||
|
||||
public void sleep() {
|
||||
energy = Math.min(energy + decay * restRatio, 1);
|
||||
}
|
||||
|
||||
public float getSleepNeed() {
|
||||
return 1 - energy;
|
||||
}
|
||||
|
||||
// public
|
||||
|
||||
public Detail[] getDetails() {
|
||||
return new Detail[] {
|
||||
new PercentDetail("Energy", energy, 1),
|
||||
new PercentDetail("Fun", recreation, 1)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
package xyz.valnet.hadean.gameobjects.worldobjects.pawn;
|
||||
|
||||
import static xyz.valnet.hadean.util.detail.Detail.mergeDetails;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2f;
|
||||
import xyz.valnet.engine.math.Vector2i;
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.hadean.gameobjects.Clock;
|
||||
import xyz.valnet.hadean.gameobjects.JobBoard;
|
||||
import xyz.valnet.hadean.gameobjects.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.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 static int pawnCount = 0;
|
||||
private String name = "Pawn " + (++ pawnCount);
|
||||
private Needs needs = new Needs();
|
||||
|
||||
// private float workEthic = (float) Math.random();
|
||||
// private float selfWorth = (float) Math.random();
|
||||
|
||||
private List<Activity> activities = new ArrayList<Activity>();
|
||||
private Activity currentActivity = null;
|
||||
|
||||
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(item);
|
||||
add(item);
|
||||
getTile().placeThing(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
x = (int) (Math.random() * Terrain.WORLD_SIZE);
|
||||
y = (int) (Math.random() * Terrain.WORLD_SIZE);
|
||||
|
||||
activities.add(new JobActivity(this, get(JobBoard.class)));
|
||||
activities.add(new SleepActivity(this, needs, get(Clock.class)));
|
||||
// activities.add(new WanderActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
super.render();
|
||||
if(currentActivity instanceof SleepActivity) {
|
||||
Assets.flat.pushColor(new Vector4f(0.5f, 0.5f, 0.5f, 1.0f));
|
||||
} else {
|
||||
Assets.flat.pushColor(Vector4f.one);
|
||||
}
|
||||
camera.draw(Layers.PAWNS, Assets.pawn, getCalculatedPosition());
|
||||
Assets.flat.popColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runAction(Action action) {}
|
||||
|
||||
@Override
|
||||
public Detail[] getDetails() {
|
||||
// return needs.getDetails();
|
||||
return mergeDetails(needs.getDetails(), new Detail[] {
|
||||
new ObjectDetail<Activity>("Activity", currentActivity),
|
||||
new PercentDetail("Sleep Value", activities.get(1).getBenefit(), 2),
|
||||
new BooleanDetail("Pathing", isPathing())
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f getWorldPosition() {
|
||||
return new Vector2f(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4f getWorldBox() {
|
||||
Vector2f pos = getCalculatedPosition();
|
||||
return new Vector4f(pos.x, pos.y, pos.x+1, pos.y+1);
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
currentActivity = null;
|
||||
|
||||
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(currentActivity != null) currentActivity.end();
|
||||
currentActivity = activity;
|
||||
stopPathing();
|
||||
currentActivity.begin(a -> endActivity(a));
|
||||
}
|
||||
|
||||
private void endActivity() {
|
||||
endActivity(currentActivity);
|
||||
}
|
||||
|
||||
private void endActivity(Activity activity) {
|
||||
activity.end();
|
||||
stopPathing();
|
||||
if(currentActivity == activity) {
|
||||
currentActivity = null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO at some point rewrite this to use an actor component array
|
||||
// where we loop through until something _does_ sometihng.
|
||||
@Override
|
||||
protected boolean act() {
|
||||
if(super.act()) return true;
|
||||
// if(doJob()) return true;
|
||||
if(currentActivity != null) {
|
||||
currentActivity.act();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void postAct() {
|
||||
needs.update(1);
|
||||
}
|
||||
|
||||
private List<Item> inventory = new ArrayList<Item>();
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package xyz.valnet.hadean.gameobjects.worldobjects.pawn;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2i;
|
||||
import xyz.valnet.engine.util.Math.WeightedAverage;
|
||||
import xyz.valnet.hadean.gameobjects.Clock;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.agents.Agent;
|
||||
|
||||
import static xyz.valnet.engine.util.Math.lerp;
|
||||
|
||||
public class SleepActivity extends Activity {
|
||||
|
||||
private Agent agent;
|
||||
private Needs needs;
|
||||
private Clock clock;
|
||||
|
||||
private float circadianStrength = (float)Math.random() * 5f;
|
||||
|
||||
private int stage;
|
||||
|
||||
public SleepActivity(Agent agent, Needs needs, Clock clock) {
|
||||
this.needs = needs;
|
||||
this.agent = agent;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUrgent() {
|
||||
return needs.getSleepNeed() >= 0.97f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBenefit() {
|
||||
float minSleepRechargePerCycle = 0.2f;
|
||||
// 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() - minSleepRechargePerCycle, 1);
|
||||
// System.out.println(1 - 2 * clock.getSunlight());
|
||||
average.add(1 - 2 * clock.getSunlight(), circadianStrength);
|
||||
return average.calculate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act() {
|
||||
needs.sleep();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package xyz.valnet.hadean.gameobjects.worldobjects.pawn;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2i;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.agents.Agent;
|
||||
|
||||
public class WanderActivity extends Activity {
|
||||
|
||||
private Agent agent;
|
||||
private Needs needs;
|
||||
|
||||
public WanderActivity(Agent agent, Needs needs) {
|
||||
this.needs = needs;
|
||||
this.agent = agent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUrgent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getBenefit() {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act() {
|
||||
// since wandering is literally just pathing.
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,11 +2,12 @@ package xyz.valnet.hadean.interfaces;
|
|||
|
||||
import xyz.valnet.engine.math.Vector4f;
|
||||
import xyz.valnet.hadean.util.Action;
|
||||
import xyz.valnet.hadean.util.detail.Detail;
|
||||
|
||||
public interface ISelectable {
|
||||
public Vector4f getWorldBox();
|
||||
public Action[] getActions();
|
||||
public void runAction(Action action);
|
||||
public String details();
|
||||
public Detail[] getDetails();
|
||||
public default void selectedRender() {}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package xyz.valnet.hadean.interfaces;
|
||||
|
||||
import xyz.valnet.engine.math.Vector2f;
|
||||
|
||||
public interface IWorker {
|
||||
public Vector2f getWorldPosition();
|
||||
public String getName();
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package xyz.valnet.hadean.scenes;
|
|||
import xyz.valnet.engine.scenegraph.SceneGraph;
|
||||
import xyz.valnet.hadean.gameobjects.BottomBar;
|
||||
import xyz.valnet.hadean.gameobjects.Camera;
|
||||
import xyz.valnet.hadean.gameobjects.Clock;
|
||||
import xyz.valnet.hadean.gameobjects.JobBoard;
|
||||
import xyz.valnet.hadean.gameobjects.SelectionUI;
|
||||
import xyz.valnet.hadean.gameobjects.Terrain;
|
||||
|
|
@ -12,7 +13,7 @@ import xyz.valnet.hadean.gameobjects.ui.HoverQuery;
|
|||
import xyz.valnet.hadean.gameobjects.ui.tabs.BuildTab;
|
||||
import xyz.valnet.hadean.gameobjects.ui.tabs.JobBoardTab;
|
||||
import xyz.valnet.hadean.gameobjects.ui.tabs.MenuTab;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.Pawn;
|
||||
import xyz.valnet.hadean.gameobjects.worldobjects.pawn.Pawn;
|
||||
|
||||
// TODO BIG IDEAS
|
||||
// have caches of types that ill need (Like IMouseListener)
|
||||
|
|
@ -31,8 +32,9 @@ public class GameScene extends SceneGraph {
|
|||
objects.add(new Terrain());
|
||||
objects.add(new Camera());
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public class Assets {
|
|||
bed = new Sprite(atlas, 0, 120, 8, 16);
|
||||
egg = new Sprite(atlas, 8, 104, 8, 8);
|
||||
bigRock = new Sprite(atlas, 16, 104, 8, 8);
|
||||
lilPickaxe = new Sprite(atlas, 8, 120, 16, 16)
|
||||
lilPickaxe = new Sprite(atlas, 8, 120, 16, 16);
|
||||
|
||||
Map<Character, Sprite> charset = new HashMap<Character, Sprite>();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package xyz.valnet.hadean.util.detail;
|
||||
|
||||
public class BooleanDetail extends Detail {
|
||||
private boolean value;
|
||||
|
||||
public BooleanDetail(String key, boolean value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString(int keyWidth) {
|
||||
return key + " ".repeat(keyWidth - getKeyWidth()) + " | " + java.lang.Boolean.toString(value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package xyz.valnet.hadean.util.detail;
|
||||
|
||||
public abstract class Detail {
|
||||
|
||||
public abstract String toString(int keyWidth);
|
||||
|
||||
protected String key;
|
||||
|
||||
public int getKeyWidth() {
|
||||
return key.length();
|
||||
}
|
||||
|
||||
public static String renderDetails(Detail[] details) {
|
||||
if(details.length == 0) return "";
|
||||
int keyWidth = 0;
|
||||
for(Detail d : details) {
|
||||
keyWidth = Math.max(d.getKeyWidth(), keyWidth);
|
||||
}
|
||||
|
||||
String str = "";
|
||||
for(Detail d : details) {
|
||||
str += d.toString(keyWidth) + "\n";
|
||||
}
|
||||
return str.stripTrailing();
|
||||
}
|
||||
|
||||
public static Detail[] mergeDetails(Detail[] a, Detail[] b) {
|
||||
Detail[] c = new Detail[a.length + b.length];
|
||||
System.arraycopy(a, 0, c, 0, a.length);
|
||||
System.arraycopy(b, 0, c, a.length, b.length);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package xyz.valnet.hadean.util.detail;
|
||||
|
||||
public class ObjectDetail<T> extends Detail {
|
||||
private T value;
|
||||
|
||||
public ObjectDetail(String key, T value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString(int keyWidth) {
|
||||
String prefix = key + " ".repeat(keyWidth - getKeyWidth()) + " | ";
|
||||
if(value == null)
|
||||
return prefix + "null";
|
||||
return prefix + value.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package xyz.valnet.hadean.util.detail;
|
||||
|
||||
public class PercentDetail extends Detail {
|
||||
private float value;
|
||||
private int sigFigs;
|
||||
|
||||
public PercentDetail(String key, float value) {
|
||||
this(key, value, 0);
|
||||
}
|
||||
|
||||
public PercentDetail(String key, float value, int sigFigs) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.sigFigs = sigFigs;
|
||||
}
|
||||
|
||||
public String toString(int keyWidth) {
|
||||
double sigFigMul = Math.pow(10, sigFigs);
|
||||
return key + " ".repeat(keyWidth - getKeyWidth()) + " | " + ((Math.round(value * (sigFigMul * 100))) / sigFigMul) + "%";
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue