diff --git a/res/textures.png b/res/textures.png index f1acfaa..e7b5641 100644 Binary files a/res/textures.png and b/res/textures.png differ diff --git a/src/main/java/xyz/valnet/engine/graphics/Tile16.java b/src/main/java/xyz/valnet/engine/graphics/Tile16.java new file mode 100644 index 0000000..6c5a553 --- /dev/null +++ b/src/main/java/xyz/valnet/engine/graphics/Tile16.java @@ -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, Sprite> cache = new HashMap, 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 directions) { + return cache.get(directions); + } +} diff --git a/src/main/java/xyz/valnet/hadean/designation/CutTreesDesignation.java b/src/main/java/xyz/valnet/hadean/designation/CutTreesDesignation.java index c3e2ea8..b1fd994 100644 --- a/src/main/java/xyz/valnet/hadean/designation/CutTreesDesignation.java +++ b/src/main/java/xyz/valnet/hadean/designation/CutTreesDesignation.java @@ -15,5 +15,4 @@ public class CutTreesDesignation extends Designation { protected void designate(Tree thing) { thing.runAction(Tree.ACTION_CHOP); } - } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/Terrain.java b/src/main/java/xyz/valnet/hadean/gameobjects/Terrain.java index 12e2ba9..53220f5 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/Terrain.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/Terrain.java @@ -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]; } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/Tile.java b/src/main/java/xyz/valnet/hadean/gameobjects/Tile.java index ac3bf91..1f16f5e 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/Tile.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/Tile.java @@ -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 boolean has(Class clazz) { + for(ITileThing thing : stuff) { + if(clazz.isInstance(thing)) { + return true; + } + } + return false; + } } diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java b/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java index 55f51d8..d4b4e34 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/ui/tabs/BuildTab.java @@ -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); diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java index 394e4f4..e3d2ddb 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/WorldObject.java @@ -43,7 +43,6 @@ public abstract class WorldObject extends GameObject { private void updateTileLinks(Set tiles) { if(tiles == null || tiles.size() == 0) return; if(!(this instanceof ITileThing)) return; - boolean inScene = inScene(); Set removeTiles = new HashSet(); Set addTiles = new HashSet(); diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Bed.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Bed.java similarity index 96% rename from src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Bed.java rename to src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Bed.java index 5d3f430..15498d8 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/Bed.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Bed.java @@ -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; diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Wall.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Wall.java new file mode 100644 index 0000000..a606a14 --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/constructions/Wall.java @@ -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("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 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); + } +} diff --git a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Boulder.java b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Boulder.java index fa31d73..b1b4d67 100644 --- a/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Boulder.java +++ b/src/main/java/xyz/valnet/hadean/gameobjects/worldobjects/items/Boulder.java @@ -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); } diff --git a/src/main/java/xyz/valnet/hadean/interfaces/BuildableMetadata.java b/src/main/java/xyz/valnet/hadean/interfaces/BuildableMetadata.java index 89b6978..b36788a 100644 --- a/src/main/java/xyz/valnet/hadean/interfaces/BuildableMetadata.java +++ b/src/main/java/xyz/valnet/hadean/interfaces/BuildableMetadata.java @@ -8,6 +8,7 @@ public @interface BuildableMetadata { public enum Type { AREA, + LINE, SINGLE } diff --git a/src/main/java/xyz/valnet/hadean/interfaces/IPingable.java b/src/main/java/xyz/valnet/hadean/interfaces/IPingable.java new file mode 100644 index 0000000..f16850f --- /dev/null +++ b/src/main/java/xyz/valnet/hadean/interfaces/IPingable.java @@ -0,0 +1,7 @@ +package xyz.valnet.hadean.interfaces; + +import java.io.Serializable; + +public interface IPingable extends Serializable { + public void ping(); +} diff --git a/src/main/java/xyz/valnet/hadean/util/Assets.java b/src/main/java/xyz/valnet/hadean/util/Assets.java index 36153ee..9693eba 100644 --- a/src/main/java/xyz/valnet/hadean/util/Assets.java +++ b/src/main/java/xyz/valnet/hadean/util/Assets.java @@ -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"); } }