context aware walls

bottom-bar
Ivory 2023-01-10 21:51:11 -05:00
parent 18f4a33038
commit 3d0754f060
13 changed files with 353 additions and 5 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,104 @@
package xyz.valnet.engine.graphics;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import xyz.valnet.engine.math.Vector4i;
public class Tile16 {
public enum Direction {
NORTH,
EAST,
SOUTH,
WEST
}
private Sprite solo;
private Sprite n; // north
private Sprite e; // east
private Sprite s; // south
private Sprite w; // west
private Sprite h; // horizontal
private Sprite v; // vertical
private Sprite cne; // corner north east
private Sprite cnw; // corner north west
private Sprite cse; // corner south east
private Sprite csw; // corner south west
private Sprite tn; // T north east west
private Sprite te; // T north south east
private Sprite ts; // T south east west
private Sprite tw; // T north south west
private Sprite all; // all sides
private Vector4i area; // the original area in the texture
private Map<EnumSet<Direction>, Sprite> cache = new HashMap<EnumSet<Direction>, Sprite>();
public Tile16(Texture base, Vector4i area) {
this.area = area;
solo = new Sprite(base, getPixelCoords(0, 0));
n = new Sprite(base, getPixelCoords(0, 3));
e = new Sprite(base, getPixelCoords(3, 0));
s = new Sprite(base, getPixelCoords(0, 1));
w = new Sprite(base, getPixelCoords(1, 0));
h = new Sprite(base, getPixelCoords(0, 2));
v = new Sprite(base, getPixelCoords(2, 0));
cne = new Sprite(base, getPixelCoords(3, 3));
cnw = new Sprite(base, getPixelCoords(1, 3));
cse = new Sprite(base, getPixelCoords(3, 1));
csw = new Sprite(base, getPixelCoords(1, 1));
tn = new Sprite(base, getPixelCoords(2, 3));
te = new Sprite(base, getPixelCoords(3, 2));
ts = new Sprite(base, getPixelCoords(2, 1));
tw = new Sprite(base, getPixelCoords(1, 2));
all = new Sprite(base, getPixelCoords(2, 2));
cache.put(EnumSet.noneOf(Direction.class), solo);
cache.put(EnumSet.of(Direction.NORTH), n);
cache.put(EnumSet.of(Direction.EAST), e);
cache.put(EnumSet.of(Direction.SOUTH), s);
cache.put(EnumSet.of(Direction.WEST), w);
cache.put(EnumSet.of(Direction.NORTH, Direction.SOUTH), h);
cache.put(EnumSet.of(Direction.EAST, Direction.WEST), v);
cache.put(EnumSet.of(Direction.NORTH, Direction.EAST), cne);
cache.put(EnumSet.of(Direction.NORTH, Direction.WEST), cnw);
cache.put(EnumSet.of(Direction.SOUTH, Direction.EAST), cse);
cache.put(EnumSet.of(Direction.SOUTH, Direction.WEST), csw);
cache.put(EnumSet.of(Direction.NORTH, Direction.EAST, Direction.WEST), tn);
cache.put(EnumSet.of(Direction.NORTH, Direction.SOUTH, Direction.EAST), te);
cache.put(EnumSet.of(Direction.SOUTH, Direction.EAST, Direction.WEST), ts);
cache.put(EnumSet.of(Direction.NORTH, Direction.SOUTH, Direction.WEST), tw);
cache.put(EnumSet.allOf(Direction.class), all);
}
private Vector4i getPixelCoords(int x, int y) {
int tileWidth = area.z / 4;
int tileHeight = area.w / 4;
return new Vector4i(
area.x + x * tileWidth,
area.y + y * tileHeight,
tileWidth,
tileHeight
);
}
public Sprite getTextureFor(EnumSet<Direction> directions) {
return cache.get(directions);
}
}

View File

@ -15,5 +15,4 @@ public class CutTreesDesignation extends Designation<Tree> {
protected void designate(Tree thing) {
thing.runAction(Tree.ACTION_CHOP);
}
}

View File

@ -45,6 +45,8 @@ public class Terrain extends GameObject implements IPathable, IWorldBoundsAdapte
}
public Tile getTile(int x, int y) {
if(x < 0 || y < 0) return null;
if(x >= WORLD_SIZE || y >= WORLD_SIZE) return null;
return tiles[x][y];
}

View File

@ -15,6 +15,7 @@ import xyz.valnet.hadean.gameobjects.worldobjects.WorldObject;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.interfaces.IItemPredicate;
import xyz.valnet.hadean.interfaces.IPingable;
import xyz.valnet.hadean.interfaces.ITileThing;
import xyz.valnet.hadean.interfaces.IWorkable;
import xyz.valnet.hadean.util.Assets;
@ -91,12 +92,36 @@ public class Tile extends WorldObject implements IWorkable {
return null;
}
public void pingThings() {
for(ITileThing thing : stuff) {
if(thing instanceof IPingable) {
((IPingable)thing).ping();
}
}
}
private void pingNeighbors() {
Vector2i pos = getCoords();
Tile north = terrain.getTile(pos.x, pos.y - 1);
Tile east = terrain.getTile(pos.x - 1, pos.y);
Tile south = terrain.getTile(pos.x, pos.y + 1);
Tile west = terrain.getTile(pos.x + 1, pos.y);
if(north != null) north.pingThings();
if(east != null) east.pingThings();
if(south != null) south.pingThings();
if(west != null) west.pingThings();
}
public void placeThing(ITileThing thing) {
stuff.add(thing);
if(thing instanceof GameObject) {
add((GameObject)thing);
}
thing.onPlaced(this);
pingThings();
pingNeighbors();
if(thing instanceof FarmPlot) {
get(JobBoard.class).postSimpleWorkJob("Till Soil", this);
}
@ -107,6 +132,10 @@ public class Tile extends WorldObject implements IWorkable {
if(toRemove.contains(thing)) return null;
toRemove.add(thing);
pingThings();
pingNeighbors();
return thing;
}
@ -197,4 +226,13 @@ public class Tile extends WorldObject implements IWorkable {
}
return str.stripTrailing();
}
public <T> boolean has(Class<T> clazz) {
for(ITileThing thing : stuff) {
if(clazz.isInstance(thing)) {
return true;
}
}
return false;
}
}

View File

@ -17,9 +17,10 @@ import xyz.valnet.hadean.gameobjects.BottomBar;
import xyz.valnet.hadean.gameobjects.Camera;
import xyz.valnet.hadean.gameobjects.inputlayer.BuildLayer;
import xyz.valnet.hadean.gameobjects.inputlayer.SelectionLayer;
import xyz.valnet.hadean.gameobjects.worldobjects.Bed;
import xyz.valnet.hadean.gameobjects.worldobjects.FarmPlot;
import xyz.valnet.hadean.gameobjects.worldobjects.Stockpile;
import xyz.valnet.hadean.gameobjects.worldobjects.constructions.Bed;
import xyz.valnet.hadean.gameobjects.worldobjects.constructions.Wall;
import xyz.valnet.hadean.input.Button;
import xyz.valnet.hadean.input.IButtonListener;
import xyz.valnet.hadean.input.SimpleButton;
@ -59,6 +60,8 @@ public class BuildTab extends Tab implements ISelectionChangeListener, IMouseCap
BuildTab.registerBuildable(CutTreesDesignation.class);
BuildTab.registerBuildable(Bed.class);
BuildTab.registerBuildable(Wall.class);
BuildTab.registerBuildable(FarmPlot.class);
BuildTab.registerBuildable(Stockpile.class);

View File

@ -43,7 +43,6 @@ public abstract class WorldObject extends GameObject {
private void updateTileLinks(Set<Tile> tiles) {
if(tiles == null || tiles.size() == 0) return;
if(!(this instanceof ITileThing)) return;
boolean inScene = inScene();
Set<Tile> removeTiles = new HashSet<Tile>();
Set<Tile> addTiles = new HashSet<Tile>();

View File

@ -1,10 +1,11 @@
package xyz.valnet.hadean.gameobjects.worldobjects;
package xyz.valnet.hadean.gameobjects.worldobjects.constructions;
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.Tile;
import xyz.valnet.hadean.gameobjects.worldobjects.Buildable;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Log;
import xyz.valnet.hadean.interfaces.BuildableMetadata;

View File

@ -0,0 +1,187 @@
package xyz.valnet.hadean.gameobjects.worldobjects.constructions;
import java.util.EnumSet;
import xyz.valnet.engine.graphics.Tile16.Direction;
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.Tile;
import xyz.valnet.hadean.gameobjects.worldobjects.Buildable;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Boulder;
import xyz.valnet.hadean.gameobjects.worldobjects.items.Item;
import xyz.valnet.hadean.interfaces.BuildableMetadata;
import xyz.valnet.hadean.interfaces.IItemReceiver;
import xyz.valnet.hadean.interfaces.IPingable;
import xyz.valnet.hadean.interfaces.ISelectable;
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.ObjectDetail;
import xyz.valnet.hadean.util.detail.PercentDetail;
@BuildableMetadata(category = "Structure", name = "Wall", type = BuildableMetadata.Type.SINGLE)
public class Wall extends Buildable implements IItemReceiver, IWorkable, ISelectable, IPingable {
private int boulders = 0;
private float work = 0;
private final float maxWork = 500;
private Job job = null;
@Override
protected void create() {
super.create();
job = add(new Job("Build Wall"));
job.addStep(job.new PickupItemByPredicate(Boulder.BOULDER_PREDICATE));
job.addStep(job.new DropoffPredicateAtItemReceiver(this, Boulder.BOULDER_PREDICATE));
job.addStep(job.new Work(this));
get(JobBoard.class).postJob(job);
}
@Override
protected void start() {
super.start();
ping();
}
@Override
public void render() {
super.render();
Vector2i pos = getWorldPosition().xy();
if(isBuilt()) {
float b = 0.7f;
Assets.flat.pushColor(new Vector4f(b, b, b, 1f));
camera.draw(Layers.GROUND, Assets.wall.getTextureFor(wallSides), pos.x, pos.y);
Assets.flat.popColor();
} else {
float p = work / maxWork;
float b = 4;
Assets.flat.pushColor(new Vector4f(b, b, b, 0.5f));
camera.draw(Layers.GROUND, Assets.wall.getTextureFor(wallSides), pos.x, pos.y);
Assets.flat.popColor();
if(boulders > 0) {
camera.drawProgressBar(p, getWorldBox());
}
}
}
@Override
public String getName() {
return "Wall";
}
@Override
public boolean receive(Item item) {
if(item == null) return false;
if(!item.matches(Boulder.BOULDER_PREDICATE)) return false;
remove(item);
boulders ++;
return true;
}
private boolean isBuilt() {
return work >= maxWork;
}
@Override
public boolean doWork(float dTime) {
work += dTime;
return isBuilt();
}
private Vector2i[] getBorders() {
Vector2i pos = getWorldPosition().xy();
return new Vector2i[] {
new Vector2i(pos.x, pos.y - 1),
new Vector2i(pos.x - 1, pos.y),
new Vector2i(pos.x + 1, pos.y),
new Vector2i(pos.x - 1, pos.y + 1),
new Vector2i(pos.x + 1, pos.y + 1),
new Vector2i(pos.x, pos.y + 2),
};
}
@Override
public Vector2i[] getWorkablePositions() {
return getBorders();
}
@Override
public String getJobName() {
return "Build Wall";
}
@Override
public Vector2i[] getItemDropoffLocations() {
return getBorders();
}
@Override
public Action[] getActions() {
return new Action[0];
}
@Override
public void runAction(Action action) {
}
@Override
public Detail[] getDetails() {
return new Detail[] {
new BooleanDetail("Built", isBuilt()),
new PercentDetail("Work", work / maxWork, 1),
new ObjectDetail<Integer>("Logs", boulders),
new BooleanDetail("North", wallSides.contains(Direction.NORTH)),
new BooleanDetail("East", wallSides.contains(Direction.EAST)),
new BooleanDetail("South", wallSides.contains(Direction.SOUTH)),
new BooleanDetail("West", wallSides.contains(Direction.WEST)),
};
}
@Override
public boolean isWalkable() {
return false;
}
@Override
public boolean shouldRemove() {
return false;
}
@Override
public void onRemove() {
}
private EnumSet<Direction> wallSides = EnumSet.noneOf(Direction.class);
@Override
public void ping() {
Vector2i pos = getWorldBox().asInt().xy();
wallSides = EnumSet.noneOf(Direction.class);
Tile north = terrain.getTile(pos.x, pos.y - 1);
if(north != null && north.has(Wall.class)) wallSides.add(Direction.NORTH);
Tile east = terrain.getTile(pos.x - 1, pos.y);
if(east != null && east.has(Wall.class)) wallSides.add(Direction.EAST);
Tile south = terrain.getTile(pos.x, pos.y + 1);
if(south != null && south.has(Wall.class)) wallSides.add(Direction.SOUTH);
Tile west = terrain.getTile(pos.x + 1, pos.y);
if(west != null && west.has(Wall.class)) wallSides.add(Direction.WEST);
}
}

View File

@ -1,12 +1,15 @@
package xyz.valnet.hadean.gameobjects.worldobjects.items;
import xyz.valnet.engine.math.Vector2i;
import xyz.valnet.hadean.interfaces.IItemPredicate;
import xyz.valnet.hadean.util.Assets;
import xyz.valnet.hadean.util.Layers;
import xyz.valnet.hadean.util.detail.Detail;
public class Boulder extends Item {
public static IItemPredicate BOULDER_PREDICATE = (item) -> (item instanceof Boulder);
public Boulder(int x, int y) {
setPosition(x, y);
}

View File

@ -8,6 +8,7 @@ public @interface BuildableMetadata {
public enum Type {
AREA,
LINE,
SINGLE
}

View File

@ -0,0 +1,7 @@
package xyz.valnet.hadean.interfaces;
import java.io.Serializable;
public interface IPingable extends Serializable {
public void ping();
}

View File

@ -6,6 +6,7 @@ import java.util.Map;
import xyz.valnet.engine.graphics.Font;
import xyz.valnet.engine.graphics.Sprite;
import xyz.valnet.engine.graphics.Texture;
import xyz.valnet.engine.graphics.Tile16;
import xyz.valnet.engine.graphics.Tile9;
import xyz.valnet.engine.math.Vector4i;
import xyz.valnet.engine.shaders.SimpleShader;
@ -24,7 +25,8 @@ public class Assets {
public static final Tile9 uiFrameDark;
public static final Tile9 fillColor;
public static final Tile16 wall;
public static final Sprite[] defaultTerrain;
public static final Sprite[] growingRice;
public static final Sprite[] farmPlot;
@ -304,6 +306,8 @@ public class Assets {
new Sprite(atlas, 7, 103, 1, 1)
);
wall = new Tile16(atlas, new Vector4i(0, 17 * 8, 32, 32));
System.out.println("END ASSETS");
}
}