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..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 @@ -1,29 +1,53 @@ 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; +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; import com.fundynamic.d2tm.math.Coordinate; -import org.newdawn.slick.Color; +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; +import java.util.List; + +import static java.util.stream.Collectors.toList; + public class PlacingStructureMouse extends AbstractBattleFieldMouseBehavior { - private EntityData entityToPlace; + private EntityData entityDataToPlace; + private Entity entityWhoConstructsIt; + private List mapCoordinatesForEntityToPlace; + private EntityRepository entityRepository; - public PlacingStructureMouse(BattleField battleField, EntityData entityData) { + public PlacingStructureMouse(BattleField battleField, PlacementBuildableEntity placementBuildableEntity) { super(battleField); - this.entityToPlace = entityData; + this.entityRepository = battleField.getEntityRepository(); + + this.entityDataToPlace = placementBuildableEntity.getEntityData(); + this.entityWhoConstructsIt = placementBuildableEntity.getEntityWhoConstructsThis(); + this.mapCoordinatesForEntityToPlace = new ArrayList<>(); } @Override public void leftClicked() { - Cell hoverCell = getHoverCell(); - // TODO: Check if it may be placed or not... - Entity entity = entityRepository.placeOnMap(hoverCell.getCoordinates(), entityToPlace, mouse.getControllingPlayer()); - battleField.entityPlacedOnMap(entity); + if (!canPlaceEntity()) return; + + // tell battlefield of the created entity + battleField.entityPlacedOnMap( + entityRepository.placeOnMap( // place entity on map + getAbsoluteCoordinateTopLeftOfStructureToPlace(), + entityDataToPlace, + mouse.getControllingPlayer() + ) + ); } @Override @@ -40,22 +64,184 @@ public void mouseMovedToCell(Cell cell) { public void render(Graphics graphics) { Cell hoverCell = getHoverCell(); if (hoverCell == null) return; + if (mapCoordinatesForEntityToPlace.isEmpty()) return; + + MapCoordinate topLeftMapCoordinate = mapCoordinatesForEntityToPlace.get(0).mapCoordinate; + + Coordinate coordinateTopLeft = battleField.translateMapCoordinateToViewportCoordinate(topLeftMapCoordinate); + SlickUtils.drawImage(graphics, entityDataToPlace.getFirstImage(), coordinateTopLeft); + + // 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)) { + isPlaceable = false; + } + + // Calculate distance first, later do checking + Entity closestFriendlyStructure = findClosestStructureOfPlayer(battleField.translateViewportCoordinateToAbsoluteMapCoordinate(coordinate)); + Coordinate constructingEntityCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(closestFriendlyStructure.getCenteredCoordinate()); - Coordinate absoluteCoordinates = hoverCell.getCoordinates(); - Coordinate viewportCoordinate = battleField.translateAbsoluteMapCoordinateToViewportCoordinate(absoluteCoordinates); + placeableMapCoordinateCandidate.distance = constructingEntityCoordinate.distance(coordinate); - 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()); - graphics.setLineWidth(lineWidth); + // 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(); + } + + 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.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 (canPlaceEntity) { + graphics.setColor(Colors.GREEN_ALPHA_128); + } else { + 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); + } + } + + @Override + 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 + mapCoordinatesForEntityToPlace = new ArrayList<>(); + + List allMapCoordinates = this.entityDataToPlace.getAllCellsAsCoordinates(absoluteMapCoordinateOfTopleftOfStructure); + + mapCoordinatesForEntityToPlace = + allMapCoordinates + .stream() + .map(mapCoordinate -> new PlaceableMapCoordinateCandidate(mapCoordinate, PlaceableState.PLACEABLE)) + .collect(toList()); + + } + + 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 public String toString() { return "PlacingStructureMouse{" + - "entityToPlace=" + entityToPlace + + "entityDataToPlace=" + entityDataToPlace + '}'; } + + 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); + } + + /** + * 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; // Get from Map!? (width * height) -> get rid of magic number + 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 PlaceableState placeableState; + public float distance = 99999; + + public PlaceableMapCoordinateCandidate(MapCoordinate mapCoordinate, PlaceableState state) { + this.mapCoordinate = mapCoordinate; + 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 7b38b3f5..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,6 +41,7 @@ public class EntityData { private float chop = -1f; private float halfChop = -1f; public float buildTimeInSeconds = 5.0F; + public float buildRange = 0F; public EntityType type; @@ -241,8 +242,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/entitiesdata/EntitiesData.java b/src/main/java/com/fundynamic/d2tm/game/entities/entitiesdata/EntitiesData.java index 82e4580a..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; @@ -87,9 +88,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, @@ -100,13 +101,42 @@ public EntityData addStructure(IniDataStructure iniDataStructure) throws SlickEx 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; 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..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"; @@ -81,19 +82,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..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 @@ -1,38 +1,45 @@ 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; 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(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, ""); + this.buildRangeInTiles = struct.get(INI_KEYWORD_BUILD_RANGE, Integer.class, 0); } 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/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..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 @@ -17,11 +17,11 @@ public class SingleEntityBuilder implements EntityBuilder { private List buildableEntities = new ArrayList<>(); - public SingleEntityBuilder(List entityDatasToBuild) { - for (EntityData entityDataToBuild : entityDatasToBuild ) { + public SingleEntityBuilder(List entityDatasToBuild, Entity 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..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 @@ -10,11 +10,14 @@ 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.SpriteSheet; +import org.newdawn.slick.geom.Circle; +import org.newdawn.slick.geom.ShapeRenderer; import java.util.ArrayList; import java.util.List; @@ -58,7 +61,7 @@ public Structure(Coordinate coordinate, SpriteSheet spritesheet, Player player, } } - this.entityBuilder = new SingleEntityBuilder(entityDatas); + this.entityBuilder = new SingleEntityBuilder(entityDatas, this); } @@ -145,14 +148,13 @@ public EntityType getEntityType() { public void render(Graphics graphics, int x, int y) { Image sprite = getSprite(); graphics.drawImage(sprite, x, y); - MapCoordinate mapCoordinate = coordinate.toMapCoordinate(); + if (Game.DEBUG_INFO) { - SlickUtils.drawShadowedText( - graphics, - Colors.WHITE, - "" + mapCoordinate.getXAsInt() + "," + mapCoordinate.getYAsInt(), - x, - y); + // 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); } } 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 8101d353..5a78c908 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(); 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 abe6ccda..109896b9 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 @@ -11,6 +11,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; @@ -165,14 +166,54 @@ 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 new Coordinate(absoluteMapCoordinate.min(viewingVector)); + 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 + * 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())); @@ -219,13 +260,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/Colors.java b/src/main/java/com/fundynamic/d2tm/utils/Colors.java index d5d21d0d..12442bf3 100644 --- a/src/main/java/com/fundynamic/d2tm/utils/Colors.java +++ b/src/main/java/com/fundynamic/d2tm/utils/Colors.java @@ -6,10 +6,27 @@ 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; 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); @@ -17,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 @@ -51,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 @@ -64,4 +81,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() + ")"; } + } diff --git a/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java b/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java index d69a9291..d8918342 100644 --- a/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java +++ b/src/main/java/com/fundynamic/d2tm/utils/SlickUtils.java @@ -1,8 +1,11 @@ package com.fundynamic.d2tm.utils; +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 { @@ -25,6 +28,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 */ @@ -33,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()); + } + } 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/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! } 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..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 @@ -19,7 +19,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 +38,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); 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 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( 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