From 7659bb91985e4b0338a7cda39c253bef75e40f01 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Sun, 2 Oct 2016 16:49:59 +0200 Subject: [PATCH 01/13] Rough implementation of show what cells we may place on and what not: - it is not efficient, it is re-calculated on draw (not on movement) - it does not respect the outcome yet, ie, even if the result is "no you may not place it here" you can still place a structure --- src/main/java/com/fundynamic/d2tm/Game.java | 1 + .../battlefield/PlacingStructureMouse.java | 84 ++++++++++++++++--- .../d2tm/game/entities/EntityData.java | 4 +- .../PlacementBuildableEntity.java | 9 +- .../entitybuilders/SingleEntityBuilder.java | 8 +- .../game/entities/structures/Structure.java | 37 +++++--- .../com/fundynamic/d2tm/game/map/Cell.java | 2 +- .../d2tm/game/rendering/gui/GuiComposite.java | 11 ++- .../gui/battlefield/BattleField.java | 48 +++++++++-- ...idebarSelectBuildableEntityGuiElement.java | 3 +- .../d2tm/game/terrain/impl/DuneTerrain.java | 1 + .../d2tm/game/terrain/impl/Rock.java | 1 + .../com/fundynamic/d2tm/math/Coordinate.java | 8 ++ .../com/fundynamic/d2tm/utils/SlickUtils.java | 5 ++ .../sidebar/PlacementBuildableEntityTest.java | 5 +- 15 files changed, 187 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/Game.java b/src/main/java/com/fundynamic/d2tm/Game.java index 5c5d3f65..b13e551a 100644 --- a/src/main/java/com/fundynamic/d2tm/Game.java +++ b/src/main/java/com/fundynamic/d2tm/Game.java @@ -18,6 +18,7 @@ public class Game extends StateBasedGame { public static final int SCREEN_HEIGHT = 600; public static final int TILE_SIZE = 32; + public static final int HALF_TILE = TILE_SIZE / 2; public static final boolean DEBUG_INFO = false; diff --git a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java index 22b6cf26..b9ec8a3b 100644 --- a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java +++ b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java @@ -1,28 +1,43 @@ package com.fundynamic.d2tm.game.controls.battlefield; -import com.fundynamic.d2tm.game.entities.Entity; -import com.fundynamic.d2tm.game.entities.EntityData; +import com.fundynamic.d2tm.game.entities.*; +import com.fundynamic.d2tm.game.entities.entitybuilders.PlacementBuildableEntity; import com.fundynamic.d2tm.game.map.Cell; import com.fundynamic.d2tm.game.rendering.gui.battlefield.BattleField; +import com.fundynamic.d2tm.game.terrain.ConstructionGround; import com.fundynamic.d2tm.math.Coordinate; +import com.fundynamic.d2tm.math.MapCoordinate; +import com.fundynamic.d2tm.math.Vector2D; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; +import java.util.List; + public class PlacingStructureMouse extends AbstractBattleFieldMouseBehavior { - private EntityData entityToPlace; + private EntityData entityDataToPlace; + private Entity entityWhoConstructsIt; - public PlacingStructureMouse(BattleField battleField, EntityData entityData) { + public PlacingStructureMouse(BattleField battleField, PlacementBuildableEntity placementBuildableEntity) { super(battleField); - this.entityToPlace = entityData; + this.entityDataToPlace = placementBuildableEntity.getEntityData(); + this.entityWhoConstructsIt = placementBuildableEntity.getEntityWhoConstructsThis(); } @Override public void leftClicked() { - Cell hoverCell = getHoverCell(); +// Cell hoverCell = getHoverCell(); + Coordinate viewportCoordinate = battleField.translateScreenToViewportCoordinate(mouseCoordinates); + + // now substract half of the structure to place, so we make the structure to place center beneath the mouse + Vector2D halfSize = entityDataToPlace.getHalfSize(); + Coordinate topLeftOfStructure = viewportCoordinate.min(halfSize); + + Coordinate coordinates = battleField.getCellByAbsoluteViewportCoordinate(topLeftOfStructure).getCoordinates(); + // TODO: Check if it may be placed or not... - Entity entity = entityRepository.placeOnMap(hoverCell.getCoordinates(), entityToPlace, mouse.getControllingPlayer()); + Entity entity = entityRepository.placeOnMap(coordinates, entityDataToPlace, mouse.getControllingPlayer()); battleField.entityPlacedOnMap(entity); } @@ -41,21 +56,66 @@ public void render(Graphics graphics) { Cell hoverCell = getHoverCell(); if (hoverCell == null) return; - Coordinate absoluteCoordinates = hoverCell.getCoordinates(); - Coordinate viewportCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteCoordinates); + Coordinate constructingEntityCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(entityWhoConstructsIt.getCenteredCoordinate()); graphics.setColor(Color.green); float lineWidth = graphics.getLineWidth(); graphics.setLineWidth(1.1f); - graphics.drawImage(entityToPlace.getFirstImage(), viewportCoordinate.getXAsInt(), viewportCoordinate.getYAsInt()); - graphics.drawRect(viewportCoordinate.getXAsInt(), viewportCoordinate.getYAsInt(), entityToPlace.getWidth(), entityToPlace.getHeight()); + + // first get absolute viewport coordinates, we can calculate on the battlefield with that + Coordinate viewportCoordinate = battleField.translateScreenToViewportCoordinate(mouseCoordinates); + + // now substract half of the structure to place, so we make the structure to place center beneath the mouse + Vector2D halfSize = entityDataToPlace.getHalfSize(); + Coordinate topLeftOfStructure = viewportCoordinate.min(halfSize); + +// Coordinate absoluteMapCoordinateOfTopleftOfStructure = battleField.translateViewportCoordinateToAbsoluteMapCoordinate(topLeftOfStructure); + Cell topLeftCellOfStructure = battleField.getCellByAbsoluteViewportCoordinate(topLeftOfStructure); + Coordinate absoluteMapCoordinateOfTopleftOfStructure = topLeftCellOfStructure.getCoordinates(); + + EntityRepository entityRepository = battleField.getEntityRepository(); + + List allCellsAsCoordinates = this.entityDataToPlace.getAllCellsAsCoordinates(absoluteMapCoordinateOfTopleftOfStructure); + for (MapCoordinate mapCoordinate : allCellsAsCoordinates) { + Coordinate absoluteMapCoordinate = mapCoordinate.toCoordinate(); + Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); + + Cell cell = battleField.getCellByAbsoluteViewportCoordinate(coordinate); + boolean placeable = cell.isVisibleFor(player); + if ((cell.getTerrain() instanceof ConstructionGround) == false) { + placeable = false; + } + + if (placeable) { + EntitiesSet entitiesAtMapCoordinate = entityRepository.findAliveEntitiesOfTypeAtVector(absoluteMapCoordinate, EntityType.STRUCTURE, EntityType.UNIT); + placeable = entitiesAtMapCoordinate.isEmpty(); + } + + if (placeable) { + graphics.setColor(Color.green); + graphics.drawRect(coordinate.getXAsInt(), coordinate.getYAsInt(), 32, 32); + }else { + graphics.setColor(Color.red); + graphics.drawRect(coordinate.getXAsInt(), coordinate.getYAsInt(), 32, 32); + graphics.drawLine(coordinate.getXAsInt(), coordinate.getYAsInt(), coordinate.getXAsInt() + 32, coordinate.getYAsInt() + 32); + graphics.drawLine(coordinate.getXAsInt(), coordinate.getYAsInt() + 32, coordinate.getXAsInt() + 32, coordinate.getYAsInt()); + } + } + +// SlickUtils.drawLine(graphics, constructingEntityCoordinate, mouseCoordinates); + graphics.setLineWidth(lineWidth); } + @Override + public void movedTo(Vector2D coordinates) { + super.movedTo(coordinates); + } + @Override public String toString() { return "PlacingStructureMouse{" + - "entityToPlace=" + entityToPlace + + "entityDataToPlace=" + entityDataToPlace + '}'; } } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java index 7b38b3f5..adf5bc74 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java @@ -241,8 +241,8 @@ public List getAllCellsAsCoordinates(Coordinate coordinate) { List result = new ArrayList<>(widthInCells * heightInCells); for (int x = 0; x < widthInCells; x++) { for (int y = 0; y < heightInCells; y++) { - int vecX = coordinate.getXAsInt() + x * Game.TILE_SIZE; - int vecY = coordinate.getYAsInt() + y * Game.TILE_SIZE; + int vecX = coordinate.getXAsInt() + (x * Game.TILE_SIZE); + int vecY = coordinate.getYAsInt() + (y * Game.TILE_SIZE); result.add(Coordinate.create(vecX, vecY).toMapCoordinate()); } } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/PlacementBuildableEntity.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/PlacementBuildableEntity.java index fccbe9c9..2dd4c688 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/PlacementBuildableEntity.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/PlacementBuildableEntity.java @@ -1,6 +1,7 @@ package com.fundynamic.d2tm.game.entities.entitybuilders; +import com.fundynamic.d2tm.game.entities.Entity; import com.fundynamic.d2tm.game.entities.EntityData; import com.fundynamic.d2tm.game.entities.entitybuilders.AbstractBuildableEntity; import com.fundynamic.d2tm.game.entities.sidebar.BuildableState; @@ -12,8 +13,11 @@ */ public class PlacementBuildableEntity extends AbstractBuildableEntity { - public PlacementBuildableEntity(EntityData entityData) { + private Entity entityWhoConstructsThis; + + public PlacementBuildableEntity(EntityData entityData, Entity entityWhoConstructsThis) { super(entityData); + this.entityWhoConstructsThis = entityWhoConstructsThis; } @Override @@ -24,4 +28,7 @@ public void update(float deltaInSeconds) { } } + public Entity getEntityWhoConstructsThis() { + return entityWhoConstructsThis; + } } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java index 9fbbd908..f3f0d974 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java @@ -13,15 +13,17 @@ */ public class SingleEntityBuilder implements EntityBuilder { + private Entity constructingForEntity; private AbstractBuildableEntity buildingEntity = null; private List buildableEntities = new ArrayList<>(); - public SingleEntityBuilder(List entityDatasToBuild) { - for (EntityData entityDataToBuild : entityDatasToBuild ) { + public SingleEntityBuilder(List entityDatasToBuild, Entity constructingForEntity) { + this.constructingForEntity = constructingForEntity; + for (EntityData entityDataToBuild : entityDatasToBuild) { // TODO: make more flexible!! if (entityDataToBuild.isTypeStructure()) { - buildableEntities.add(new PlacementBuildableEntity(entityDataToBuild)); + buildableEntities.add(new PlacementBuildableEntity(entityDataToBuild, constructingForEntity)); } else { buildableEntities.add(new SpawningBuildableEntity(entityDataToBuild)); } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java index f464987c..58ff0b5e 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java @@ -58,7 +58,7 @@ public Structure(Coordinate coordinate, SpriteSheet spritesheet, Player player, } } - this.entityBuilder = new SingleEntityBuilder(entityDatas); + this.entityBuilder = new SingleEntityBuilder(entityDatas, this); } @@ -144,16 +144,33 @@ public EntityType getEntityType() { @Override public void render(Graphics graphics, int x, int y) { Image sprite = getSprite(); - graphics.drawImage(sprite, x, y); +// graphics.drawImage(sprite, x, y); + graphics.drawRect(x, y, entityData.getWidth(), entityData.getHeight()); + Coordinate centeredCoordinate = getCenteredCoordinate(); MapCoordinate mapCoordinate = coordinate.toMapCoordinate(); - if (Game.DEBUG_INFO) { - SlickUtils.drawShadowedText( - graphics, - Colors.WHITE, - "" + mapCoordinate.getXAsInt() + "," + mapCoordinate.getYAsInt(), - x, - y); - } + SlickUtils.drawShadowedText( + graphics, + Colors.WHITE, + "" + coordinate.getXAsInt() + "," + coordinate.getYAsInt(), + x, + y); + + SlickUtils.drawShadowedText( + graphics, + Colors.WHITE, + "" + centeredCoordinate.getXAsInt() + "," + centeredCoordinate.getYAsInt(), + x, + y + 18); + + +// if (Game.DEBUG_INFO) { +// SlickUtils.drawShadowedText( +// graphics, +// Colors.WHITE, +// "" + mapCoordinate.getXAsInt() + "," + mapCoordinate.getYAsInt(), +// x, +// y); +// } } public void select() { diff --git a/src/main/java/com/fundynamic/d2tm/game/map/Cell.java b/src/main/java/com/fundynamic/d2tm/game/map/Cell.java index bb561969..ee1b4591 100644 --- a/src/main/java/com/fundynamic/d2tm/game/map/Cell.java +++ b/src/main/java/com/fundynamic/d2tm/game/map/Cell.java @@ -85,7 +85,7 @@ public Cell getCellRight() { * @return */ public Coordinate getCoordinates() { - return getMapCoordinate().toCoordinate(); + return mapCoordinate.toCoordinate(); } public boolean isAtSameLocationAs(Cell other) { diff --git a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/GuiComposite.java b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/GuiComposite.java index 35444e2f..d7699532 100644 --- a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/GuiComposite.java +++ b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/GuiComposite.java @@ -9,6 +9,7 @@ import com.fundynamic.d2tm.game.controls.battlefield.PlacingStructureMouse; import com.fundynamic.d2tm.game.entities.Entity; import com.fundynamic.d2tm.game.entities.entitybuilders.AbstractBuildableEntity; +import com.fundynamic.d2tm.game.entities.entitybuilders.PlacementBuildableEntity; import com.fundynamic.d2tm.game.rendering.gui.battlefield.BattleField; import com.fundynamic.d2tm.game.rendering.gui.sidebar.Sidebar; import com.fundynamic.d2tm.math.Vector2D; @@ -171,8 +172,8 @@ public void entityBuilderSelected(Entity entityBuilder) { * Event: a buildable Entity which should be placed is selected. * @param abstractBuildableEntity */ - public void wantsToPlaceBuildableEntityOnBattlefield(AbstractBuildableEntity abstractBuildableEntity) { - battleField.setMouseBehavior(new PlacingStructureMouse(battleField, abstractBuildableEntity.getEntityData())); + public void wantsToPlaceBuildableEntityOnBattlefield(PlacementBuildableEntity abstractBuildableEntity) { + battleField.setMouseBehavior(new PlacingStructureMouse(battleField, abstractBuildableEntity)); } /** @@ -187,4 +188,10 @@ public void entityPlacedOnMap(Entity entity) { public void allEntityBuildersDeSelected() { sidebar.hideEntityBuilderGui(); } + + public void wantsToPlaceBuildableEntityOnBattlefield(AbstractBuildableEntity abstractBuildableEntity) { + if (abstractBuildableEntity instanceof PlacementBuildableEntity) { + wantsToPlaceBuildableEntityOnBattlefield((PlacementBuildableEntity) abstractBuildableEntity); + } + } } diff --git a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java index 0b184418..510f040b 100644 --- a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java +++ b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java @@ -165,13 +165,31 @@ private void drawBufferToGraphics(Graphics graphics, Vector2D drawingVector) { } public Coordinate translateAbsoluteMapCoordinateToViewportCoordinate(Coordinate absoluteMapCoordinate) { - return new Coordinate(absoluteMapCoordinate.min(viewingVector)); + return absoluteMapCoordinate.min(viewingVector); } + /** + *

+ * Given an absolute viewport coordinate (acquire one via {@link #translateScreenToViewportCoordinate(Vector2D)}, returns + * an absolute map {@link Coordinate}. + *

+ * @param positionOnViewport + * @return + */ public Coordinate translateViewportCoordinateToAbsoluteMapCoordinate(Vector2D positionOnViewport) { return new Coordinate(positionOnViewport.add(viewingVector)); } + /** + *

+ * Given a Vector that represents a screen(/mouse) coordinate. The coordinate will be corrected into the + * viewport (of this battlefield class). For instance: assuming the viewport is on 10,20 drawn on the SCREEN. A given + * 10,20 coordinate will result in a 0,0 absolute coordinate. A screen coordinate of 20, 30 results in 0,10. + *

+ * Can return NULL when coordinate is not within the dimensions of this Gui Element! + * @param screenPosition + * @return + */ public Coordinate translateScreenToViewportCoordinate(Vector2D screenPosition) { if (!isVectorWithin(screenPosition)) return null; // outside our viewport! return new Coordinate(screenPosition.min(getTopLeft())); @@ -218,13 +236,31 @@ public void draggedToCoordinates(Vector2D coordinates) { @Override public void movedTo(Vector2D coordinates) { this.mouseBehavior.movedTo(coordinates); + mouseMovedToCell(getCellByScreenCoordinate(coordinates)); + } - Coordinate viewportCoordinate = translateScreenToViewportCoordinate(coordinates); - Coordinate absoluteMapCoordinate = translateViewportCoordinateToAbsoluteMapCoordinate(viewportCoordinate); - - Cell cellByAbsoluteMapCoordinates = map.getCellByAbsoluteMapCoordinates(absoluteMapCoordinate); + /** + * Returns a Cell based on the screen coordinate. + * + * @param coordinates + * @return + */ + public Cell getCellByScreenCoordinate(Vector2D coordinates) { + Coordinate absoluteViewportCoordinate = translateScreenToViewportCoordinate(coordinates); + return getCellByAbsoluteViewportCoordinate(absoluteViewportCoordinate); + } - mouseMovedToCell(cellByAbsoluteMapCoordinates); + /** + *

+ * Given an absolute viewport coordinate (you can acquire one by calling {@link #translateScreenToViewportCoordinate(Vector2D)}, this method + * figures out which map coordinate is underneath and what cell is associated with it. Returns the found cell. + *

+ * @param absoluteViewportCoordinate + * @return + */ + public Cell getCellByAbsoluteViewportCoordinate(Coordinate absoluteViewportCoordinate) { + Coordinate absoluteMapCoordinate = translateViewportCoordinateToAbsoluteMapCoordinate(absoluteViewportCoordinate); + return map.getCellByAbsoluteMapCoordinates(absoluteMapCoordinate); } @Override diff --git a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarSelectBuildableEntityGuiElement.java b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarSelectBuildableEntityGuiElement.java index 346e54ea..6bebfd4b 100644 --- a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarSelectBuildableEntityGuiElement.java +++ b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarSelectBuildableEntityGuiElement.java @@ -75,7 +75,8 @@ public void leftClicked() { // TODO: Is done constructing. if (entityBuilder.isAwaitingPlacement()) { // TODO move to BattlefieldInteractable interface?! - guiComposite.wantsToPlaceBuildableEntityOnBattlefield(focussedRenderableBuildableEntity.getAbstractBuildableEntity()); + AbstractBuildableEntity abstractBuildableEntity = focussedRenderableBuildableEntity.getAbstractBuildableEntity(); + guiComposite.wantsToPlaceBuildableEntityOnBattlefield(abstractBuildableEntity); } } diff --git a/src/main/java/com/fundynamic/d2tm/game/terrain/impl/DuneTerrain.java b/src/main/java/com/fundynamic/d2tm/game/terrain/impl/DuneTerrain.java index cd2e1626..20f466cc 100644 --- a/src/main/java/com/fundynamic/d2tm/game/terrain/impl/DuneTerrain.java +++ b/src/main/java/com/fundynamic/d2tm/game/terrain/impl/DuneTerrain.java @@ -58,4 +58,5 @@ private Image makeTileImage(MapEditor.TerrainFacing terrainFacing) { public boolean isPassable(Entity entity) { return true; } + } diff --git a/src/main/java/com/fundynamic/d2tm/game/terrain/impl/Rock.java b/src/main/java/com/fundynamic/d2tm/game/terrain/impl/Rock.java index 65ee2822..613582a1 100644 --- a/src/main/java/com/fundynamic/d2tm/game/terrain/impl/Rock.java +++ b/src/main/java/com/fundynamic/d2tm/game/terrain/impl/Rock.java @@ -26,4 +26,5 @@ public int getTerrainType() { return TERRAIN_ROCK; } + } diff --git a/src/main/java/com/fundynamic/d2tm/math/Coordinate.java b/src/main/java/com/fundynamic/d2tm/math/Coordinate.java index 74038d16..526f7bc7 100644 --- a/src/main/java/com/fundynamic/d2tm/math/Coordinate.java +++ b/src/main/java/com/fundynamic/d2tm/math/Coordinate.java @@ -34,4 +34,12 @@ public Coordinate add(Vector2D vec) { public Coordinate min(Vector2D otherVector) { return new Coordinate(super.min(otherVector)); } + + public Coordinate addHalfTile() { + return new Coordinate(getX() + Game.HALF_TILE, getYAsInt() + Game.HALF_TILE); + } + + public static Coordinate create(Vector2D vector2D) { + return new Coordinate(vector2D.getX(), vector2D.getY()); + } } diff --git a/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java b/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java index d69a9291..17b10496 100644 --- a/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java +++ b/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java @@ -1,6 +1,7 @@ package com.fundynamic.d2tm.utils; +import com.fundynamic.d2tm.math.Coordinate; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; @@ -25,6 +26,10 @@ public static void drawShadowedText(Graphics graphics, Color color, String msg, drawText(graphics, color, msg, x, y); // text } + public static void drawLine(Graphics graphics, Coordinate from, Coordinate to) { + graphics.drawLine(from.getXAsInt(), from.getYAsInt(), to.getXAsInt(), to.getYAsInt()); + } + /** * Draws a text */ diff --git a/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java b/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java index c8d4c764..abd13680 100644 --- a/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java +++ b/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java @@ -1,5 +1,6 @@ package com.fundynamic.d2tm.game.entities.sidebar; +import com.fundynamic.d2tm.game.entities.Entity; import com.fundynamic.d2tm.game.entities.EntityData; import com.fundynamic.d2tm.game.entities.entitybuilders.PlacementBuildableEntity; import org.junit.Assert; @@ -19,7 +20,7 @@ public void setUp() { @Test public void buildStates() { - PlacementBuildableEntity placementBuildableEntity = new PlacementBuildableEntity(entityData); + PlacementBuildableEntity placementBuildableEntity = new PlacementBuildableEntity(entityData, null); Assert.assertEquals(BuildableState.SELECTABLE, placementBuildableEntity.getBuildableState()); // start building @@ -38,7 +39,7 @@ public void buildStates() { @Test public void progress() { - PlacementBuildableEntity placementBuildableEntity = new PlacementBuildableEntity(entityData); + PlacementBuildableEntity placementBuildableEntity = new PlacementBuildableEntity(entityData, null); placementBuildableEntity.startBuilding(); Assert.assertEquals(0.0F, placementBuildableEntity.getProgress(), 000.1f); From df311fbfe56743c15b61162fe47902e15aecf5cd Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Sun, 2 Oct 2016 16:57:06 +0200 Subject: [PATCH 02/13] draw structures again, remove debug rendering --- .../game/entities/structures/Structure.java | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java index 58ff0b5e..ff3c0d42 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java @@ -144,33 +144,17 @@ public EntityType getEntityType() { @Override public void render(Graphics graphics, int x, int y) { Image sprite = getSprite(); -// graphics.drawImage(sprite, x, y); - graphics.drawRect(x, y, entityData.getWidth(), entityData.getHeight()); - Coordinate centeredCoordinate = getCenteredCoordinate(); - MapCoordinate mapCoordinate = coordinate.toMapCoordinate(); - SlickUtils.drawShadowedText( - graphics, - Colors.WHITE, - "" + coordinate.getXAsInt() + "," + coordinate.getYAsInt(), - x, - y); - - SlickUtils.drawShadowedText( - graphics, - Colors.WHITE, - "" + centeredCoordinate.getXAsInt() + "," + centeredCoordinate.getYAsInt(), - x, - y + 18); - - -// if (Game.DEBUG_INFO) { -// SlickUtils.drawShadowedText( -// graphics, -// Colors.WHITE, -// "" + mapCoordinate.getXAsInt() + "," + mapCoordinate.getYAsInt(), -// x, -// y); -// } + graphics.drawImage(sprite, x, y); + + if (Game.DEBUG_INFO) { + MapCoordinate mapCoordinate = coordinate.toMapCoordinate(); + SlickUtils.drawShadowedText( + graphics, + Colors.WHITE, + "" + mapCoordinate.getXAsInt() + "," + mapCoordinate.getYAsInt(), + x, + y); + } } public void select() { From e50410f86e59f906c67b08c799bc66acc5ab1615 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Sun, 2 Oct 2016 16:57:25 +0200 Subject: [PATCH 03/13] render transparent red/greenish blocks instead of crosses --- .../battlefield/PlacingStructureMouse.java | 18 +++++++++--------- .../java/com/fundynamic/d2tm/utils/Colors.java | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java index b9ec8a3b..df7651a5 100644 --- a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java +++ b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java @@ -9,6 +9,7 @@ import com.fundynamic.d2tm.math.Coordinate; import com.fundynamic.d2tm.math.MapCoordinate; import com.fundynamic.d2tm.math.Vector2D; +import com.fundynamic.d2tm.utils.Colors; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; @@ -76,6 +77,10 @@ public void render(Graphics graphics) { EntityRepository entityRepository = battleField.getEntityRepository(); List allCellsAsCoordinates = this.entityDataToPlace.getAllCellsAsCoordinates(absoluteMapCoordinateOfTopleftOfStructure); + MapCoordinate topLeftMapCoordinate = allCellsAsCoordinates.get(0); + Coordinate coordinateTopLeft = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(topLeftMapCoordinate.toCoordinate()); + graphics.drawImage(entityDataToPlace.getFirstImage(), coordinateTopLeft.getXAsInt(), coordinateTopLeft.getYAsInt()); + for (MapCoordinate mapCoordinate : allCellsAsCoordinates) { Coordinate absoluteMapCoordinate = mapCoordinate.toCoordinate(); Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); @@ -92,18 +97,13 @@ public void render(Graphics graphics) { } if (placeable) { - graphics.setColor(Color.green); - graphics.drawRect(coordinate.getXAsInt(), coordinate.getYAsInt(), 32, 32); - }else { - graphics.setColor(Color.red); - graphics.drawRect(coordinate.getXAsInt(), coordinate.getYAsInt(), 32, 32); - graphics.drawLine(coordinate.getXAsInt(), coordinate.getYAsInt(), coordinate.getXAsInt() + 32, coordinate.getYAsInt() + 32); - graphics.drawLine(coordinate.getXAsInt(), coordinate.getYAsInt() + 32, coordinate.getXAsInt() + 32, coordinate.getYAsInt()); + graphics.setColor(Colors.GREEN_ALPHA_128); + } else { + graphics.setColor(Colors.RED_ALPHA_128); } + graphics.fillRect(coordinate.getXAsInt(), coordinate.getYAsInt(), 32, 32); } -// SlickUtils.drawLine(graphics, constructingEntityCoordinate, mouseCoordinates); - graphics.setLineWidth(lineWidth); } diff --git a/src/main/java/com/fundynamic/d2tm/utils/Colors.java b/src/main/java/com/fundynamic/d2tm/utils/Colors.java index d5d21d0d..53244983 100644 --- a/src/main/java/com/fundynamic/d2tm/utils/Colors.java +++ b/src/main/java/com/fundynamic/d2tm/utils/Colors.java @@ -10,6 +10,8 @@ public class Colors { public static final Color BLACK = Color.black; public static final Color WHITE = Color.white; + public static final Color GREEN_ALPHA_128 = new Color(0,255,0,128); + public static final Color RED_ALPHA_128 = new Color(255,0,0,128); public static final Color BLACK_ALPHA_128 = new Color(0,0,0,128); public static final Color WHITE_ALPHA_128 = new Color(255, 255, 255, 128); public static final Color RED = new Color(255, 0, 0, 128); @@ -64,4 +66,5 @@ public static Color create(float r, float g, float b) { public static String toString(Color c) { return "Color(" + c.getRed() + "," + c.getGreen() + "," + c.getBlue() + "," + c.getAlpha() + ")"; } + } From cf17f62d6dd8db4551dfad0d74438274280f2883 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Sun, 2 Oct 2016 17:24:50 +0200 Subject: [PATCH 04/13] Optimize & respect placement results - only calculate state per mouse movement - show state per cell (still) - use java 8 streaming for a change --- .../battlefield/PlacingStructureMouse.java | 125 +++++++++++------- 1 file changed, 79 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java index df7651a5..f12db554 100644 --- a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java +++ b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java @@ -1,6 +1,7 @@ package com.fundynamic.d2tm.game.controls.battlefield; +import com.fundynamic.d2tm.Game; import com.fundynamic.d2tm.game.entities.*; import com.fundynamic.d2tm.game.entities.entitybuilders.PlacementBuildableEntity; import com.fundynamic.d2tm.game.map.Cell; @@ -10,35 +11,36 @@ import com.fundynamic.d2tm.math.MapCoordinate; import com.fundynamic.d2tm.math.Vector2D; import com.fundynamic.d2tm.utils.Colors; -import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toList; public class PlacingStructureMouse extends AbstractBattleFieldMouseBehavior { private EntityData entityDataToPlace; private Entity entityWhoConstructsIt; + private List mapCoordinatesForEntityToPlace; + private EntityRepository entityRepository; public PlacingStructureMouse(BattleField battleField, PlacementBuildableEntity placementBuildableEntity) { super(battleField); + this.entityRepository = battleField.getEntityRepository(); + this.entityDataToPlace = placementBuildableEntity.getEntityData(); this.entityWhoConstructsIt = placementBuildableEntity.getEntityWhoConstructsThis(); + this.mapCoordinatesForEntityToPlace = new ArrayList<>(); } @Override public void leftClicked() { -// Cell hoverCell = getHoverCell(); - Coordinate viewportCoordinate = battleField.translateScreenToViewportCoordinate(mouseCoordinates); - - // now substract half of the structure to place, so we make the structure to place center beneath the mouse - Vector2D halfSize = entityDataToPlace.getHalfSize(); - Coordinate topLeftOfStructure = viewportCoordinate.min(halfSize); + boolean isPassable = !this.mapCoordinatesForEntityToPlace.stream().anyMatch(c -> c.isPassable == false); + if (!isPassable) return; - Coordinate coordinates = battleField.getCellByAbsoluteViewportCoordinate(topLeftOfStructure).getCoordinates(); - - // TODO: Check if it may be placed or not... - Entity entity = entityRepository.placeOnMap(coordinates, entityDataToPlace, mouse.getControllingPlayer()); + Entity entity = entityRepository.placeOnMap(getAbsoluteCoordinateTopLeftOfStructureToPlace(), entityDataToPlace, mouse.getControllingPlayer()); battleField.entityPlacedOnMap(entity); } @@ -56,60 +58,80 @@ public void mouseMovedToCell(Cell cell) { public void render(Graphics graphics) { Cell hoverCell = getHoverCell(); if (hoverCell == null) return; + if (mapCoordinatesForEntityToPlace.isEmpty()) return; - Coordinate constructingEntityCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(entityWhoConstructsIt.getCenteredCoordinate()); + MapCoordinate topLeftMapCoordinate = mapCoordinatesForEntityToPlace.get(0).mapCoordinate; - graphics.setColor(Color.green); - float lineWidth = graphics.getLineWidth(); - graphics.setLineWidth(1.1f); + Coordinate coordinateTopLeft = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(topLeftMapCoordinate.toCoordinate()); + graphics.drawImage(entityDataToPlace.getFirstImage(), coordinateTopLeft.getXAsInt(), coordinateTopLeft.getYAsInt()); - // first get absolute viewport coordinates, we can calculate on the battlefield with that - Coordinate viewportCoordinate = battleField.translateScreenToViewportCoordinate(mouseCoordinates); + for (PlaceableMapCoordinateCandidate placeableMapCoordinateCandidate : mapCoordinatesForEntityToPlace) { + Coordinate absoluteMapCoordinate = placeableMapCoordinateCandidate.mapCoordinate.toCoordinate(); + Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); + if (placeableMapCoordinateCandidate.isPassable) { + graphics.setColor(Colors.GREEN_ALPHA_128); + } else { + graphics.setColor(Colors.RED_ALPHA_128); + } + graphics.fillRect(coordinate.getXAsInt(), coordinate.getYAsInt(), Game.TILE_SIZE, Game.TILE_SIZE); + } + } - // now substract half of the structure to place, so we make the structure to place center beneath the mouse - Vector2D halfSize = entityDataToPlace.getHalfSize(); - Coordinate topLeftOfStructure = viewportCoordinate.min(halfSize); + @Override + public void movedTo(Vector2D coordinates) { + super.movedTo(coordinates); -// Coordinate absoluteMapCoordinateOfTopleftOfStructure = battleField.translateViewportCoordinateToAbsoluteMapCoordinate(topLeftOfStructure); - Cell topLeftCellOfStructure = battleField.getCellByAbsoluteViewportCoordinate(topLeftOfStructure); - Coordinate absoluteMapCoordinateOfTopleftOfStructure = topLeftCellOfStructure.getCoordinates(); + Coordinate absoluteMapCoordinateOfTopleftOfStructure = getAbsoluteCoordinateTopLeftOfStructureToPlace(); - EntityRepository entityRepository = battleField.getEntityRepository(); + // first determine all cells that will be occupied + mapCoordinatesForEntityToPlace = new ArrayList<>(); - List allCellsAsCoordinates = this.entityDataToPlace.getAllCellsAsCoordinates(absoluteMapCoordinateOfTopleftOfStructure); - MapCoordinate topLeftMapCoordinate = allCellsAsCoordinates.get(0); - Coordinate coordinateTopLeft = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(topLeftMapCoordinate.toCoordinate()); - graphics.drawImage(entityDataToPlace.getFirstImage(), coordinateTopLeft.getXAsInt(), coordinateTopLeft.getYAsInt()); + List allMapCoordinates = this.entityDataToPlace.getAllCellsAsCoordinates(absoluteMapCoordinateOfTopleftOfStructure); + + mapCoordinatesForEntityToPlace = + allMapCoordinates + .stream() + .map(mapCoordinate -> new PlaceableMapCoordinateCandidate(mapCoordinate, false)) + .collect(toList()); - for (MapCoordinate mapCoordinate : allCellsAsCoordinates) { - Coordinate absoluteMapCoordinate = mapCoordinate.toCoordinate(); + // Now, do checks if the structure may be placed + for (PlaceableMapCoordinateCandidate placeableMapCoordinateCandidate : mapCoordinatesForEntityToPlace) { + Coordinate absoluteMapCoordinate = placeableMapCoordinateCandidate.mapCoordinate.toCoordinate(); Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); Cell cell = battleField.getCellByAbsoluteViewportCoordinate(coordinate); - boolean placeable = cell.isVisibleFor(player); - if ((cell.getTerrain() instanceof ConstructionGround) == false) { - placeable = false; - } - if (placeable) { - EntitiesSet entitiesAtMapCoordinate = entityRepository.findAliveEntitiesOfTypeAtVector(absoluteMapCoordinate, EntityType.STRUCTURE, EntityType.UNIT); - placeable = entitiesAtMapCoordinate.isEmpty(); + // first check if it is visible (easiest check) + boolean isPlaceable = cell.isVisibleFor(player); + + // visible? then check if it may be constructed (is it construction ground?) + if (isPlaceable && (cell.getTerrain() instanceof ConstructionGround) == false) { + isPlaceable = false; } - if (placeable) { - graphics.setColor(Colors.GREEN_ALPHA_128); - } else { - graphics.setColor(Colors.RED_ALPHA_128); + // TODO: distance checking + Coordinate constructingEntityCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(entityWhoConstructsIt.getCenteredCoordinate()); + + // still placeable? good. Final (expensive) check -> any other units that may block this? + if (isPlaceable) { + EntitiesSet entitiesAtMapCoordinate = entityRepository.findAliveEntitiesOfTypeAtVector(absoluteMapCoordinate, EntityType.STRUCTURE, EntityType.UNIT); + isPlaceable = entitiesAtMapCoordinate.isEmpty(); } - graphics.fillRect(coordinate.getXAsInt(), coordinate.getYAsInt(), 32, 32); + placeableMapCoordinateCandidate.isPassable = isPlaceable; } - graphics.setLineWidth(lineWidth); } - @Override - public void movedTo(Vector2D coordinates) { - super.movedTo(coordinates); + public Coordinate getAbsoluteCoordinateTopLeftOfStructureToPlace() { + // first get absolute viewport coordinates, we can calculate on the battlefield with that + Coordinate viewportCoordinate = battleField.translateScreenToViewportCoordinate(mouseCoordinates); + + // now substract half of the structure to place, so we make the structure to place center beneath the mouse + Vector2D halfSize = entityDataToPlace.getHalfSize(); + Coordinate topLeftOfStructure = viewportCoordinate.min(halfSize); + + Cell topLeftCellOfStructure = battleField.getCellByAbsoluteViewportCoordinate(topLeftOfStructure); + return topLeftCellOfStructure.getCoordinates(); } @Override @@ -118,4 +140,15 @@ public String toString() { "entityDataToPlace=" + entityDataToPlace + '}'; } + + private class PlaceableMapCoordinateCandidate { + + public MapCoordinate mapCoordinate; + public boolean isPassable = false; + + public PlaceableMapCoordinateCandidate(MapCoordinate mapCoordinate, boolean passable) { + this.mapCoordinate = mapCoordinate; + this.isPassable = passable; + } + } } From beee4da18301c6330a87f0105c87af98e45b7042 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Sun, 2 Oct 2016 20:01:04 +0200 Subject: [PATCH 05/13] make sure structure must be in reach plus... - render per cell if it is within reach or not - all cells must be in reach now (makes more sense) - get buildRange from entityData (todo: grab from rules.ini file) --- .../battlefield/PlacingStructureMouse.java | 121 +++++++++++++----- .../d2tm/game/entities/EntityData.java | 1 + .../game/entities/structures/Structure.java | 10 ++ .../com/fundynamic/d2tm/utils/Colors.java | 3 + 4 files changed, 104 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java index f12db554..83e0222c 100644 --- a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java +++ b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java @@ -4,6 +4,7 @@ import com.fundynamic.d2tm.Game; import com.fundynamic.d2tm.game.entities.*; import com.fundynamic.d2tm.game.entities.entitybuilders.PlacementBuildableEntity; +import com.fundynamic.d2tm.game.entities.predicates.PredicateBuilder; import com.fundynamic.d2tm.game.map.Cell; import com.fundynamic.d2tm.game.rendering.gui.battlefield.BattleField; import com.fundynamic.d2tm.game.terrain.ConstructionGround; @@ -15,7 +16,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; @@ -37,8 +37,7 @@ public PlacingStructureMouse(BattleField battleField, PlacementBuildableEntity p @Override public void leftClicked() { - boolean isPassable = !this.mapCoordinatesForEntityToPlace.stream().anyMatch(c -> c.isPassable == false); - if (!isPassable) return; + if (!isAllCoordinatesForEntityToPlacePassableAndWithinReach()) return; Entity entity = entityRepository.placeOnMap(getAbsoluteCoordinateTopLeftOfStructureToPlace(), entityDataToPlace, mouse.getControllingPlayer()); battleField.entityPlacedOnMap(entity); @@ -65,14 +64,70 @@ public void render(Graphics graphics) { Coordinate coordinateTopLeft = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(topLeftMapCoordinate.toCoordinate()); graphics.drawImage(entityDataToPlace.getFirstImage(), coordinateTopLeft.getXAsInt(), coordinateTopLeft.getYAsInt()); + // Now, do checks if the structure may be placed + for (PlaceableMapCoordinateCandidate placeableMapCoordinateCandidate : mapCoordinatesForEntityToPlace) { + Coordinate absoluteMapCoordinate = placeableMapCoordinateCandidate.mapCoordinate.toCoordinate().addHalfTile(); + Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); + + Cell cell = battleField.getCellByAbsoluteViewportCoordinate(coordinate); + + // first check if it is visible (easiest check) + boolean isPlaceable = cell.isVisibleFor(player); + + // visible? then check if it may be constructed (is it construction ground?) + if (isPlaceable && (cell.getTerrain() instanceof ConstructionGround) == false) { + isPlaceable = false; + } + + // Calculate distance first, later do checking + Entity closestFriendlyStructure = findClosestStructureOfPlayer(battleField.translateViewportCoordinateToAbsoluteMapCoordinate(coordinate)); + Coordinate constructingEntityCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(closestFriendlyStructure.getCenteredCoordinate()); + + placeableMapCoordinateCandidate.distance = constructingEntityCoordinate.distance(coordinate); + + graphics.setColor(Colors.WHITE); + graphics.drawLine( + coordinate.getXAsInt(), + coordinate.getYAsInt(), + constructingEntityCoordinate.getXAsInt(), + constructingEntityCoordinate.getYAsInt() + ); + + // still placeable? good. Final (expensive) check -> any other units that may block this? + if (isPlaceable) { + EntitiesSet entitiesAtMapCoordinate = entityRepository.findAliveEntitiesOfTypeAtVector(absoluteMapCoordinate, EntityType.STRUCTURE, EntityType.UNIT); + isPlaceable = entitiesAtMapCoordinate.isEmpty(); + } + placeableMapCoordinateCandidate.isPassable = isPlaceable; + } + + float maxDistance = entityWhoConstructsIt.getEntityData().buildRange; // from center of the structure that built this entity (to place) + + for (PlaceableMapCoordinateCandidate pmcc : mapCoordinatesForEntityToPlace) { + if (!pmcc.isPassable) continue; // not worth evaluating distance + pmcc.isWithinReach = pmcc.distance < maxDistance; + } + + + // Render stuff! for (PlaceableMapCoordinateCandidate placeableMapCoordinateCandidate : mapCoordinatesForEntityToPlace) { Coordinate absoluteMapCoordinate = placeableMapCoordinateCandidate.mapCoordinate.toCoordinate(); Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); + if (placeableMapCoordinateCandidate.isPassable) { - graphics.setColor(Colors.GREEN_ALPHA_128); + if (placeableMapCoordinateCandidate.isWithinReach) { + graphics.setColor(Colors.GREEN_ALPHA_128); + } else { + graphics.setColor(Colors.YELLOW_ALPHA_128); + } } else { - graphics.setColor(Colors.RED_ALPHA_128); + if (placeableMapCoordinateCandidate.isWithinReach) { + graphics.setColor(Colors.RED_ALPHA_128); + } else { + graphics.setColor(Colors.DARK_RED_ALPHA_128); + } } + graphics.fillRect(coordinate.getXAsInt(), coordinate.getYAsInt(), Game.TILE_SIZE, Game.TILE_SIZE); } } @@ -81,6 +136,10 @@ public void render(Graphics graphics) { public void movedTo(Vector2D coordinates) { super.movedTo(coordinates); + // TODO: move this to an update method thing? because it is possible that state changes without us + // moving the mouse. So this won't last unfortunately. (or we should do some kind of message thing so this + // class knows the state changed and should re-calculate or something like that) + Coordinate absoluteMapCoordinateOfTopleftOfStructure = getAbsoluteCoordinateTopLeftOfStructureToPlace(); // first determine all cells that will be occupied @@ -94,32 +153,6 @@ public void movedTo(Vector2D coordinates) { .map(mapCoordinate -> new PlaceableMapCoordinateCandidate(mapCoordinate, false)) .collect(toList()); - // Now, do checks if the structure may be placed - for (PlaceableMapCoordinateCandidate placeableMapCoordinateCandidate : mapCoordinatesForEntityToPlace) { - Coordinate absoluteMapCoordinate = placeableMapCoordinateCandidate.mapCoordinate.toCoordinate(); - Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); - - Cell cell = battleField.getCellByAbsoluteViewportCoordinate(coordinate); - - // first check if it is visible (easiest check) - boolean isPlaceable = cell.isVisibleFor(player); - - // visible? then check if it may be constructed (is it construction ground?) - if (isPlaceable && (cell.getTerrain() instanceof ConstructionGround) == false) { - isPlaceable = false; - } - - // TODO: distance checking - Coordinate constructingEntityCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(entityWhoConstructsIt.getCenteredCoordinate()); - - // still placeable? good. Final (expensive) check -> any other units that may block this? - if (isPlaceable) { - EntitiesSet entitiesAtMapCoordinate = entityRepository.findAliveEntitiesOfTypeAtVector(absoluteMapCoordinate, EntityType.STRUCTURE, EntityType.UNIT); - isPlaceable = entitiesAtMapCoordinate.isEmpty(); - } - placeableMapCoordinateCandidate.isPassable = isPlaceable; - } - } public Coordinate getAbsoluteCoordinateTopLeftOfStructureToPlace() { @@ -141,10 +174,36 @@ public String toString() { '}'; } + public boolean isAllCoordinatesForEntityToPlacePassableAndWithinReach() { + return this.mapCoordinatesForEntityToPlace.stream().allMatch(c -> c.isPassable && c.isWithinReach); + } + + private Entity findClosestStructureOfPlayer(Coordinate coordinate) { + PredicateBuilder predicateBuilder = Predicate.builder().forPlayer(player).ofType(EntityType.STRUCTURE); + EntitiesSet allStructuresForPlayer = entityRepository.filter(predicateBuilder.build()); + + float closestDistanceFoundSoFar = 320000; + Entity closestEntityFoundSoFar = null; + + for (Entity entity : allStructuresForPlayer) { + float distance = entity.getCenteredCoordinate().distance(coordinate); + if (distance < closestDistanceFoundSoFar) { + closestEntityFoundSoFar = entity; + closestDistanceFoundSoFar = distance; + } + } + return closestEntityFoundSoFar; + } + + /** + * A class that holds state per map coordinate needed for placement. Mostly used for rendering. + */ private class PlaceableMapCoordinateCandidate { public MapCoordinate mapCoordinate; public boolean isPassable = false; + public boolean isWithinReach = true; + public float distance = 99999; public PlaceableMapCoordinateCandidate(MapCoordinate mapCoordinate, boolean passable) { this.mapCoordinate = mapCoordinate; diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java index adf5bc74..4bbc28e8 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java @@ -41,6 +41,7 @@ public class EntityData { private float chop = -1f; private float halfChop = -1f; public float buildTimeInSeconds = 5.0F; + public float buildRange = 208; // 6 * 32 + half a tile public EntityType type; diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java index ff3c0d42..7950e8df 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java @@ -10,11 +10,16 @@ import com.fundynamic.d2tm.game.rendering.gui.battlefield.RenderQueue; import com.fundynamic.d2tm.math.Coordinate; import com.fundynamic.d2tm.math.MapCoordinate; +import com.fundynamic.d2tm.math.Vector2D; import com.fundynamic.d2tm.utils.Colors; import com.fundynamic.d2tm.utils.SlickUtils; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; +import org.newdawn.slick.ShapeFill; import org.newdawn.slick.SpriteSheet; +import org.newdawn.slick.fills.GradientFill; +import org.newdawn.slick.geom.Circle; +import org.newdawn.slick.geom.ShapeRenderer; import java.util.ArrayList; import java.util.List; @@ -147,6 +152,11 @@ public void render(Graphics graphics, int x, int y) { graphics.drawImage(sprite, x, y); if (Game.DEBUG_INFO) { + Vector2D halfSize = getHalfSize(); + Circle circle = new Circle(x + halfSize.getXAsInt(), y + halfSize.getYAsInt(), entityData.buildRange); + graphics.setColor(Colors.YELLOW_ALPHA_32); + ShapeRenderer.fill(circle); + MapCoordinate mapCoordinate = coordinate.toMapCoordinate(); SlickUtils.drawShadowedText( graphics, diff --git a/src/main/java/com/fundynamic/d2tm/utils/Colors.java b/src/main/java/com/fundynamic/d2tm/utils/Colors.java index 53244983..b114d8ab 100644 --- a/src/main/java/com/fundynamic/d2tm/utils/Colors.java +++ b/src/main/java/com/fundynamic/d2tm/utils/Colors.java @@ -11,7 +11,10 @@ public class Colors { public static final Color BLACK = Color.black; public static final Color WHITE = Color.white; public static final Color GREEN_ALPHA_128 = new Color(0,255,0,128); + public static final Color YELLOW_ALPHA_32 = new Color(255, 255, 0, 32); + public static final Color YELLOW_ALPHA_128 = new Color(255, 255, 0, 128); public static final Color RED_ALPHA_128 = new Color(255,0,0,128); + public static final Color DARK_RED_ALPHA_128 = new Color(200, 0, 0, 128); public static final Color BLACK_ALPHA_128 = new Color(0,0,0,128); public static final Color WHITE_ALPHA_128 = new Color(255, 255, 255, 128); public static final Color RED = new Color(255, 0, 0, 128); From 3580c04e750a33ce7b86c635b035c5b26e036b97 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Mon, 3 Oct 2016 21:24:58 +0200 Subject: [PATCH 06/13] Make sure we run this test on rock terrain so we know it will be 'on valid terrain' to place. (we already know it is 'in range' because the building to place is next to the constyard) --- .../d2tm/game/rendering/gui/sidebar/SidebarTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarTest.java b/src/test/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarTest.java index 42a8c8ae..ba5c9012 100644 --- a/src/test/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarTest.java +++ b/src/test/java/com/fundynamic/d2tm/game/rendering/gui/sidebar/SidebarTest.java @@ -4,12 +4,17 @@ import com.fundynamic.d2tm.game.AbstractD2TMTest; import com.fundynamic.d2tm.game.entities.entitiesdata.EntitiesData; import com.fundynamic.d2tm.game.entities.structures.Structure; +import com.fundynamic.d2tm.game.map.MapEditor; import com.fundynamic.d2tm.game.state.PlayingState; +import com.fundynamic.d2tm.game.terrain.impl.DuneTerrain; +import com.fundynamic.d2tm.game.terrain.impl.DuneTerrainFactory; +import com.fundynamic.d2tm.graphics.Theme; import com.fundynamic.d2tm.math.Coordinate; import com.fundynamic.d2tm.math.MapCoordinate; import com.fundynamic.d2tm.math.Vector2D; import org.junit.Assert; import org.junit.Test; +import org.mockito.Mockito; /** * This is a fairly big test: @@ -36,6 +41,10 @@ public class SidebarTest extends AbstractD2TMTest { @Test public void selectingConstructionYardActivatesSidebar() { + // make a map full of rock to make sure this test passes + MapEditor mapEditor = new MapEditor(new DuneTerrainFactory(Mockito.mock(Theme.class))); + mapEditor.fillMapWithTerrain(map, DuneTerrain.TERRAIN_ROCK); + // Place structure MapCoordinate mapCoordinate = MapCoordinate.create(STRUCTURE_MAP_COORDINATE_X, STRUCTURE_MAP_COORDINATE_Y); Structure structure = entityRepository.placeStructureOnMap( From 207e5fa04e58192e08766b9d73e04952b176a40f Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Mon, 3 Oct 2016 21:27:34 +0200 Subject: [PATCH 07/13] fix minor codacy issues --- .../fundynamic/d2tm/game/entities/structures/Structure.java | 2 -- .../d2tm/game/entities/units/UnitFacingsTest.java | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java index 7950e8df..8faf2c43 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java @@ -15,9 +15,7 @@ import com.fundynamic.d2tm.utils.SlickUtils; import org.newdawn.slick.Graphics; import org.newdawn.slick.Image; -import org.newdawn.slick.ShapeFill; import org.newdawn.slick.SpriteSheet; -import org.newdawn.slick.fills.GradientFill; import org.newdawn.slick.geom.Circle; import org.newdawn.slick.geom.ShapeRenderer; diff --git a/src/test/java/com/fundynamic/d2tm/game/entities/units/UnitFacingsTest.java b/src/test/java/com/fundynamic/d2tm/game/entities/units/UnitFacingsTest.java index 2e6d4bba..d3b03ced 100644 --- a/src/test/java/com/fundynamic/d2tm/game/entities/units/UnitFacingsTest.java +++ b/src/test/java/com/fundynamic/d2tm/game/entities/units/UnitFacingsTest.java @@ -19,7 +19,7 @@ public class UnitFacingsTest { @Test public void returnsRightWhenUnableToFigureOutWhereToFaceTo() { - assertEquals(UnitFacings.RIGHT, determine(unitAbsoluteMapCoordinates, unitAbsoluteMapCoordinates)); + assertEquals(RIGHT, determine(unitAbsoluteMapCoordinates, unitAbsoluteMapCoordinates)); } @Test @@ -31,7 +31,7 @@ public void determinesFacingRightDown() { @Test public void determinesFacingLeftDown() { Vector2D coordinatesToFaceTo = unitAbsoluteMapCoordinates.add(create(-1, 1)); - assertEquals(UnitFacings.LEFT_DOWN, determine(unitAbsoluteMapCoordinates, coordinatesToFaceTo)); + assertEquals(LEFT_DOWN, determine(unitAbsoluteMapCoordinates, coordinatesToFaceTo)); } @Test @@ -55,7 +55,7 @@ public void determinesFacingUp() { @Test public void determinesFacingDown() { Vector2D coordinatesToFaceTo = unitAbsoluteMapCoordinates.add(create(0, 1)); - assertEquals(UnitFacings.DOWN, determine(unitAbsoluteMapCoordinates, coordinatesToFaceTo)); + assertEquals(DOWN, determine(unitAbsoluteMapCoordinates, coordinatesToFaceTo)); } @Test From b5dfba0cf569cdbc22a19132ba315767cc0e1eb7 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Mon, 3 Oct 2016 21:30:17 +0200 Subject: [PATCH 08/13] This should fix the flaky test - by making sure we only move to a target when it is surely a different vector --- .../d2tm/game/entities/units/Unit.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/units/Unit.java b/src/main/java/com/fundynamic/d2tm/game/entities/units/Unit.java index 3a8e4f20..bb65cdc7 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/units/Unit.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/units/Unit.java @@ -384,10 +384,11 @@ public void moveTo(Vector2D absoluteMapCoordinates) { public void takeDamage(int hitPoints, Entity origin) { if (player.isCPU()) { if (origin == null) { - // we're hit and we don't have an idea where it came from - int correctX = Random.getRandomBetween(-1, 2) * Game.TILE_SIZE; - int correctY = Random.getRandomBetween(-1, 2) * Game.TILE_SIZE; - Vector2D target = coordinate.add(Vector2D.create(correctX, correctY)); + Vector2D target = coordinate; + // keep thinking of a random position to move to + while (target.equals(coordinate)) { + target = getRandomVectorToMoveTo(); + } moveTo(target); } else { if (entityToAttack == null) { @@ -398,6 +399,13 @@ public void takeDamage(int hitPoints, Entity origin) { hitPointBasedDestructibility.takeDamage(hitPoints); } + public Vector2D getRandomVectorToMoveTo() { + // we're hit and we don't have an idea where it came from + int correctX = Random.getRandomBetween(-1, 2) * Game.TILE_SIZE; + int correctY = Random.getRandomBetween(-1, 2) * Game.TILE_SIZE; + return coordinate.add(Vector2D.create(correctX, correctY)); + } + @Override public boolean isDestroyed() { return hasSpawnedExplosions && hitPointBasedDestructibility.hasDied(); From d04a98ff90ecedd7f97d40ff70daf0761f58d49d Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Mon, 3 Oct 2016 21:44:36 +0200 Subject: [PATCH 09/13] fix a few more simple codacy issues --- .../d2tm/game/controls/battlefield/PlacingStructureMouse.java | 2 +- .../d2tm/game/entities/entitybuilders/SingleEntityBuilder.java | 2 -- .../game/entities/sidebar/PlacementBuildableEntityTest.java | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java index 83e0222c..d6a3f3ec 100644 --- a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java +++ b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java @@ -75,7 +75,7 @@ public void render(Graphics graphics) { boolean isPlaceable = cell.isVisibleFor(player); // visible? then check if it may be constructed (is it construction ground?) - if (isPlaceable && (cell.getTerrain() instanceof ConstructionGround) == false) { + if (isPlaceable && !(cell.getTerrain() instanceof ConstructionGround)) { isPlaceable = false; } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java index f3f0d974..f2dd8ce6 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitybuilders/SingleEntityBuilder.java @@ -13,13 +13,11 @@ */ public class SingleEntityBuilder implements EntityBuilder { - private Entity constructingForEntity; private AbstractBuildableEntity buildingEntity = null; private List buildableEntities = new ArrayList<>(); public SingleEntityBuilder(List entityDatasToBuild, Entity constructingForEntity) { - this.constructingForEntity = constructingForEntity; for (EntityData entityDataToBuild : entityDatasToBuild) { // TODO: make more flexible!! if (entityDataToBuild.isTypeStructure()) { diff --git a/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java b/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java index abd13680..17a93ec8 100644 --- a/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java +++ b/src/test/java/com/fundynamic/d2tm/game/entities/sidebar/PlacementBuildableEntityTest.java @@ -1,6 +1,5 @@ package com.fundynamic.d2tm.game.entities.sidebar; -import com.fundynamic.d2tm.game.entities.Entity; import com.fundynamic.d2tm.game.entities.EntityData; import com.fundynamic.d2tm.game.entities.entitybuilders.PlacementBuildableEntity; import org.junit.Assert; From ccd462772ff0e27d4c0de33ef70bf593db4e6fd0 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Mon, 3 Oct 2016 22:03:30 +0200 Subject: [PATCH 10/13] cosmetic and docs --- .../battlefield/PlacingStructureMouse.java | 33 ++++++++++++------- .../gui/battlefield/BattleField.java | 23 +++++++++++++ .../com/fundynamic/d2tm/utils/Colors.java | 16 +++++++-- .../com/fundynamic/d2tm/utils/SlickUtils.java | 13 ++++++++ 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java index d6a3f3ec..c581d826 100644 --- a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java +++ b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java @@ -12,6 +12,7 @@ import com.fundynamic.d2tm.math.MapCoordinate; import com.fundynamic.d2tm.math.Vector2D; import com.fundynamic.d2tm.utils.Colors; +import com.fundynamic.d2tm.utils.SlickUtils; import org.newdawn.slick.Graphics; import java.util.ArrayList; @@ -39,8 +40,14 @@ public PlacingStructureMouse(BattleField battleField, PlacementBuildableEntity p public void leftClicked() { if (!isAllCoordinatesForEntityToPlacePassableAndWithinReach()) return; - Entity entity = entityRepository.placeOnMap(getAbsoluteCoordinateTopLeftOfStructureToPlace(), entityDataToPlace, mouse.getControllingPlayer()); - battleField.entityPlacedOnMap(entity); + // tell battlefield of the created entity + battleField.entityPlacedOnMap( + entityRepository.placeOnMap( // place entity on map + getAbsoluteCoordinateTopLeftOfStructureToPlace(), + entityDataToPlace, + mouse.getControllingPlayer() + ) + ); } @Override @@ -61,8 +68,8 @@ public void render(Graphics graphics) { MapCoordinate topLeftMapCoordinate = mapCoordinatesForEntityToPlace.get(0).mapCoordinate; - Coordinate coordinateTopLeft = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(topLeftMapCoordinate.toCoordinate()); - graphics.drawImage(entityDataToPlace.getFirstImage(), coordinateTopLeft.getXAsInt(), coordinateTopLeft.getYAsInt()); + Coordinate coordinateTopLeft = battleField.translateMapCoordinateToViewportCoordinate(topLeftMapCoordinate); + SlickUtils.drawImage(graphics, entityDataToPlace.getFirstImage(), coordinateTopLeft); // Now, do checks if the structure may be placed for (PlaceableMapCoordinateCandidate placeableMapCoordinateCandidate : mapCoordinatesForEntityToPlace) { @@ -86,12 +93,7 @@ public void render(Graphics graphics) { placeableMapCoordinateCandidate.distance = constructingEntityCoordinate.distance(coordinate); graphics.setColor(Colors.WHITE); - graphics.drawLine( - coordinate.getXAsInt(), - coordinate.getYAsInt(), - constructingEntityCoordinate.getXAsInt(), - constructingEntityCoordinate.getYAsInt() - ); + SlickUtils.drawLine(graphics, coordinate, constructingEntityCoordinate); // still placeable? good. Final (expensive) check -> any other units that may block this? if (isPlaceable) { @@ -178,11 +180,20 @@ public boolean isAllCoordinatesForEntityToPlacePassableAndWithinReach() { return this.mapCoordinatesForEntityToPlace.stream().allMatch(c -> c.isPassable && c.isWithinReach); } + /** + * This method finds a closest player Structure to the given coordinate. + * @param coordinate + * @return + */ private Entity findClosestStructureOfPlayer(Coordinate coordinate) { + // TODO: Optimize + // Perhaps have a 'satisfying distance' ? ie, whenever it finds one within this distance, stop searching? + // Do not loop over everything? + // Move to EntityRepository? PredicateBuilder predicateBuilder = Predicate.builder().forPlayer(player).ofType(EntityType.STRUCTURE); EntitiesSet allStructuresForPlayer = entityRepository.filter(predicateBuilder.build()); - float closestDistanceFoundSoFar = 320000; + float closestDistanceFoundSoFar = 320000; // Get from Map!? (width * height) -> get rid of magic number Entity closestEntityFoundSoFar = null; for (Entity entity : allStructuresForPlayer) { diff --git a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java index 510f040b..88d95fc1 100644 --- a/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java +++ b/src/main/java/com/fundynamic/d2tm/game/rendering/gui/battlefield/BattleField.java @@ -10,6 +10,7 @@ import com.fundynamic.d2tm.game.map.Perimeter; import com.fundynamic.d2tm.game.rendering.gui.GuiElement; import com.fundynamic.d2tm.math.Coordinate; +import com.fundynamic.d2tm.math.MapCoordinate; import com.fundynamic.d2tm.math.Rectangle; import com.fundynamic.d2tm.math.Vector2D; import org.newdawn.slick.Color; @@ -164,10 +165,32 @@ private void drawBufferToGraphics(Graphics graphics, Vector2D drawingVector) { graphics.drawImage(buffer, drawingVector.getX(), drawingVector.getY()); } + /** + *

+ * This method takes an absoluteMapCoordinate and translates this into a Viewport coordinate. This means + * effectively that any given coordinate is substracted by the camera(viewport) position. + *

+ *

Usage

+ *

+ * Example: You want to draw anything on screen. Pass in the absolute coordinates here, and use the + * resulting Coordinate as vector to draw within the viewport. + *

+ * @param absoluteMapCoordinate + * @return + */ public Coordinate translateAbsoluteMapCoordinateToViewportCoordinate(Coordinate absoluteMapCoordinate) { return absoluteMapCoordinate.min(viewingVector); } + /** + *

A short-hand for {@link #translateAbsoluteMapCoordinateToViewportCoordinate(Coordinate)} but for {@link MapCoordinate}

+ * @param mapCoordinate + * @return + */ + public Coordinate translateMapCoordinateToViewportCoordinate(MapCoordinate mapCoordinate) { + return translateAbsoluteMapCoordinateToViewportCoordinate(mapCoordinate.toCoordinate()); + } + /** *

* Given an absolute viewport coordinate (acquire one via {@link #translateScreenToViewportCoordinate(Vector2D)}, returns diff --git a/src/main/java/com/fundynamic/d2tm/utils/Colors.java b/src/main/java/com/fundynamic/d2tm/utils/Colors.java index b114d8ab..12442bf3 100644 --- a/src/main/java/com/fundynamic/d2tm/utils/Colors.java +++ b/src/main/java/com/fundynamic/d2tm/utils/Colors.java @@ -6,6 +6,18 @@ import java.util.HashMap; import java.util.Map; +/** + *

General purpose

+ *

+ * Easy color management & caching of colors that are being created - to prevent recreation of Color classes all the time. + *

+ *

Usage

+ *

+ * Get a color by one of the static final fields. + * + * Or use the {@link #create(int, int, int, int)} to create a new color. + *

+ */ public class Colors { public static final Color BLACK = Color.black; @@ -22,7 +34,7 @@ public class Colors { public static Map colorMap = new HashMap<>(); /** - * Creates a color and cashes it. + * Creates a color and caches it. Cache-key is determined by rgba. * @param r * @param g * @param b @@ -56,7 +68,7 @@ public static Color create(int r, int g, int b) { } /** - * Shorthand for {@link #create(float, float, float, -1)} + * Shorthand for {@link #create(int, int, int, -1)} * @param r * @param g * @param b diff --git a/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java b/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java index 17b10496..d8918342 100644 --- a/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java +++ b/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java @@ -2,8 +2,10 @@ import com.fundynamic.d2tm.math.Coordinate; +import com.fundynamic.d2tm.math.Vector2D; import org.newdawn.slick.Color; import org.newdawn.slick.Graphics; +import org.newdawn.slick.Image; public class SlickUtils { @@ -38,4 +40,15 @@ public static void drawText(Graphics graphics, Color color, String msg, int x, i graphics.drawString(msg, x, y); } + + /** + * Draws image on given vector. + * @param graphics + * @param image + * @param vec + */ + public static void drawImage(Graphics graphics, Image image, Vector2D vec) { + graphics.drawImage(image, vec.getXAsInt(), vec.getYAsInt()); + } + } From b28b151538f4d324425b2580fbf41a9542851945 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Wed, 5 Oct 2016 21:40:10 +0200 Subject: [PATCH 11/13] this build range logic feels more right --- .../battlefield/PlacingStructureMouse.java | 69 ++++++++++++------- .../d2tm/game/entities/EntityData.java | 2 +- .../game/entities/structures/Structure.java | 9 +-- 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java index c581d826..cc901029 100644 --- a/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java +++ b/src/main/java/com/fundynamic/d2tm/game/controls/battlefield/PlacingStructureMouse.java @@ -38,7 +38,7 @@ public PlacingStructureMouse(BattleField battleField, PlacementBuildableEntity p @Override public void leftClicked() { - if (!isAllCoordinatesForEntityToPlacePassableAndWithinReach()) return; + if (!canPlaceEntity()) return; // tell battlefield of the created entity battleField.entityPlacedOnMap( @@ -92,44 +92,56 @@ public void render(Graphics graphics) { placeableMapCoordinateCandidate.distance = constructingEntityCoordinate.distance(coordinate); - graphics.setColor(Colors.WHITE); - SlickUtils.drawLine(graphics, coordinate, constructingEntityCoordinate); + // render the lines when debug info is true + if (Game.DEBUG_INFO) { + graphics.setColor(Colors.WHITE); + SlickUtils.drawLine(graphics, coordinate, constructingEntityCoordinate); + } // still placeable? good. Final (expensive) check -> any other units that may block this? if (isPlaceable) { EntitiesSet entitiesAtMapCoordinate = entityRepository.findAliveEntitiesOfTypeAtVector(absoluteMapCoordinate, EntityType.STRUCTURE, EntityType.UNIT); isPlaceable = entitiesAtMapCoordinate.isEmpty(); } - placeableMapCoordinateCandidate.isPassable = isPlaceable; + + if (!isPlaceable) { + placeableMapCoordinateCandidate.placeableState = PlaceableState.BLOCKED; + } } float maxDistance = entityWhoConstructsIt.getEntityData().buildRange; // from center of the structure that built this entity (to place) for (PlaceableMapCoordinateCandidate pmcc : mapCoordinatesForEntityToPlace) { - if (!pmcc.isPassable) continue; // not worth evaluating distance - pmcc.isWithinReach = pmcc.distance < maxDistance; + if (pmcc.placeableState != PlaceableState.PLACEABLE) continue; + if (pmcc.distance > maxDistance) { + pmcc.placeableState = PlaceableState.OUT_OF_REACH; + } } + boolean canPlaceEntity = canPlaceEntity(); // Render stuff! for (PlaceableMapCoordinateCandidate placeableMapCoordinateCandidate : mapCoordinatesForEntityToPlace) { Coordinate absoluteMapCoordinate = placeableMapCoordinateCandidate.mapCoordinate.toCoordinate(); Coordinate coordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteMapCoordinate); - if (placeableMapCoordinateCandidate.isPassable) { - if (placeableMapCoordinateCandidate.isWithinReach) { - graphics.setColor(Colors.GREEN_ALPHA_128); - } else { - graphics.setColor(Colors.YELLOW_ALPHA_128); - } + if (canPlaceEntity) { + graphics.setColor(Colors.GREEN_ALPHA_128); } else { - if (placeableMapCoordinateCandidate.isWithinReach) { - graphics.setColor(Colors.RED_ALPHA_128); - } else { - graphics.setColor(Colors.DARK_RED_ALPHA_128); + switch (placeableMapCoordinateCandidate.placeableState) { + case PLACEABLE: + graphics.setColor(Colors.GREEN_ALPHA_128); + break; + case BLOCKED: + graphics.setColor(Colors.RED_ALPHA_128); + break; + case OUT_OF_REACH: + graphics.setColor(Colors.YELLOW_ALPHA_128); + break; } } + graphics.fillRect(coordinate.getXAsInt(), coordinate.getYAsInt(), Game.TILE_SIZE, Game.TILE_SIZE); } } @@ -152,7 +164,7 @@ public void movedTo(Vector2D coordinates) { mapCoordinatesForEntityToPlace = allMapCoordinates .stream() - .map(mapCoordinate -> new PlaceableMapCoordinateCandidate(mapCoordinate, false)) + .map(mapCoordinate -> new PlaceableMapCoordinateCandidate(mapCoordinate, PlaceableState.PLACEABLE)) .collect(toList()); } @@ -176,8 +188,14 @@ public String toString() { '}'; } - public boolean isAllCoordinatesForEntityToPlacePassableAndWithinReach() { - return this.mapCoordinatesForEntityToPlace.stream().allMatch(c -> c.isPassable && c.isWithinReach); + public boolean canPlaceEntity() { + // blocked == not good + // passable == good + // out of reach, but one passable == good + boolean blocked = this.mapCoordinatesForEntityToPlace.stream().anyMatch(c -> c.placeableState == PlaceableState.BLOCKED); + if (blocked) return false; + + return this.mapCoordinatesForEntityToPlace.stream().anyMatch(c -> c.placeableState == PlaceableState.PLACEABLE); } /** @@ -212,13 +230,18 @@ private Entity findClosestStructureOfPlayer(Coordinate coordinate) { private class PlaceableMapCoordinateCandidate { public MapCoordinate mapCoordinate; - public boolean isPassable = false; - public boolean isWithinReach = true; + public PlaceableState placeableState; public float distance = 99999; - public PlaceableMapCoordinateCandidate(MapCoordinate mapCoordinate, boolean passable) { + public PlaceableMapCoordinateCandidate(MapCoordinate mapCoordinate, PlaceableState state) { this.mapCoordinate = mapCoordinate; - this.isPassable = passable; + this.placeableState = state; } } + + enum PlaceableState { + PLACEABLE, + BLOCKED, + OUT_OF_REACH + } } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java index 4bbc28e8..0c0b988c 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java @@ -41,7 +41,7 @@ public class EntityData { private float chop = -1f; private float halfChop = -1f; public float buildTimeInSeconds = 5.0F; - public float buildRange = 208; // 6 * 32 + half a tile + public float buildRange = Game.HALF_TILE + (3 * Game.TILE_SIZE); public EntityType type; diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java index 8faf2c43..5e674592 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/structures/Structure.java @@ -150,18 +150,11 @@ public void render(Graphics graphics, int x, int y) { graphics.drawImage(sprite, x, y); if (Game.DEBUG_INFO) { + // render build-range Vector2D halfSize = getHalfSize(); Circle circle = new Circle(x + halfSize.getXAsInt(), y + halfSize.getYAsInt(), entityData.buildRange); graphics.setColor(Colors.YELLOW_ALPHA_32); ShapeRenderer.fill(circle); - - MapCoordinate mapCoordinate = coordinate.toMapCoordinate(); - SlickUtils.drawShadowedText( - graphics, - Colors.WHITE, - "" + mapCoordinate.getXAsInt() + "," + mapCoordinate.getYAsInt(), - x, - y); } } From b5783f74c7c70f6d44815939b37d399fa5897d42 Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Wed, 5 Oct 2016 21:54:16 +0200 Subject: [PATCH 12/13] read entity data for structures from the struct class to reduce long param list --- .../entities/entitiesdata/EntitiesData.java | 6 ++-- .../entitiesdata/EntitiesDataReader.java | 15 ++------ .../entitiesdata/ini/IniDataStructure.java | 32 +++++++++-------- .../entitiesdata/ini/IniDataUnit.java | 1 - .../entitiesdata/EntitiesDataTest.java | 35 +++++++++++-------- 5 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java index 82e4580a..901e634d 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java @@ -87,9 +87,9 @@ public void addParticle(String id, String pathToImage, int widthInPixels, int he * * @throws SlickException */ - public EntityData addStructure(IniDataStructure iniDataStructure) throws SlickException { + public EntityData addStructure(String id, IniDataStructure iniDataStructure) throws SlickException { EntityData entityData = createEntity( - iniDataStructure.id, + id, iniDataStructure.image, null, iniDataStructure.width, @@ -106,7 +106,7 @@ public EntityData addStructure(IniDataStructure iniDataStructure) throws SlickEx if (!idProvided(iniDataStructure.explosion)) { if (!tryGetEntityData(EntityType.PARTICLE, iniDataStructure.explosion)) { - throw new IllegalArgumentException("structure " + iniDataStructure.id + " [explosion] refers to non-existing [EXPLOSIONS/" + iniDataStructure.explosion + "]"); + throw new IllegalArgumentException("structure " + id + " [explosion] refers to non-existing [EXPLOSIONS/" + iniDataStructure.explosion + "]"); } entityData.explosionId = iniDataStructure.explosion; } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java index e4f2253b..50828a7c 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java @@ -81,19 +81,8 @@ public void readStructures(EntitiesData entitiesData, Ini ini) throws SlickExcep for (String id : strings) { Profile.Section struct = structures.getChild(id); entitiesData.addStructure( - new IniDataStructure( - id, - struct.get(INI_KEYWORD_IMAGE, String.class, null), - struct.get(INI_KEYWORD_WIDTH, Integer.class), - struct.get(INI_KEYWORD_HEIGHT, Integer.class), - struct.get(INI_KEYWORD_SIGHT, Integer.class), - struct.get(INI_KEYWORD_HIT_POINTS, Integer.class), - struct.get(INI_KEYWORD_EXPLOSION, String.class, EntitiesData.UNKNOWN), - struct.get(INI_KEYWORD_BUILD_ICON, String.class, null), - struct.get(INI_KEYWORD_BUILDS, String.class, ""), - struct.get(INI_KEYWORD_BUILD_TIME, Float.class, 0F), - struct.get(INI_KEYWORD_BUILD_LIST, String.class, "") - ) + id, + new IniDataStructure(struct) ); } } diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java index 975ad3f2..5e6f374a 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java @@ -1,15 +1,17 @@ package com.fundynamic.d2tm.game.entities.entitiesdata.ini; +import com.fundynamic.d2tm.game.entities.entitiesdata.EntitiesData; import com.fundynamic.d2tm.game.entities.entitybuilders.EntityBuilderType; import com.fundynamic.d2tm.utils.StringUtils; +import org.ini4j.Profile; + +import static com.fundynamic.d2tm.game.entities.entitiesdata.EntitiesDataReader.*; /** * Object representation of a STRUCTURE entry in the INI file. */ public class IniDataStructure { - - public String id; public String image; public int width; public int height; @@ -21,18 +23,20 @@ public class IniDataStructure { public float buildTimeInSeconds; public String buildList; - public IniDataStructure(String id, String image, int width, int height, int sight, int hitpoints, String explosion, String buildIcon, String entityBuilderType, float buildTimeInSeconds, String buildList) { - this.id = id; - this.image = image; - this.width = width; - this.height = height; - this.sight = sight; - this.hitpoints = hitpoints; - this.explosion = explosion; - this.buildIcon = buildIcon; - this.entityBuilderType = entityBuilderType; - this.buildTimeInSeconds = buildTimeInSeconds; - this.buildList = buildList; + public IniDataStructure() { + } + + public IniDataStructure(Profile.Section struct) { + this.image = struct.get(INI_KEYWORD_IMAGE, String.class, null); + this.width = struct.get(INI_KEYWORD_WIDTH, Integer.class); + this.height = struct.get(INI_KEYWORD_HEIGHT, Integer.class); + this.sight = struct.get(INI_KEYWORD_SIGHT, Integer.class); + this.hitpoints = struct.get(INI_KEYWORD_HIT_POINTS, Integer.class); + this.explosion = struct.get(INI_KEYWORD_EXPLOSION, String.class, EntitiesData.UNKNOWN); + this.buildIcon = struct.get(INI_KEYWORD_BUILD_ICON, String.class, null); + this.entityBuilderType = struct.get(INI_KEYWORD_BUILDS, String.class, ""); + this.buildTimeInSeconds = struct.get(INI_KEYWORD_BUILD_TIME, Float.class, 0F); + this.buildList = struct.get(INI_KEYWORD_BUILD_LIST, String.class, ""); } public EntityBuilderType getEntityBuilderType() { diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataUnit.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataUnit.java index e5c27eb2..6dfdbb20 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataUnit.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataUnit.java @@ -39,7 +39,6 @@ public class IniDataUnit { public float buildTimeInSeconds; public IniDataUnit() { - } public IniDataUnit(Profile.Section struct) { diff --git a/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataTest.java b/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataTest.java index c48b5f61..d7d5510f 100644 --- a/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataTest.java +++ b/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataTest.java @@ -104,25 +104,30 @@ public void createStructureCreatesStructureData() throws SlickException { int widthInCells = widthInPixels / Game.TILE_SIZE; int heightInCells = heightInPixels / Game.TILE_SIZE; int hitPoints = 1000; + String entityBuilderType = ""; String idOfEntity = "1"; int sight = 3; String explosionId = "UNKNOWN"; + + + IniDataStructure iniDataStructure = new IniDataStructure(); + + iniDataStructure.image = "constyard.png"; + iniDataStructure.width = widthInPixels; + iniDataStructure.height = heightInPixels; + iniDataStructure.sight = sight; + iniDataStructure.hitpoints = 1000; + iniDataStructure.explosion = explosionId; + iniDataStructure.buildIcon = "icon_constyard.bmp"; + iniDataStructure.entityBuilderType = entityBuilderType; + iniDataStructure.buildTimeInSeconds = 1.0F; + iniDataStructure.buildList = "WINDTRAP,REFINERY"; + // add entitiesData.addStructure( - new IniDataStructure( - idOfEntity, - "constyard.png", - widthInPixels, - heightInPixels, - sight, - 1000, - explosionId, - "icon_constyard.bmp", - "", - 1.0F, - "WINDTRAP,REFINERY" - ) + idOfEntity, + iniDataStructure ); // get & assert @@ -145,8 +150,8 @@ public void createStructureCreatesStructureData() throws SlickException { @Test (expected = IllegalArgumentException.class) public void createStructureWithDuplicateIdThrowsIllegalArgumentException() throws SlickException { String idOfEntity = "1"; - entitiesData.addStructure(new IniDataStructure(idOfEntity, "constyard.png", 32, 32, 2, 1000, "1", "icon_constyard.bmp", "", 1.0F, "")); // success! - entitiesData.addStructure(new IniDataStructure(idOfEntity, "this is irrelevant", 32, 32, 3, 1000, "1", "icon_constyard.bmp", "", 1.0F, "")); // boom! + entitiesData.addStructure(idOfEntity, new IniDataStructure()); // success! + entitiesData.addStructure(idOfEntity, new IniDataStructure()); // boom! } From 18eacbcd0b4dd0093aff696987d181f23fdc0a2a Mon Sep 17 00:00:00 2001 From: Stefan Hendriks Date: Wed, 5 Oct 2016 22:19:30 +0200 Subject: [PATCH 13/13] Read buildRange (in tiles) from rules.ini --- .../d2tm/game/entities/EntityData.java | 2 +- .../entities/entitiesdata/EntitiesData.java | 30 +++++++++++++++++++ .../entitiesdata/EntitiesDataReader.java | 1 + .../entitiesdata/ini/IniDataStructure.java | 3 ++ src/main/resources/rules.ini | 1 + .../entitiesdata/EntitiesDataReaderTest.java | 6 ++++ src/test/resources/test-rules.ini | 3 ++ 7 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java index 0c0b988c..7e7c62bd 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/EntityData.java @@ -41,7 +41,7 @@ public class EntityData { private float chop = -1f; private float halfChop = -1f; public float buildTimeInSeconds = 5.0F; - public float buildRange = Game.HALF_TILE + (3 * Game.TILE_SIZE); + public float buildRange = 0F; public EntityType type; diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java index 901e634d..67eb4a9c 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java @@ -1,6 +1,7 @@ package com.fundynamic.d2tm.game.entities.entitiesdata; +import com.fundynamic.d2tm.Game; import com.fundynamic.d2tm.game.entities.EntityData; import com.fundynamic.d2tm.game.entities.EntityNotFoundException; import com.fundynamic.d2tm.game.entities.EntityType; @@ -100,6 +101,35 @@ public EntityData addStructure(String id, IniDataStructure iniDataStructure) thr iniDataStructure.hitpoints ); + // The buildRange is (for now) determined by the size of the structure. + // Because the range is calculated from the center of the structure. + // In order to make it 'fair' for larger structures (if any would appear), + // we add 'half' of the structure to the range. + // + // For a constyard it is a square, so 64x64 pixels = 1 'half', meaning: + // + // (64/64)/2 = 0,5 * 32 (tile width) = 16 pixels + // + // A larger structure would be (width/height), ie a heavy factory thing would be: + // (96/64)/2 = ,75 * 32 = 24 pixels. + // + // Although on every squared structure this would even out fine, but then the + // 'buildRange' should have one tile extra because (again) it is calculated + // from the center + // + // this is all weird , then again, fixing this would require to check for every cell + // on the structure which basically makes calculating the 'can I place it in this distance' logic + // Width*height times more consuming. + // + // Unless.... + // + // We do the 'computed map' thing (where we have all entity data on a map attached), so we don't + // need to do an expensive lookup in the EntityRepository + //TODO: Do something about the above, for now accept its quirks + float someRatio = (float)entityData.getWidth() / (float)entityData.getHeight(); + int extraFromCenter = (int)((someRatio / 2) * Game.TILE_SIZE); // this is seriously flawed :/ (too tired to fix now) + // add additional '1' to get 'past the center and occupy one cell'. + entityData.buildRange = extraFromCenter + ((1 + iniDataStructure.buildRangeInTiles) * Game.TILE_SIZE); entityData.entityBuilderType = iniDataStructure.getEntityBuilderType(); entityData.buildTimeInSeconds = iniDataStructure.buildTimeInSeconds; entityData.buildList = iniDataStructure.buildList; diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java index 50828a7c..472908c4 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReader.java @@ -32,6 +32,7 @@ public class EntitiesDataReader { public static final String INI_KEYWORD_BUILD_ICON = "BuildIcon"; public static final String INI_KEYWORD_BUILDS = "Builds"; public static final String INI_KEYWORD_BUILD_TIME = "BuildTime"; + public static final String INI_KEYWORD_BUILD_RANGE = "BuildRange"; public static final String INI_KEYWORD_BUILD_LIST = "BuildList"; public static final String INI_KEYWORD_FPS = "Fps"; public static final String INI_KEYWORD_RECOLOR = "Recolor"; diff --git a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java index 5e6f374a..e4b09789 100644 --- a/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java +++ b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/ini/IniDataStructure.java @@ -17,10 +17,12 @@ public class IniDataStructure { public int height; public int sight; public int hitpoints; + public int buildRangeInTiles; public String explosion; public String buildIcon; public String entityBuilderType; public float buildTimeInSeconds; + public String buildList; public IniDataStructure() { @@ -37,6 +39,7 @@ public IniDataStructure(Profile.Section struct) { this.entityBuilderType = struct.get(INI_KEYWORD_BUILDS, String.class, ""); this.buildTimeInSeconds = struct.get(INI_KEYWORD_BUILD_TIME, Float.class, 0F); this.buildList = struct.get(INI_KEYWORD_BUILD_LIST, String.class, ""); + this.buildRangeInTiles = struct.get(INI_KEYWORD_BUILD_RANGE, Integer.class, 0); } public EntityBuilderType getEntityBuilderType() { diff --git a/src/main/resources/rules.ini b/src/main/resources/rules.ini index 55153174..5b32accd 100644 --- a/src/main/resources/rules.ini +++ b/src/main/resources/rules.ini @@ -197,6 +197,7 @@ Builds=STRUCTURES BuildIcon=ui/icons/icon_constyard.bmp BuildList=WINDTRAP,REFINERY,BARRACKS,LIGHTFACTORY,HEAVYFACTORY BuildTime=30 +BuildRange=2 [STRUCTURES/REFINERY] Image=structures/3x2_refinery.png diff --git a/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReaderTest.java b/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReaderTest.java index 1c4a2330..e7f39bce 100644 --- a/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReaderTest.java +++ b/src/test/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesDataReaderTest.java @@ -1,5 +1,6 @@ package com.fundynamic.d2tm.game.entities.entitiesdata; +import com.fundynamic.d2tm.Game; import com.fundynamic.d2tm.game.entities.EntityData; import com.fundynamic.d2tm.game.entities.EntityType; import com.fundynamic.d2tm.game.entities.entitybuilders.EntityBuilderType; @@ -43,6 +44,11 @@ public void readsBuildingStructureFromIniFile() { assertThat(constyard.explosionId, is("BOOM")); assertThat(constyard.buildIcon, is(not(nullValue()))); assertThat(constyard.entityBuilderType, is(EntityBuilderType.STRUCTURES)); + + // 1 extra tile range is added by the EntitiesData class (while it is '2' in the test-rules.ini!) + // therefor we do times 3! + float value = ((Game.TILE_SIZE) * 3) + Game.HALF_TILE; // we can do half-tile because it is a 64x64 structure + assertThat(constyard.buildRange, is(value)); // calculated by entitiesData class } @Test diff --git a/src/test/resources/test-rules.ini b/src/test/resources/test-rules.ini index 15cecad6..0ea65633 100644 --- a/src/test/resources/test-rules.ini +++ b/src/test/resources/test-rules.ini @@ -59,6 +59,9 @@ Height=64 Sight=4 Explosion=BOOM Builds=STRUCTURES +# BuildRange of 1 means attached to building +# BuildRange of 2 means you can have 1 tile in between +BuildRange=2 [STRUCTURES/WINDTRAP] Image=structures/2x2_windtrap.bmp