Java tutorial
/** * Copyright 2012 Jami Couch * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This project uses: * * LibGDX * Copyright 2011 see LibGDX AUTHORS file * Licensed under Apache License, Version 2.0 (see above). * */ package com.ahsgaming.valleyofbones.map; import java.util.HashMap; import com.ahsgaming.valleyofbones.GameController; import com.ahsgaming.valleyofbones.Player; import com.ahsgaming.valleyofbones.units.AbstractUnit; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.*; /** * @author jami * */ public class HexMap { public static final String LOG = "HexMap"; GameController gameController; MapData mapData; HashMap<Integer, MapView> mapViews; public HexMap(GameController gameController, FileHandle jsonFile) { this.gameController = gameController; init(MapData.createFromFile(jsonFile)); } public HexMap(GameController gameController, JsonValue json) { this.gameController = gameController; init(MapData.createFromJson(json)); } public void init(MapData mapData) { this.mapData = mapData; mapViews = new HashMap<Integer, MapView>(); for (Player player : gameController.getPlayers()) { mapViews.put(player.getPlayerId(), MapView.createMapView(mapData, player)); } } public void invalidateViews() { for (MapView view : mapViews.values()) { view.setMapDirty(true); } } public void update() { // if (!mapDirty) return; // // mapDirty = false; for (MapView view : mapViews.values()) { if (view.isMapDirty()) updateView(view); } } void updateView(MapView view) { view.setMapDirty(false); // change all to FOG unless a UNIT can see them, or they are HIGHLIGHTED or DIMMED Array<AbstractUnit> units; if (view.player == null) { units = gameController.getUnits(); } else { units = gameController.getUnitsByPlayerId(view.player.getPlayerId()); } for (int i = 0; i < view.hexStatus.length; i++) { view.hexStatus[i] = MapView.FOG; } for (int i = 0; i < units.size; i++) { if (units.get(i).getData().isBuilding()) { units.removeIndex(i); i--; } } int[] unitpositions = new int[units.size]; int[] radii = new int[units.size]; for (int u = 0; u < units.size; u++) { AbstractUnit unit = units.get(u); unitpositions[u] = (int) (unit.getView().getBoardPosition().y * mapData.bounds.x + unit.getView().getBoardPosition().x); radii[u] = unit.getData().getSightRange(); } boolean[] notavailable = new boolean[view.hexStatus.length]; /*for (Unit u: gameController.getUnits()) { if (u.getOwner() == null || !u.getOwner().equals(player)) notavailable[(int)(u.getBoardPosition().y * bounds.x + u.getBoardPosition().x)] = true; } */ boolean[] available = getAvailablePositions(unitpositions, radii, notavailable, false); for (int i = 0; i < view.hexStatus.length; i++) { if (available[i]) view.hexStatus[i] = MapView.NORMAL; if (view.hexHighlight[i]) view.hexStatus[i] = MapView.HIGHLIGHT; if (view.hexDimmed[i]) view.hexStatus[i] = MapView.DIMMED; } view.modified = TimeUtils.millis(); } public void highlightArea(MapView view, Vector2 center, int radius) { Array<AbstractUnit> units = gameController.getUnits(); boolean[] notavailable = new boolean[view.hexStatus.length]; for (AbstractUnit u : units) { if (!u.getView().getBoardPosition().equals(center)) notavailable[(int) (u.getView().getBoardPosition().y * mapData.bounds.x + u.getView().getBoardPosition().x)] = true; } int[] start = { (int) (center.y * mapData.bounds.x + center.x) }; int[] radii = { radius }; boolean[] available = getAvailablePositions(start, radii, notavailable, true); for (int i = 0; i < view.hexStatus.length; i++) { if (available[i] && view.hexStatus[i] != MapView.FOG) { if (notavailable[i] || i == start[0]) { view.hexDimmed[i] = true; view.hexStatus[i] = MapView.DIMMED; } else { view.hexHighlight[i] = true; view.hexStatus[i] = MapView.HIGHLIGHT; } } else if (view.hexStatus[i] != MapView.FOG) { if (getMapDist(center, new Vector2((int) (i % mapData.bounds.x), (int) (i / mapData.bounds.x))) <= radius) { view.hexDimmed[i] = true; view.hexStatus[i] = MapView.DIMMED; } } } } public MapData getMapData() { return mapData; } public MapView getMapView(int id) { if (id == -1) { if (!mapViews.containsKey(id)) { mapViews.put(id, MapView.createMapView(mapData, null)); } } return mapViews.get(id); } public Array<MapView> getMapViews() { Array<MapView> returnVal = new Array<MapView>(mapViews.values().size()); for (MapView mv : mapViews.values()) { returnVal.add(mv); } return returnVal; } public int getWidth() { return (int) mapData.bounds.x; } public int getHeight() { return (int) mapData.bounds.y; } public int getTileWidth() { return (int) mapData.tileSize.x; } public int getTileHeight() { return (int) mapData.tileSize.y; } public int getMapWidth() { return mapData.getMapWidth(); } public int getMapHeight() { return mapData.getMapHeight(); } public Array<MapData.MapObject> getPlayerSpawns() { Array<MapData.MapObject> spawns = new Array<MapData.MapObject>(); for (MapData.MapObject obj : mapData.mapObjects) { if (obj.type.equals("spawn")) { spawns.add(obj); } } return spawns; } public Array<MapData.MapObject> getControlPoints() { Array<MapData.MapObject> points = new Array<MapData.MapObject>(); for (MapData.MapObject obj : mapData.mapObjects) { if (obj.type.equals("unit")) { points.add(obj); } } return points; } public boolean isBoardPositionVisible(Player player, Vector2 pos) { return mapViews.get(player.getPlayerId()) != null && mapViews.get(player.getPlayerId()).isBoardPositionVisible(pos); } public boolean detectorCanSee(Player player, Array<AbstractUnit> units, Vector2 boardPosition) { for (int u = 0; u < units.size; u++) { AbstractUnit unit = units.get(u); if (unit.getOwner() == player && unit.getData().isDetector() && !unit.getData().isBuilding() && getMapDist(unit.getView().getBoardPosition(), boardPosition) <= unit.getData() .getSightRange()) return true; } return false; } public static Vector2 boardToMapCoords(MapData data, int bx, int by) { return new Vector2(bx * data.tileSize.x + (by % 2 == 1 ? data.tileSize.x * 0.5f : 0), by * data.tileSize.y * 0.75f); } public Vector2 boardToMapCoords(float x, float y) { return HexMap.boardToMapCoords(mapData, (int) x, (int) y); } public static Vector2 mapToBoardCoords(MapData data, float x, float y) { Vector2 boardCoords = new Vector2(); float dx; float dy = y / (data.tileSize.y * 0.75f); float mx = x % data.tileSize.x; float my = y % (data.tileSize.y * 0.75f); boardCoords.y = (float) Math.floor(dy); if (my < data.tileSize.y * 0.25) { if (Math.floor(dy) % 2 == 1) { mx = (x - data.tileSize.x * 0.5f) % data.tileSize.x; } // if (mx, my) <= (y = -0.5x + .25 * Th) or (mx, my) <= (y = 0.5x - 0.25 * Th) if (my <= -0.5 * mx + 0.25f * data.tileSize.y || my <= 0.5 * mx - 0.25 * data.tileSize.y) { boardCoords.y -= 1; } } if (boardCoords.y % 2 == 1) { dx = (x - data.tileSize.x * 0.5f) / data.tileSize.x; } else { dx = x / data.tileSize.x; } boardCoords.x = (float) Math.floor(dx); return boardCoords; } /* * (x - 1!, y + 1) (x + 1?, y + 1) * \ / * (x - 1, y) --- (x, y) --- (x + 1, y) * / \ * (x - 1!, y - 1) (x + 1?, y - 1) * ! --> y % 2 == 0 ? -1 : 0 * ? --> y % 2 == 0 ? 0 : 1 */ public static int getMapDist(Vector2 from, Vector2 to) { // completion cases: if (from.x == to.x) return Math.round(Math.abs(from.y - to.y)); if (from.y == to.y) return Math.round(Math.abs(from.x - to.x)); // otherwise, move along the gradient from = new Vector2(from); float dx = to.x - from.x, dy = to.y - from.y; if (from.y % 2 == 0 && dx < 0) { from.x -= 1; } else if (from.y % 2 == 1 && dx > 0) { from.x += 1; } if (dy > 0) { from.y += 1; } else { from.y -= 1; } return 1 + getMapDist(from, to); } public static Vector2[] getAdjacent(int x, int y) { Vector2[] adjacent = new Vector2[6]; adjacent[0] = new Vector2(x + 1, y); adjacent[1] = new Vector2(x - 1, y); adjacent[2] = new Vector2(x + (y % 2 == 0 ? 0 : 1), y - 1); adjacent[3] = new Vector2(x + (y % 2 == 0 ? 0 : 1), y + 1); adjacent[4] = new Vector2(x + (y % 2 == 0 ? -1 : 0), y - 1); adjacent[5] = new Vector2(x + (y % 2 == 0 ? -1 : 0), y + 1); return adjacent; } public static Vector2[] getAdjacent(Vector2 boardPosition) { return getAdjacent((int) boardPosition.x, (int) boardPosition.y); } public boolean[] getAvailablePositions(int[] start, int[] radii, boolean[] unavailable, boolean requireTraversible) { boolean[] available = new boolean[(int) (mapData.bounds.x * mapData.bounds.y)]; for (int u = 0; u < start.length; u++) { int unit = start[u]; available[unit] = true; Array<Integer> current = new Array<Integer>(), next = new Array<Integer>(); current.add(unit); for (int r = 0; r <= radii[u]; r++) { while (current.size > 0) { int p = current.pop(); available[p] = true; // check if this position is traversible and not blocked if (requireTraversible && !mapData.isBoardPositionTraversible((int) (p % mapData.bounds.x), (int) (p / mapData.bounds.x))) unavailable[p] = true; if (unavailable[p]) continue; // we can see this point but not beyond it for (Vector2 point : getAdjacent((int) (p % mapData.bounds.x), (int) (p / mapData.bounds.x))) { if (point.x < 0 || point.y < 0 || point.x >= mapData.bounds.x || point.y >= mapData.bounds.y) continue; next.add((int) (point.y * mapData.bounds.x + point.x)); } } current.addAll(next); next.clear(); } } return available; } }